// Datei: TelefonbuchServer.java
// Autor: Werner Brecht
// Datum: 25.07.2018
// Thema: Ein Telefonbuchserver, der einen Web-Browser als
// Benutzerschnittstelle verwendet und per RMI zwei
// Telefonbücher in den beiden Abteilungen Einkauf und
// Vertrieb nebenläufig abfragt.
//
// In den beiden Abteilungen wird
// (1) das Telefonbuch aus der ersten Übungsaufgabe
// übernommen und die Name-Nummer-Liste an die
// jeweilige Abteilung angepasst.
// (2) ein Abteilungsserver implementiert, der die RMI-
// Aufrufe bearbeitet und auf das jeweilige
// Telefonbuch zugreift.
//
// Der Telefonbuchserver aus der zweiten Aufgabe wird um
// die Methode verteileAbfrage() erweitert, die neben-
// läufig (mit Threads) die RMI-Aufrufe startet.
//
// Eine Beschreibung der Arbeitsweise des Programms ist
// beim Grobentwurf des Lösungsbeispiels zur dritten
// Aufgabe zu finden.
//
// Beim Programmstart sind die Rechnernamen der beiden
// Abteilungsserver als Params (e: Einkauf, v: Vertrieb)
// anzugeben:
// java TelefonbuchServer e=... v=...
// Ihre Reihenfolge spielt keine Rolle.
// =============================================================
import java.util.*;
import java.io.*;
import java.util.regex.*;
import java.net.*;
import java.rmi.*;
class TelefonbuchServer {
// Globale Variablen: Rechnernamen der Abteilungsserver
// Registry-Ports der Abteilungsserver
// Listen für die Ergebnisse der Threads,
// die auf die Abteilungen zugreifen
// -----------------------------------------------------------
static String abtHostE = null; // Abteilung Einkauf
static int regPortE = 0;
static String abtHostV = null; // Abteilung Vertrieb
static int regPortV = 0;
static ArrayList<String> thrE = new ArrayList<String>();
static ArrayList<String> thrV = new ArrayList<String>();
public static void main(String[] args) throws Exception {
// Parameterauswertung
// ---------------------------------------------------------
if(args.length != 2) {
System.out.println();
System.out.println("==================================");
System.out.println("Aufruf mit 2 Params: e=... v=...");
System.out.println(" Abteilungsserver angeben!");
System.out.println("==================================");
System.out.println();
System.exit(0);
}
if(!
(
(
(args[0].startsWith("e") || args[0].startsWith("E"))
&
(args[1].startsWith("v") || args[1].startsWith("V"))
)
||
(
(args[0].startsWith("v") || args[0].startsWith("V"))
&
(args[1].startsWith("e") || args[1].startsWith("E"))
)
)
) {
System.out.println();
System.out.println("=================================");
System.out.println("Parameter sind falsch aufgebaut:");
System.out.println(" e=HostnameEink v=HostnameVertr");
System.out.println("=================================");
System.out.println();
System.exit(0);
}
if(args[0].startsWith("e") || args[0].startsWith("E")) {
abtHostE = args[0];
abtHostV = args[1];
}
else {
abtHostE = args[1];
abtHostV = args[0];
}
if(abtHostE.startsWith("e"))
abtHostE = abtHostE.replace("e=", "");
else
abtHostE = abtHostE.replace("E=", "");
if(abtHostV.startsWith("v"))
abtHostV = abtHostV.replace("v=", "");
else
abtHostV = abtHostV.replace("V=", "");
// Registry-Ports festlegen:
// Einkauf -> E -> 69 -> 60069
// Vertrieb -> V -> 86 -> 60086
// ---------------------------------------------------------
regPortE = 60069;
regPortV = 60086;
// Laufen die Abteilungsserver?
// Nein -> Abbruch
// Ja -> Oben eintragen
// ---------------------------------------------------------
System.out.println();
System.out.println("Zentraler Telefonbuchserver ist aktiv");
System.out.println("-------------------------------------");
System.out.println();
System.out.println
("ACHTUNG: Der Server kann nur arbeiten,");
System.out.println
(" wenn beide Abteilungsserver laufen!");
System.out.println();
System.out.println(" Ist dies der Fall?");
System.out.print(" Eingabe: (j/n)->");
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String ein = br.readLine();
if(!(ein.equals("")||ein.equals("j")||ein.equals("J"))) {
System.out.println();
System.out.println("==================================");
System.out.println("Bitte die Abteilungsserver starten");
System.out.println("und Programm erneut aufrufen!");
System.out.println("=================================");
System.out.println();
System.exit(0);
}
// Ergebnisliste bereitstellen und ServerSocket am Port 9876
// erzeugen
// ---------------------------------------------------------
ArrayList<String> erg = new ArrayList<String>();
erg.add("Anfang");
String qs = "Anfang";
int serverport = 9876;
ServerSocket ss = new ServerSocket(serverport);
Socket cs = null;
// Start des zentralen Telefonbuchservers
// ---------------------------------------------------------
String host = InetAddress.getLocalHost().getHostName();
System.out.println();
System.out.println("Abteilungen: Einkauf, Vertrieb");
System.out.println("Host : " + host);
System.out.println("Port : " + serverport);
System.out.println();
// GET-Requests des Browsers entgegennehmen und analysieren.
// Ggf. Query String erzeugen und Suchvorgänge starten.
// Ergebnis ausgeben.
// ---------------------------------------------------------
while(true) {
if(erg.get(0).equals("Beendet")) break;
if(! qs.equals("Favicon"))
System.out.println("Warte auf Browser-Requests");
cs = ss.accept(); // Auf Browser Requests warten
qs = vomBrowser(cs);
if(qs.equals("Favicon")) { cs.close(); continue; }
System.out.println("Query String vom Browser -> " + qs);
erg = analyse(qs);
zumBrowser(cs, erg);
cs.close();
}
ss.close();
} // main()
// =============================================================
static String vomBrowser(Socket cs) throws Exception {
// Aus dem Socket cs lesen, Query String ausblenden
// ---------------------------------------------------------
BufferedReader br = new BufferedReader(
new InputStreamReader(cs.getInputStream()));
String qs = br.readLine();
// Favicon-Requests ignorieren (kein Query String)
// ---------------------------------------------------------
if(qs.startsWith("GET /favicon.ico")) return "Favicon";
// Leitseite wird angefordert: qs=Anfang
// ---------------------------------------------------------
if(qs.startsWith("GET / H")) return "Anfang";
// Benutzer hat Beenden gewählt
// ---------------------------------------------------------
if(qs.contains("&ende=Beenden")) return "Ende";
// Query String ausblenden
// ---------------------------------------------------------
qs = qs.replace("GET ", "");
qs = qs.replace(" HTTP/1.1", "");
qs = qs.replace("/?", "");
return qs;
} // vomBrowser()
// =============================================================
static ArrayList<String> analyse(String qs) throws Exception {
// Query String qs kann sein (* = mögliche Benutzereingabe):
// Anfang
// Ende
// name=*&nummer=* (ev. nur 1 *)
//
// Ergebnisliste und Hilfsvariablen anlegen
// ---------------------------------------------------------
ArrayList<String> ksuErg = new ArrayList<String>();
// Query Strings, die das Telefonbuch nicht ansprechen
// (1) Dialogbeginn
// ---------------------------------------------------------
if(qs.equals("Anfang")) {
ksuErg.add("Anfang");
return ksuErg;
}
// (2) Dialogende
// ---------------------------------------------------------
if(qs.equals("Ende")) {
ksuErg = verteileAbfrage(qs);
return ksuErg;
}
// (3) Leere Eingaben
// ---------------------------------------------------------
if(qs.equals("name=&nummer=")) {
ksuErg.add("Leere Eingabe");
return ksuErg;
}
// Die URL-Kodierung rückgängig machen.
// Das Programm ist unter Windows-7 entwickelt worden.
// ---------------------------------------------------------
qs = URLDecoder.decode(qs, "ISO-8859-1");
// (4) Syntaktisch falsche Eingaben
// Fehler mit & bzw. mit =
// ---------------------------------------------------------
String regex = "^[^&]*&[^&]*$";
if(! qs.matches(regex)) {
ksuErg.add("Syntaxfehler");
return ksuErg;
}
regex = "^[^=]+=[^=]+=[^=]*$";
if(! qs.matches(regex)) {
ksuErg.add("Syntaxfehler");
return ksuErg;
}
// Der Query String ist korrekt aufgebaut und nicht leer,
// aber noch nicht in der Form für das Telefonbuch:
// name=*&nummer=* (ev. nur 1 *)
// Er wird umgewandelt und per RMI an die Abteilungsserver
// übergeben.
// ---------------------------------------------------------
String[] token = null;
String name = null; // zu suchender Name
String nummer = null; // zu suchende Nummer
String kqs = null; // Korrekt aufgebauter Query String
token = qs.split("&");
name = token[0].replace("name=", "");
nummer = token[1].replace("nummer=", "");
kqs = name + "&" + nummer;
// Kumilierte Suchergebnislisten
// ---------------------------------------------------------
ksuErg = verteileAbfrage(kqs);
return ksuErg;
} // analyse()
// =============================================================
static ArrayList<String> verteileAbfrage(String kqs)
throws Exception {
class VerteilerThread extends Thread {
String abteilung = null;
String kqs = null;
VerteilerThread(String abt, String kqs) {
this.abteilung = abt;
this.kqs = kqs;
}
public void run() {
String kennung = "rmi://";
RmiDekl dekl = null;
try {
if(abteilung.equals("Einkauf")) {
kennung = kennung + TelefonbuchServer.abtHostE + ":"
+ TelefonbuchServer.regPortE
+ "/RmiAbfrage";
dekl = (RmiDekl)Naming.lookup(kennung);
if(kqs.equals("Ende"))
dekl.beende();
else
TelefonbuchServer.thrE = dekl.frageTbuch(kqs);
}
else {
kennung = kennung + TelefonbuchServer.abtHostV + ":"
+ TelefonbuchServer.regPortV
+ "/RmiAbfrage";
dekl = (RmiDekl)Naming.lookup(kennung);
if(kqs.equals("Ende"))
dekl.beende();
else
TelefonbuchServer.thrV = dekl.frageTbuch(kqs);
}
} catch(Exception e) {}
} // run()
} // Lokale Klasse
ArrayList<String> ksuErg = new ArrayList<String>();
VerteilerThread vtE = new VerteilerThread("Einkauf", kqs);
VerteilerThread vtV = new VerteilerThread("Vertrieb", kqs);
vtE.start();
vtV.start();
vtE.join();
vtV.join();
if(kqs.equals("Ende"))
ksuErg.add("Beendet");
else {
for(String s : thrE) ksuErg.add(s+"&Einkauf");
for(String s : thrV) ksuErg.add(s+"&Vertrieb");
ksuErg.add("Suche");
}
return ksuErg;
} // verteileAbfrage()
// =============================================================
static void zumBrowser(Socket cs, ArrayList<String> erg)
throws Exception {
int len = erg.size();
if(len == 1) {
String art = erg.get(0);
// Fälle, bei denen keine Suche gestartet wurde
// ---------------------------------------------------------
if(art.equals("Anfang")) {
schreibeKopf(cs);
schreibeForm(cs);
return;
}
if(art.equals("Beendet")) {
schreibeKopf(cs);
schreibeEnde(cs);
return;
}
if(art.equals("Leere Eingabe")) {
schreibeKopf(cs);
schreibeLeer(cs);
schreibeForm(cs);
return;
}
if(art.startsWith("Syntaxfehler")) {
schreibeKopf(cs);
schreibeSyntax(cs);
schreibeForm(cs);
return;
}
}
// Suche wurde gestartet: Finde (ev. leer bis auf Suche)
// Meier&4711&Einkauf
// ...
// Suche
// ---------------------------------------------------------
schreibeKopf(cs);
if(len == 1) schreibeOhne(cs);
else schreibeErg(cs, erg, len);
schreibeForm(cs);
} // zumBrowser()
// =============================================================
// HTTP-Kopf und Anfang der HTML-Seite in den Socket schreiben
// -----------------------------------------------------------
static void schreibeKopf(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html>");
pw.println("<body>");
pw.flush();
} // schreibeKopf()
// =============================================================
// HTML-Form und Seitenende in den Socket schreiben
// -----------------------------------------------------------
static void schreibeForm(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2 align=center>Telefonbuchabfrage</h2>");
pw.println("<h3>Sie können nach einem Namen oder nach "
+"einer Nummer<br>oder (nebenläufig) nach "
+"beidem suchen.<br>In Ihren Eingaben darf kein &"
+" und kein = vorkommen!</h3>");
String host = InetAddress.getLocalHost().getHostName();
int port = cs.getLocalPort();
String url = "http://"+host+":"+port;
pw.println("<form method=get action=\"" + url + "\">");
pw.println("<table>");
pw.println("<tr>");
pw.println("<td>Name:</td>");
pw.println("<td><input name=name></td>");
pw.println("<td></td>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td>Nummer:</td>");
pw.println("<td><input name=nummer></td>");
pw.println("<td></td>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td><input type=submit value=Abschicken></td>");
pw.println("<td><input type=reset></td>");
pw.println("<td><input type=submit name=ende value=Beenden></td>");
pw.println("</tr>");
pw.println("</table>");
pw.println("</form>");
pw.println("</body>");
pw.println("</html>");
pw.flush();
} // schreibeForm()
// =============================================================
// HTML-Endeseite in den Socket schreiben
// -----------------------------------------------------------
static void schreibeEnde(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2>Ende der Telefonbuchabfrage</h2>");
pw.println("<h2>Auf Wiedersehen!</h2>");
pw.println("</body>");
pw.println("</html>");
pw.flush();
} // schreibeEnde()
// =============================================================
// Schreiben bei leeren Eingaben
// -----------------------------------------------------------
static void schreibeLeer(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2>Ihre Eingaben waren leer!</h2>");
pw.println("<h2> Bitte wiederholen!</h2>");
pw.println("<h2>====================================</h2>");
pw.println("<br>");
pw.flush();
} // schreibeLeer()
// =============================================================
// Schreiben bei Syntaxfehler
// -----------------------------------------------------------
static void schreibeSyntax(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2>Ihre Eingaben waren fehlerhaft!</h2>");
pw.println("<h2>Bitte kein & und kein = eingeben!</h2>");
pw.println("<h2>====================================</h2>");
pw.println("<br>");
pw.flush();
} // schreibeSyntax()
// =============================================================
// Schreiben bei leerem Ergebnis
// -----------------------------------------------------------
static void schreibeOhne(Socket cs) throws Exception {
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2>Ihre Suche hatte leider keine Treffer!</h2>");
pw.println("<h2></h2>");
pw.println("<h2>====================================</h2>");
pw.println("<br>");
pw.flush();
} // schreibeOhne()
// =============================================================
// Schreiben bei nicht leerem Ergebnis
// Meier&4711&Einkauf
// -----------------------------------------------------------
static void schreibeErg(Socket cs, ArrayList<String> erg,
int len) throws Exception {
String[] token = null;
PrintWriter pw = new PrintWriter(cs.getOutputStream());
pw.println("<h2>Ihre Suche hat folgendes Ergebnis:</h2>");
pw.println("<h2></h2>");
len--;
pw.println("<table>");
pw.println("<tr><th>Name</th><th>Nummer</th>" +
"<th>Abteilung</th></tr>");
for(int i=0; i<len; i++) {
token = erg.get(i).split("&");
pw.println("<tr><td>" + token[0] + "</td>");
pw.println(" <td>" + token[1] + "</td>");
pw.println(" <td>" + token[2] + "</td></tr>");
}
pw.println("</table>");
pw.println("<h2>====================================</h2>");
pw.flush();
} // schreibeErg()
} // class