Verteilte WebApplikationen in dynamischen Netzen auf Basis von Jini Fallstudie und Evaluierung Jahresprojekt des Studienganges Computer Networking Fachhochschule Furtwangen Sommersemester 1999 und Wintersemester 1999/2000 Prokektteam: Thomas Mainka, Mirko Tochtermann, Oliver Hilß, Christian Bister, Maik Dobbermann, Stefan Zier Verteilte Web-Applikationen in dynamischen Netzen auf Basis von Jini Fallstudie und Evaluierung I.) II.) III.) IV.) V.) VI.) VII.) VIII.) IX.) Einleitung 1.) Technologische Grundlagen a.) Java b.) RMI c.) Sicherheit d.) Servlets e.) Jini f.) Vergleich zu CORBA 2.) Überblick über das Projekt Anwendungsarchitektur 1.) Dienstemarkt 2.) Webserver 3.) Jini als Middleware 4.) Datenbank 5.) Objektmodell der Geschäftsobjekte 6.) Objektmodell des Webservers Dienste 1.) Der Spielmanager-Dienst 2.) Der Fileserver-Dienst 3.) Der Message-Board-Dienst 4.) Der Mail-Dienst 5.) Der Chatserver-Dienst a.) Der ChatServer und seine Klassen b.) Der ChatController c.) Das ChatOutServlet d.) Das ChatIn-Servlet e.) Das Session-Manager-Servlet Webserver 1.) Service-Controller 2.) Dynamische Seitengenerierung Datenbank Praxiserfahrungen, Produkte 1.) Servlet Engines a.) JRun von Allaire b.) Tomcat/Jakarta Projekt der Apache Software Foundation c.) Jserv/Apache Software Foundation d.) Jigsaw/W3C 2.) Virtual Machines 3.) Jini a.) Zuverlässigkeit b.) Managebarkeit 4.) Datenbanken Performance, Benchmarks Zusammenfassung, Abschlußbericht Anhang 1.) Übersicht 2.) Packages I. Einleitung I.) Einleitung Im Rahmen des Studienganges Computer Networking an der Fachhochschule Furtwangen müssen Studenten ein Jahresprojekt machen. Dazu erhalten Sie entweder hochschulintern oder aus der Wirtschaft einen Autrag, der innerhalb des vierten und fünften Semesters umgesetzt werden muß. Im Sommersemester 1999 wurde durch Herrn Müller-Markmann ein Jahresprojekt initiiert, das das Ergebnis eines vorangegangenen Jahresprojektes namens net5 evaluieren und weiterentwickeln sollte. Bei net5 handelt es sich um eine Webplattform zur Durchführung von Wirtschaftsplanspielen. Das System bot bereits Nachrichtenbretter (Bulletin Boards) und Dateiaustausch. Der Dateiaustausch dient hauptsächlich zum Übertragen der Spielzüge. Eine direkte Webanbindung des Spieles kam leider nicht in Frage, weil die Programmierung einer Wirtschaftssimulation zu komplex ist. Die verwendete Simulation besteht aus einem Windows-Client zur Einfabe der Daten und einer DOS-Software zur Durchführung der Simulation. Der Datenaustausch zwischen diesen beiden Programmen erfolgt über Dateien, deren Format sich von Version zu Version ändert und deshalb nicht offenliegt. Zusätzlich zu dem bestehenden System sollte ein mehrsprachiges Benutzerinterface, eine FAQ (Frequently Asked Questions) sowie ein Chatsystem entwickelt werden, das es den Spielteilnehmern ermöglicht, in Echtzeit über Spielentscheidungen zu diskutieren. Zeitgleich kamen Mitarbeiter der Firma SAP auf unsere ehemalige Fachbereichsleiterin Frau Frank zu und fragten, ob nicht im Rahmen eines der damals beginnenden Jahresprojekte Jini von Sun etwas genauer erforscht werden könnte. Unser Projekt wurde ausgesucht, da die Technologie am besten in unser Projekt passte. Zunächst war eine komplette Neuimplementierung der Planspieleplattform geplant. Nach einiger Zeit verwarfen wir diese Idee jedoch aus Zeitgründen und entschieden uns, die bestehende Plattform weiterzuverwenden und parallel dazu eine Fallstudie mit Jini und Java Spaces zu machen. Im folgenden finden Sie das Ergebnis dieser Fallstudie. 1.) Technologische Grundlagen a.) Java Heutzutage werden die meisten verteilten Applikationen, die neu erstellt werden in Java entwickelt, weil Java-Software nur ein mal compiliert werden muß und auf allen Plattformen läuft, auf denen eine Java Virtual Machine (JVM) verfügbar ist. Zudem bietet Java als Sprache Anbindung an fast alle aktuell verfügbaren Technologien, zum Beispiel Datenbanken (JDBC – Java DataBase Connectivity), Verzeichnisdienste, Naming-Dienste (JNDI – Java Naming and Directory Interface), Webserver (Servlets, JSP – Java Server Pages), OMG CORBA (Object Management Group Common Object Request Broker Architecture). Weiterhin war Java von vornherein als Sprache für Netzwerke konzipiert, mit RMI ist daher die Entwicklung von verteilten Applikationen im Vergleich zu den meisten anderen Entwicklungsumgebungen sehr einfach. Der im Vergleich zu C++ vereinfachte Sprachumfang und die Garbage Collection ermöglichen somit die schnelle Entwicklung stabiler Serversoftware. b.) RMI RMI (Remote Method Invocation) ist eine parallele zu dem auf Unix-Systemen weit verbreiteten RPC (Remote Procedure Call). Im Gegensatz zu RPC ist RMI jedoch objektorientiert und für Client und Server weitgehend transparent – Clients können Methoden eines Objektes, das auf dem Server „lebt“ (Remote Objekt), in gleicher Weise wie Methoden lokaler Objekte aufrufen. Der Server „merkt“ nicht, daß der aufrufende Prozeß auf einem anderen Host läuft. RMI funktioniert sowohl sehr effizient im LAN (direkt über TCP Sockets) als auch sehr kompatibel im WAN (getunnelt über HTTP) – sogar durch Firewalls. Dazu muß bei der Programmierung der Client-Software noch nicht einmal beachtet werden, welches dieser beiden Verfahren genutzt werden soll. Schlägt die direkte Kommunikation über Sockets fehl, so findet automatisch ein Fallback auf den HTTP-Tunnel statt. Zudem ist RMI neuerdings auch interoperabel mit allen CORBA IIOP (Internet Inter-ORB Protocol) kompatiblen Systemen (RMI over IIOP). Ähnlich wie bei RPC benutzt auch RMI sogenannte Stubs und Skeletons. Der Stub ist dabei ein dem Interface des Remote Objektes nachempfundener Wrapper um die Kommunikationsfunktionalitäten des RMI Clients. Das Skeleton ist das serverseitige Pendant zum Stub. Er bekommt Requests vom Client, ruft die Methoden des Remote Objektes auf und liefert die Ergebnisse zurück. Seit JDK (Java Development Kit) Version 1.2.0 existiert an Stelle der klassenspezifischen Skeletons ein generisches Skeleton, daher entfällt die Erstellung der Skeletons. Stubs müssen jedoch nach wie vor mittels eines RMI compilers generiert werden. c.) Sicherheit Java-Programme laufen in einer Java Virtual Machine (JVM) ab. Beim Start der JVM wird ein Sicherheits-Profil geladen, mit dem der JVM mitgeteilt wird, welche Benutzer oder Hosts welche Befugnisse (Permissions) auf dem Rechner haben. Permissions sind Java-Klassen. Möchte man den Zugriff auf eine Methode kontrollieren, so muß man vor deren Abarbeitung abfragen, ob der Aufrufende die entsprechende Befugnis besitzt. Dieses System läßt eine sehr feine Dosierung der Zugriffe auf ein System zu. d.) Servlets Java Servlets sind der gebräuchlichste Weg, Java-basierte Software über eine Webschnittstelle verfügbar zu machen. Eine sogenannte Servlet Engine nimmt entweder direkt oder von einem Webserver Anfragen entgegen und wandelt sie in Response Objekte um. Zur Generierung der Antwort ruft die Servlet Engine daraufhin eine Methode des Servlets auf, die anhand des Request Objektes das HTML-Dokument generiert, und es über das sogenannte Response Objekt an die Servlet Engine zurückgibt. Im Vergleich zu CGI haben Servlets erhebliche Performance-Vorteile. Außerdem wird ein Servlet im Gegensatz zu CGI-Skripten nur ein mal gestartet und kann daher Ressourcen über mehrere Anfragen hinweg benutzen. e.) Jini Jini ist eine recht neue, auf Java basierende Technologie, die es ermöglichen soll, dynamische Netze mit Plug and Play-Funktionalität aufzubauen. Das Hauptaugenmerk liegt dabei auf dem LAN-Bereich, jedoch ist auch eine Kommunikation im WAN möglich. Jini-Netzwerke sind Dienst-basiert. Clients können beliebige Dienste ortstransparent nutzen. Der große Vorteil von Jini ist dabei, daß zum Auffinden eines Dienstes weder dessen Name, noch der Host auf dem er läuft bekannt sein muß. Stattdessen werden in Jini-Netzen Dienste mittels Suchprädikaten, die die Eigenschaften des gesuchten Dienstes beschreiben, gefunden. Zu diesem Zweck existiert in jedem Jini-Netzwerk mindestens ein sogenannter Lookup Service. Wird ein Dienst auf einem Host gestartet, so kann er erst dann genutzt werden, wenn er sich bei mindestens einem Lookup Service registriert hat. Die Nutzung der Dienste in Jini erfolgt über RMI. Zusätzlich benutzt Jini einen Kommunikationsmechanismus auf Netzebene, der es erlaubt, den Lookup Service im LAN mit Hilfe von IP Multicasts aufzufinden (Discovery). f.) Vergleich zu CORBA Vergleich man Jini mit CORBA, so kann man feststellen, daß Jini und CORBA sehr ähnliche Technologien sind, jedoch auf den ersten Blick auf andere Nutzergruppen ausgelegt. Jini ist eher für den Einsatz mit Clients gedacht – entweder im Customer- und Embedded Bereich (z.B. zur problemlosen Anbindung von Kleingeräten an Netzwerke), oder aber im Office Bereich zur Minimierung des Administrationsaufwandes. CORBA im Gegensatz bietet Interoperabilität zwischen Plattformen und Programmiersprachen und nimmt deshalb erheblich komplexere Programmierung und Administration in Kauf. Aus diesen Gründen wird es hauptsächlich zur Entwicklung verteilter Enterprise-Applikationen eingesetzt. 2.) Überblick über das Projekt Bei dem Projekt handelt es sich um eine Webanwendung. Die eigentliche Anwendung nutzt einige Dienste, die wir als Jini Services implementiert haben. Zu den Diensten zählen: - Dateiablage-Service Nachrichtenbrett-Verwaltung Spiel- und Spielerverwaltung Chat-Service Email-Versand-Service Einige der Dienste benutzen - für den Benutzer der Dienste transparent - noch persistente Speicher (Dateisystem bzw. Relationales Datenbanksystem). Die Anwendung selbst konnte aus Zeitgründen nicht vollständig zu Ende entwickelt werden, jedoch konnten wir ausreichend Erfahrungen mit Jini und Java Spaces sammeln um diese Fallstudie zu entwickeln. Zudem war es möglich, durch einige Benchmarks eine Ahnung von der vorraussichtlichen Performance des Systems zu bekommen. II. Anwendungsarchitektur II.) Anwendungsarchitektur 1.) Dienstemarkt Um eine hohe Wiederverwendbarkeit der Einzelkomponenten zu erreichen, versucht man in modernen Software-Anwendungsarchitekturen eine starke Untergliederung in Subsysteme mit möglichst wenigen Abhängigkeiten untereinander zu erreichen. Ein solcher Dienstemarkt, wie er in Jini und den meisten anderen Kommunikationsinfrastrukturen verwendet wird, erfüllt genau diesen Zweck. Komponenten einer Anwendung werden auf Dienste abgebildet. Abhängigkeiten bestehen nur insofern, als daß ein Dienst andere Dienste vorraussetzen kann, um zu funktionieren. Dienste sind nach Außen hin nur anhand ihrer Schnittstellen bekannt. Änderungen in Diensten haben auf Clients keinerlei Auswirkungen, die Schnittstelle und Funktionsweise bleibt gleich. Durch diese strikte Trennung wird es möglich, gleichzeitig sehr stabile Systeme zu erstellen und ein hohes Maß an Wiederverwendbarkeit der Softwarekomponenten zu erreichen. Für die genannte Planspielapplikation entwirckelten wir die in Kap.I genannten Dienste. 2.) Webserver Der Webserver bildet die Schnittstelle zwischen dem Webbrowser des Endbenutzers und dem Dienstemarkt der dem Benutzer zur Verfügung stellt. Gleichzeitig führt er die Dienste in einen Applikationskontext zusammen, so daß sie für den Benutzer nicht mehr als einzelne Einheiten erkennbar sind. Auch der Fluß der Applikation sowie die Darstellung der Benutzerschnittstelle wird vom Webserver übernommen. Um Wiederverwendbarkeit und Wartbarkeit zu gewährleisten wurde auch der Webserver in mehrere Stufen unterteilt. Hierbei galt es zu beachten, daß HTTP und somit auch Servlets Request-Responseorientiert sind, der Fluß der Applikation jedoch in vielen Kontexten mehrere zusammenhängende Aktion-Reaktion-Kombinationen erfordert. Um diese unterschiedlichen Charakteristika zu vereinen verwenden Webserver oftmals sogenannten Sessions. Einem Benutzer wird beim Beginn der Applikation eine Sitzung zugeordnet. Daten, die bei einem Request im Sessionkontext eines Benutzers gespeichert wurden sind beim nächsten Request, den ein Benutzer macht, wieder verfügbar. Oftmals bezeichnet man einen solchen Webserver auch als stateful. Zu beachten ist jedoch, daß die Session nur Informationen speichern sollte, die den Applikationsfluß betreffen. Andere Daten sollten von den transistenten und persistenten Speicherdiensten außerhalb des Webservers gespeichert werden. Weiterhin benötigt wird auf dem Webserver eine Softwarekomponente, die Informationen, die zur Laufzeit der Applikation entstehen, mit statischen Texten (HTML-Code und Text) zusammenführt. Diese Komponente wird im Weiteren als Template-Engine bezeichnet. Die Trennung von HTML-Code und Applikation hat mehrere Vorteile: Zum einen können die Texte und der HTML-Code von Personen gewartet werden, die kein oder nur geringes Wissen über den inneren Aufbau der eigentlichen Applikation wissen, zum anderen ist es dadurch möglich, die gleiche Funktionalität mit mehreren unterschiedlichen Interfaces zu präsentieren – also z.B. auch in unterschiedlichen Sprachen – ohne mehrere Versionen des Applikationsquelltextes pflegen zu müssen. Schließlich muß das Auffinden, die Kommunikation und die Fehlerbehandlung für die JiniDienste für die Anwendung möglichst an zentraler Stelle stattfinden. Aus diesem Grunde haben wir ein zentrales Service-Repository eingeplant, das Verbindungen zu Jini Services aufrecht erhält und den Servlets zur Verfügung stellt. Dadurch muß nicht bei jedem Request ein Discovery von Diensten stattfinden. 3.) Jini als Middleware In unserem Fall wird Jini an Stelle von konventioneller Kommunikations-Middleware wie CORBA oder OSF/DCE eingesetzt. Jini erlaubt einen erheblich dynamischeren Betrieb eines Dienstemarktes als die meisten heute eingesetzten Kommunikations-Middlewares. So muß der Standort des Namensdienstes zum Auffinden von Services bei herkömmlicher Kommunikations-Middleware meist bekannt sein. Oftmals stellt der Namensdienst deswegen einen sogenannten Single Point of Failure dar. Bei Jini suchen sich sowohl Clients als auch Services zur Laufzeit Namensdienste (Lookup Service Discovery). Services registrieren sich bei Lookup Services, um ihre Dienste für Clients zugänglich zu machen. Clients nutzen Lookup Services, um anhand von bestimmten Merkmalen Services zu finden. Dadurch wird es möglich, Load Balancing zwischen Lookup Services zu machen und dynamisch sowohl Lookup Services als auch andere Dienste zum Netz hinzuzufügen oder vom Netz zu entfernen. Diese Eigenschaften machen Jini für Serveranwendungen, die sich oft ändern und bei denen hohe Verfügbarkeit gefordert ist geradezu optimal. Die meisten heutigen Webanwendungen entsprechen diesem Profil. Durch Jini können Erweiterungen, Softwareupdates und Umstrukturierungen sehr einfach realisiert werden, ohne daß dabei Systemausfälle entstehen. Durch die Beschränkung von Jini auf Java und die Mächtigkeit von RMI können stabile und performante Anwendungen mit Jini sehr viel schneller entwickelt werden als mit anderer Kommunikationsmiddleware. 4.) Datenbank In den meisten heutigen Serveranwendungen werden Datenbanksysteme zur persistenten Speicherung von Daten verwendet. Weil relationale Datenbanksysteme einen QuasiStandard darstellen entschieden wir uns für ein solches System. Dadurch sollte sichergestellt sein, daß die Anwendung später einfach zu warten ist. Java stellt zum Zugriff auf Datenbanken ein API namens JDBC (Java DataBase Connectivity) zur Verfügung. Seit der Version 1.1 gehört dieses API zum Lieferumfang des JDK (Java Development Kit). Der Webserver selbst „kennt“ die Datenbank jedoch nicht. Er kennt nur die Jini Services, die die Datenbank als Speicher benutzen. Die Daten, die für die Anwendung gebraucht werden, sind recht einfach. Das Datenbankschema in Kapitel V erläutert den Aufbau der Datenbank. In der Anwendung gibt es eine Ausnahme in Hinsicht auf persistente Speicherung. Der Fileserver-Dienst muß größere Mengen an Daten speichern, die von der Anwendung nicht direkt interpretiert werden müssen. Daher wird hierfür das Dateisystem des Hosts benutzt, auf dem der Service läuft. Metadaten über die Dateien werden jedoch in der Datenbank gespeichert. 5.) Objektmodell der Geschäftobjekte Die Zusammenhänge der Klassen entsprechen weitgehend den Datenbanktabellen. Diese sind in Kapitel V detailliert besprochen. Zu beachten ist, daß die Klassen jeweils den Packages des entsprechenden Jini-Services zugeordnet sind. Detaillierte Dokumentation zu den einzelnen Klassen kann man der API-Dokumentation und dem Quellcode im Anhang entnehmen. 6.) Objektmodell des Webservers Zusätzlich zu den Geschäftsobjekten existieren noch Klassen, aus denen die eigentliche Applikation zusammengesetzt wird. Da die Jini Services im allgemeinen aus einer Klasse bestehen und daher trivial sind, wird hier nur das Objektmodell des Webservers kurz erläutert dargelegt. Im Webserver existiert eine zentrale Stelle, die für die Kommunikation mit der Jini Services verantwortlich ist. Der sogenannte Service-Controller stellt das Service Repositoy des Webservers dar. Er stellt den einzelnen Servlets sehr einfache Methoden zur Verfügung, um Referenzen der Jini Services zu bekommen. Das Discovery und das Lookup wird vom Service-Controller übernommen. Die Servlets steuern große Teile der Webanwendung und generieren mit Hilfe einer sogenannten Template Engine den HTML-Code, der schließlich als Antwort auf eine Anfrage an den Browser des Benutzers zurückgegeben wird. Alle Servlets benutzen zusammen einen Service Controller. III. Dienste III.) Dienste 1.) Der Spielmanager-Dienst Der Spielmanager-Dienst ist für die Verwaltung von Spielen, Spielern und Gruppen zuständig. Zudem dient er zur Authentifizierung der Spieler. Der Spielmanager-Dienst operiert im allgemeinen mit vier Business-Objekten: - Game, stellt ein Spiel dar GameMaster, der Leiter eines Spieles Player, ein Mitspieler Group, eine Gruppe eines Spieles zu der mehrere Spieler gehören Zur Authentifizierung übergibt der Client des Spielmanager-Dienstes, in unserem Fall also der Webserver, den Benutzernamen und das Passwort an den Spielmanager-Dienst. Der Spielmanager-Dienst überprüft das Passwort und liefert – sofern es gültig war – ein PlayerObjekt an den Client zurück. public Player authenticatePlayer(String login, String password) Dabei ist zu bemerken, daß der Spielmanager-Dienst das eigentliche Passwort des Benutzers nie preisgibt, das heißt die Authentifizierung ist in seinem Verantwortungsbereich angesiedelt. Ähnliche Methoden existieren auch für Spielleiter. Zudem bietet der Spielleiter-Dienst Methoden zum Erzeugen, Löschen und Auflisten von Spielen, Spielleitern, Spielern und Gruppen. Für die Attribute in den genannten Objekten, wie zum Beispiel die Sprache eines Spielers, die sich nach der Erzeugung der Spielerobjektes ändern können, existieren Methoden zur Änderung. Außerdem stellt der Spielmanager-Dienst auch Methoden zur Verfügung, die es ermöglichen, allen Spielern eines Spiels, einer Gruppe oder einzelnen Spielern oder Spielleitern Emails zuzuschicken. Zur Durchführung dieser Aufgaben benutzt der Spielmanager-Dienst den Mail-Dienst. Schließlich steht noch eine Methode zur Verfügung, mit deren Hilfe einem Teilnehmer (Spielleiter oder Spieler) , für den Fall daß er es vergessen hat, eine Mail mit seinem Passwort zugeschickt wird. 2.) Der Fileserver-Dienst Der Fileserver-Dienst stellt Methoden zur Verfügung, um Dateien zu Speichern und auf gespeicherte Dateien zuzugreifen. Dateien werden dabei immer in sogenannten Places gespeichert. Ein Place wird physikalisch auf ein Verzeichnis abgebildet und durch einen String identifiziert. Der Einfachheit halber verzichteten wir auf eine Verwaltung von Unterverzeichnissen. Somit ergibt sich eine flache Dateistruktur. Das einzige Business Object, das der Fileserver-Dienst verwendet ist FileInfo. FileInfo speichert Zusatzinformationen, die nicht direkt im Dateisystem gespeichert werden können. Dazu gehört zum Beispiel der Login des Teilnehmers, der die Datei abgespeichert hat. Die eigentliche Datenübertragung erfolgt über ein byte-Array, das serialisiert und über RMI verschickt wird. public byte[] getFile(String place, String name) public void putFile(String place, String uploader, String name, byte[] content) Diese Übertragungsart setzt vorraus, daß die gesamte Datei vor der Übertragung in den Hauptspeicher des Servers geladen werden muß. Dies ist für Anwendungen, bei denen sehr große oder sehr viele Dateien übertragen werden müssen, nicht praktikabel. Für solche Anwendungen kann man zum Beispiel Streams verwenden. Der Einfachheit halber und weil für ein Planspiel keine sehr großen Dateien benötigt werden haben wir uns dennoch für diese Übertragungsart entschieden. 3.) Der Message-Board-Dienst Der Message-Board-Dienst verwaltet die Nachrichtenbretter, über die die Teilnehmer der Planspiele kommunizieren können. Er benutzt ein Business Object namens Message. Eine Message stellt eine Nachricht dar, die in einem Message-Board veröffentlicht worden ist. Message-Boards werden über Strings identifiziert. Die Nachrichten können Bezüge untereinander haben. So kann eine Nachricht eine die Antwort auf eine andere Nachricht sein. Der Message-Board-Dienst stellt Methoden zur Verfügung, um Nachrichten zu erzeugen und zu löschen sowie eine Methode, um ein komplettes Message-Board zu löschen. Wird eine Nachricht zu einem Message-Board hinzugefügt ohne daß ein Message-Board mit dem angegebenen Namen existiert, so wie automatisch ein neues Message-Board erstellt. 4.) Der Mail-Dienst Der Mail-Dienst stellt der Anwendung und anderen Diensten die Möglichkeit zur Verfügung, E-Mails zu versenden. Dabei besteht die Möglichkeit, Schablonen für eine Mail zu benutzen, sogenannte Templates. Templates werden in eine Datenbanktabelle verwaltet, die mit externen Tools oder von Hand gepflegt werden muß. Die Tabelle enthält unter anderem den kompletten Text und das Subject. Folgendes Template ist zum Beispiel denkbar: Hallo <snd_name> ! Ich freue mich sehr, daß Sie in der Gruppe <group> mitspielen werden. Ich werde Ihnen zum baldmöglichsten Termin eine Spieldiskette für das <game_type> zukommen lassen. Furtwangen, <datum>, <game_master_name> Die von < und > eingeschlossenen Strings stellen Variablen dar, an deren Stelle der MailDienst Werte einsetzt. Einige Werte wie die Email-Adressen des Senders und Empfängers sind dabei schon vorgegeben, weitere Werte können dem Mail-Dienst in einer Hashtable mitgeteilt werden. Folgender Methodenaufruf wird benutzt, um unter Zuhilfenahme eines Templates eine Mail zu versenden: public void sendTemplate(String template, String snd_email, String snd_name, String rcp_email, String rcp_name, Hashtable strings) Die Hashtable namens string enthält die Werte für die Variablen. Zum endgültigen Versand der Emails benutzt der Maildienst die Java Mail API und einen SMTP-Server. 5.) Chat-Server Der Chat-Server dient zur Kommunikation der Spieler einer Gruppe untereinander und zum gruppenübergreifenden Chat der gesamten Spielteilnehmer. Der Chat ist im "öffentlichen Bereich" nicht zugänglich, d.h. am Chat können nur angemeldete Benutzer teilnehmen. Das Chat-System ist in 5 Komponenten unterteilt. - der eigentliche ChatServer, der als Jini-Dienst auf einem beliebigen Server läuft, der ChatController der als Servlet auf dem Webserver im Hintergrund läuft und die Verbindung zum ChatServer Jini-Dienst aufnimmt, das ChatOut-Servlet das die Ausgabe der Chatzeilen für den entsprechenden Chatroom übernimmt, das ChatIn Servlet das die Eingabe der Chatzeilen übernimmt und sie an den ChatController weiterreicht und das SessionManagement Servlet ist für das Session Management zuständig und bietet dem Spieler verschiedenste Möglichkeiten Einstellungen für das ChatSystem vorzunehmen. a.) Der ChatServer und seine Klassen Zu Beginn der Implementierungsphase erstellten wir zuerst ein Remote-Interface (Chat) welches die notwendigen Methoden deklariert. Der ChatController kommuniziert über dieses Interface mit dem ChatServer. Die Klasse ChatServer implementiert das Chat-Interface und erhält dadurch folgende Funktionalität: Über entsprechende Methoden kann ein Chatroom angelegt bzw. entfernt werden. Ist der Chatroom bereits angelegt wird keine Aktion ausgeführt. Der Client (in unserem Fall der ChatController) hat die Möglichkeit über eine Methode eine bestimmte Anzahl der zuletzt eingefügten Chatzeilen anzufordern. Das ist vor allem sinnvoll für Spieler die sich neu bei dem Chatroom anmelden. Weiterhin bietet das Interface eine Methode, um eine neue Chatzeile in einen bestimmten Chatroom einzufügen. In dieser Methode werden über eine Distributed Event Notification auch alle registrierten Clients über die neu eingetroffene Chatzeile informiert bzw. bekommen die neue Chatzeile übergeben. Wir verwenden für die registrierten Clients eine eigene Klasse (ChatEventData), in der wir die EventDaten ablegen. b.) Der ChatController Der ChatController übernimmt die Rolle des "RemoteEventListeners" und erhält Events, die vom ChatServer beim Eintreffen neuer Chatzeilen versendet werden. Er ist somit in der Lage, neue Chatzeilen ("verpackt" im Event-Objekt) zu empfangen und in einem Vector abzulegen. Von dort aus können die Zeilen wiederum weiterverarbeitet werden. Für jeden Chatroom wird ein ChatController erzeugt. Zur Kontrolle ordnet der ChatController jedem angemeldeten Client im entsprechenden Chatroom eine nur einmal vorkommende Zahl zu. c.) Das ChatOut-Servlet Das ChatOut-Servlet dient der Anzeige aller Chatzeilen für die Clients. Das Servlet bedient sich des Vektors, welcher vom ChatController gefüllt wird und holt der Reihe nach die Chatzeile heraus. Dieser Vorgang läuft in einer Endlos-Schleife ab, damit zum einen die Zeilen mit möglichst verschwindend geringer Verzögerung beim Client angezeigt werden können und zum anderen der HTTP-Stream stets "offen" gehalten wird. d.) Das ChatIn-Servlet Das ChatIn-Servlet beinhaltet die Eingabezeile für die Chat-Benutzer. Sendet der Benutzer die Eingabe ab, so wird die Chat-Zeile mit dem entsprechenden Benutzernamen ergänzt und mit der zuvor ausgewählten Farbe formatiert. Dann wird der erzeugte String an den ChatServer gesendet. Dabei wird auch der Name des Chatrooms übergeben, damit die Zeile auch bei den richtigen Chat-Benutzern (die im gleichen Chatroom angemeldet sind) erscheint. In unserem Codebeispiel erzeugen wir standardmässig einen Chatroom „room2“, da die gesamte Applikation, die auch die Übergabe des zu erstellenden Chatrooms beinhaltet, noch nicht komplett programmiert wurde. e.) Das SessionManager-Servlet Das SessionManager-Servlet erstellt pro angemeldeten Benutzer eine eindeutige Session, auf die die Servlets zugreifen können. Auf diese Art können die Servlets Client-Bezogene Daten austauschen. Wir verwenden diese Möglichkeit in unserem Chatsystem um dem Benutzer die Auswahl aus verschiedenen Farben (schwarz, grün, rot, blau) zu ermöglichen und seinen „Nickname“ zu setzen. Wurde noch kein SessionManager-Servlet erzeugt greift das ChatIn-Servlet auf DefaultWerte zurück. IV. Webserver IV.) Webserver 1.) Service-Controller Wie in Kapiel II.2 schon kurz erwähnt implementierten wir ein generisches ServiceRepository, das es dem Webserver ermöglichen soll, eine ständige Verbindung zu JiniDiensten aufrecht zu erhalten. Dabei sollte ein Failover-Mechanismus implementiert werden, der die Verfügbarkeit von Diensten in regelmäßigen Intervallen überprüft. Fällt ein Dienst aus, so an dessen Stelle ein anderer, gleichwertiger Dienst benutzt werden, der im Netzwerk angeboten wird. Als Schnittstelle zwischen dem eigentlichen Webserver und den Jini-Diensten implementierten wir deswegen einen Service-Controller. Zusätzlich zu den oben genannten Funktionen hat der Service-Controller die Aufgabe, für den Fall, daß ein Dienst ausfällt und für eine gewisse Zeit kein Ersatzdienst auffindbar ist, einen Systemadministrator per Email zu benachrichtigen. Beim Start des Webservers muß dieser den Service-Controller instantiieren und diesem mitteilen, welche Jini-Dienste er benötigt. Dazu stellt der Service-Controller folgende Methode bereit. public void addSearchClass(String classname) Ähnliche Methoden mit anderen Suchkriterien währen natürlich denkbar und sinnvoll, wir beschränkten und jedoch im Rahmen dieser Fallstudie darauf, den Klassennamen des JiniDienstes als Suchkriterium zu verwenden. Entsprechend gibt es auch eine Methode, um einen Klassennamen von der Liste der gesuchten Dienste zu entfernen. Danach kann der Webserver den Service-Controller mittels folgender Methode benutzen: public Object getService(String classname) Die Methode liefert direkt eine Referenz auf den gesuchten Dienst zurück. Dieser Methodenaufruf wird in aller Regel von dem Code benutzt, der eine HTML-Seite generiert. Um eine hohe Verfügbarkeit des Webservers zu erreichen kann man unter Zuhilfenahme der oben genannten Failover-Mechanismen mehrere Instanzen der einzelnen Dienste auf unterschiedlichen Hosts starten. Fällt einer der Hosts aus, so führt der Service-Controller ein erneutes Discovery durch und verwendet ab diesem Zeitpunkt einen alternativen Dienst. Zusätzlich wäre denkbar, auf Basis des Dienstemarktes von Jini einen sehr dynamischen Load-Balancing-Mechanismus zu implementieren. Dazu müsste man den Service-Controller so modifizieren, daß er alle Verfügbaren Dienste abwechselnd benutzt. 2.) Dynamische Seitengenerierung Auch die Generierung der HTML-Seiten basiert auf Templates. Wie schon in Kapitel II.2 erwähnt, sollte die eigentliche Programmierung und die Entwicklung der HTML-Seiten von verschiedenen Personen möglichst getrennt durchzuführen sein. Zum Einsatz kam dazu eine Template Engine von Stefan Zier namens FastHtml. FastHtml unterteilt HTML-Seiten in sogenannte Blöcke. Dabei hat jede HTML-Seite einen Hauptblock, der den Rahmen für die Seite darstellt. Weitere Blöcke, die entweder aus anderen Dateien geladen werden oder in der Hauptdatei definiert werden können dienen als Hilfsmittel, um die komplette Seite zu gestalten. Folgendes Beispiel verdeutlicht die Funktionsweise: <% servlet="mm.fasthtml.FastHtmlTest" %> <html> <head> <title>$$ var="title" $$</title> </head> <body bgcolor="$$ body_color $$"> <h2>$$ var="headline" $$</h2> Hello $$ var="name" $$ how are you?<p> <table> $$ var="member_list" $$ </table> </body> </html> <% block="member_list_line" %> <tr> <td>$$ var="name" $$</td> <td><a href="mailto:$$ var="email" $$"><b>$$ var="email" $$</b></td> <td>$$ var="phone" $$</td> </tr> Die Tags, die mit <% und %> umgeben sind stellen den Beginn einer Blockdefinition dar. Tags die von $$ umgeben sind sind Variablen innerhalb eines Blockes. Variablen können entweder direkt vom zugehörigen Servlet eingesetzt werden, oder es können statisch Blöcke inkludiert werden. Beim Aufruf einer URL (z.B. http://net5.foo.fh-furtwangen.de/index.fh) wird zunächst die Template-Engine aufgerufen, die dann die Kontrolle an das in der servlet=-Klausel des Hauptblocks genannte Servlet abgibt. Nachdem das Servlet die entsprechenden Variablen einer Hashtable mit Werten gefüllt hat (ggf. unter Zuhilfenahme von Blocks) gibt es die Kontrolle zurück an die Template-Engine, die sich schließlich um die Ausgabe der Seite zum Benutzer kümmert. V. Datenbank V.) Datenbank Die folgende Grafik zeigt das zugrundeliegende Datenbankschema. Mail_template file gameMaster game PGroup msgboard Player message Abbildung 5.1 Datenbankschema Wie man erkennen kann, sieht das Schema mehrere Spielleiter vor. Jedes Spiel hat genau einen Spielleiter. Ein Spielleiter kann mehrere Spiele verwalten. Ein Spiel hat mehrere Gruppen, jede Gruppe gehört zu genau einem Spiel. Das gleiche Verhältnis besteht zwischen einem Spieler und einer Gruppe. Jede Gruppe hat zudem ein Nachrichtenbrett. Darüberhinaus hat jedes Spiel ein Nachrichtenbrett, das für alle Spielergruppen erreichbar ist. Ein Nachrichtenbrett hat mehrere Nachrichten, die jeweils einem Spieler (dem Absender) zugeordnet sind. Zudem hat eine Nachricht optional eine Elternnachricht, also ist die Antwort auf eine andere Nachricht. Weiterhin können Gruppen und Spiele beliebig viele Dateien haben. Schließlich gibt es eine Tabelle, in der die Mail Templates gespeichert werden. VI. Praxiserfahrungen, Produkte VI.) Praxiserfahrungen, Produkte 1.) Servlet Engines Im Laufe unseres Projektes testeten wir vier verschiedene Servlet-Engines. Aufgrund positiver Erfahrungen eines unserer Projektmitglieder mit JRun von Live-Software (mittlerweile von Allaire gekauft) entwickelten wir zuerst mit JRun als Servlet-Engine. a.) JRun von Allaire JRun erfüllte unsere Bedingungen, es war frei verfügbar (in der Standard-Version), es unterstützte das Pre-Loading von Servlets und es war bezüglich der Performance für unsere Anwendung ausreichend. JRun bietet die Möglichkeit entweder als Modul für Apache zu laufen, oder als eigenständiger Webserver. Wobei bei letzterer Anwendung alle html-pages als Servlets bzw. Jsp-Seiten geschrieben sein müssen. Wir entschieden uns für den Einsatz von JRun als Standalone-Webserver. Bis zu einem gewissen Punkt hatten wir keine Probleme mit JRun, wenn man von den etwas unübersichtlichen Konfigurationsdateien und der mageren Dokumentation absieht. Weitaus grössere Probleme bereitete uns die Ausgabe der Chatzeilen in unserem Servlet. Die Ausgabe erschien immer erst nach einer gewissen Zeit bzw. einer gewissen Anzahl von übertragenen Bytes. Wir nahmen an, dass es sich dabei um einen Buffer handelt, der die Ausgabedaten erst zwischenspeichert und nach einer bestimmten Anzahl von Bytes ausgibt. Nach mehrmaliger Rücksprache mit Live-Software stellte sich heraus, dass es sich um einen „hardgecodeten“ internen Buffer von JRun handelt. Somit war unsere mehrwöchige Arbeit mit dieser Plattform obsolet und wir mussten uns nach einer anderen Servlet-Engine umsehen. b.) Tomcat/Jakarta Projekt der Apache Software Foundation Diese Servlet-Engine bietet ebenfalls die Möglichkeit als Standalone-Webserver zu arbeiten oder mit Hilfe eines Connectors als Servlet-Engine für den Apache-Webserver zu arbeiten. Leider war dieses Produkt zum Zeitpunkt unseres Tests noch sehr instabil und auch zu langsam um unseren Anforderungen zu genügen. Es stellte sich auch heraus, dass diese Servlet-Engine nicht das Pre-Loading von Servlets unterstützt. Die Installation erwies sich als äusserst schwierig und schlecht dokumentiert. Wir kamen nach einer kurzen Testphase zu dem Schluss, dass diese Servlet-Engine nicht unseren Ansprüchen genügt. c.) Jserv/Apache Software Foundation Jserv kann nur als Modul in Verbindung mit dem Apache Webserver eingesetzt werden. Zur Kommunikation zwischen dem Webserver und Jserv wurde ein eigenes Protokoll entwickelt. Nach etwas schwieriger Installation stellte sich heraus, das dieses Protokoll eine Leistungsbremse für die Servlet-Engine darstellt. Ausserdem gab es zu bemängeln, dass die Dokumentation im Bezug auf Einrichtung eines Servlets zu mager war. Es stellte sich dann auch als zu kompliziert heraus, ein einfaches Servlet einzubinden und zu testen. Jserv arbeitet mit sog. Servlet Zones die über 3 verschiedene Konfigurationsfiles konfiguriert werden müssen. Jserv bietet die Möglichkeit des Pre-Loadings von Servlets, leider wurden wir in der Dokumentation nicht fündig, wie man dieses Feature aktiviert. Zudem hatte Jserv dasselbe Problem wie JRun in bezug auf die Ausgabe unseres ChatServlets – die Zeilen erschienen stark verzögert nach der Eingabe. Somit kam auch diese Servlet-Engine für uns nicht mehr in Frage. d.) Jigsaw/W3C Jigsaw ist ein Standalone-Webserver mit Servlet-Engine. Angenehm aufgefallen ist uns die sehr einfache, wie auch reibungslose Installation von Jigsaw. Des Weiteren wird auch eine sehr ausführliche Dokumentation mitgeliefert. Zudem bietet Jigsaw die Möglichkeit die Servlets per Java-Administrationstool sehr einfach grafisch einzubinden. Mit dieser Servlet-Engine war es nun endlich möglich, unser ChatServlet ohne verzögerte Ausgabe laufen zu lassen. Ein weiterer grosser Vorteil von Jigsaw ist seine ausgezeichnete Performance und sein sehr gutes Antwortverhalten unter Vollast (siehe dazu Kapitel VII). Letztendlich bietet Jigsaw die Möglichkeit Servlets dynamisch im laufenden Betrieb hinzuzufügen und zu testen, was sich gerade in der Testphase als sehr nützliches Feature erweist. 2.) Virtual Machines Im Rahmen des Projektes analysierten wir auch einige Java Virtual Machines. Da wir als Zielplattform entweder Linux oder Solaris einsetzen wollten analysierten wir speziell diese Plattformen. Für Solaris gibt es aktuell nur die JVM von Sun, wahlweise mit einer sogenannten Performance Engine namens HotSpot, ein etwas ausgeklügelterer JIT (Just-In-TimeCompiler). Wir testeten diese JVM in der Version 1.2.2. Die Performance war erwartungsgemäß sehr gut, mit Hotspot noch etwas besser. Auch waren keine Stabilitätsprobleme festzustellen. Für Linux gibt es mittlerweile recht viele JVMs. Wir schauten uns IBMs JVM 1.1.8, die Betaversion des Blackdown-Ports der Sun JVM sowie den Release Candidate 1 der JVM von Sun an. Die IBM JVM schied recht bald aus, da Jini auf Java 1.2 aufsetzt. Ansonsten stellten wir bei der IBM JVM die beste Performance unter Linux fest. Leider war die JVM nicht immer stabil und zeigte stellenweise Fehler. Der Blackdown-Port ist höflich ausgedrückt als Katastrophe zu bezeichnen. Keiner unserer Tests mit vielen Threads lief durch. Entweder die JVM blieb einfach stehen oder verabschiedete sich mit einem Segmentation Fault. Bei den anderen Tests „glänzte“ Blackdown durch mangelhafte Performance. Der erst seit kurzer Zeit verfügbare Release Candidate 1 der Sun JVM 1.2.2 für Linux, der aus dem Blackdown-Source-Tree heraus in Zusammenarbeit mit Inprise entwickelt wurde machte hingegen einen sehr guten Eindruck. Die Performance war zwar nicht ganz so gut wie die der IBM JVM, jedoch lief der Release Candidate 1 schon erheblich stabiler als die anderen beiden Kandidaten unter Linux. Weil unsere Zielplattform vermutlich ein Intel-Rechner gewesen wäre und Solaris/Intel eher eine Exotenrolle spielt, hätten wir uns für Linux mit der Sun JVM entschieden. 3.) Jini a.) Zuverlässigkeit Zu Projektbeginn war Jini von Sun Microsystems in der Version 1.0 verfügbar. Mit dieser Version arbeiteten wir in den ersten beiden Monaten. Hierbei kam es immer wieder zu Problemen beim Starten des RMI-Daemons und dem Starten des Lookup-Service. Dies führte teilweise dazu, dass auf dem Entwicklungssystem zehn und mehr Java Virtual Machines gestartet wurden. Dieses Phänomen hat zur Anfangsphase unseren Entwicklungsfortschritt stark eingeschränkt. Konnte der Jini-Dienst einmal gestartet werden, lief es, auch über eine längere Zeit, stabil und zuverlässig. Seit der Version 1.01 der Jini-Implementation verschwanden diese Probleme vollständig. Besonders während der Tests unserer Dienste konnten wir dadurch viel Zeit und Arbeit sparen. Als Fazit bleibt zu sagen, dass Jini zumindest seit der Version 1.01 durchaus im produktivem Umfeld einsetzbar ist und auch stabil und zuverlässig läuft. Dabei sind jedoch die Anmerkungen zur Managebarkeit im nächsten Punkt zu beachten. b.) Managebarkeit Damit alle Voraussetzungen für ein funktionstüchtiges Jini-System mit mehreren JiniDiensten in einer heterogenen Umgebung erfüllt werden können, ist gleich zu Beginn mit einem erheblichen Konfigurationsaufwand zu rechnen. Grundlegende Dienste, wie zum Beispiel der Lookup-Service zum Anmelden und Auffinden von Diensten, müssen auf jeden Fall stabil und zuverlässig laufen. Andernfalls muss man während der Entwicklungs- und Testphase grosse Probleme in Kauf nehmen (z.B. bei der Fehlersuche). Zu den grundlegenden Konfigurationsarbeiten gehört die Erstellung eines Start-Skripts für den Jini-Lookup-Service und das Setzen des korrekten "Classpaths“. Es wäre zu wünschen, dass Sun in Richtung Managebarkeit noch eine einfachere Lösung bereitstellen würde, da die meisten Administratoren mit dem bisher notwendigen Aufwand überfordert sind, bzw. ihnen die Zeit fehlt, das System aufzusetzen. 4.) Datenbanken Aufgrund der sehr einfach strukturierten Datensätze und der Bedingung, dass die von uns verwendete Datenbank nichts kosten darf, entschieden wir uns für MySQL als Datenbanksystem. MySQL von T.c.X DataKonsultAB (www.tcx.se) ist für Privatanwender kostenlos. Zudem ist die Installation sehr einfach und mit MySQL kommt eine sehr ausführliche Dokumentation mit. Es handelt sich hierbei um ein multi-threaded und multi-user Datenbanksystem, das auf verschiedenen Plattformen läuft und bietet APIs für Programmiersprachen wie C, C++, Java, Perl etc., um einige Features zu nennen. Obwohl mysql keine referentielle Integrität und prepared Statements bietet war es für unsere Anwendungszwecke völlig ausreichend. VII. Performance, Benchmarks VII.) Performance, Benchmarks In diesem Kapitel versuchen wir einen Einblick in die Möglichkeiten einer Jini-Anwendung im Hinblick auf Performance zu geben. Wir testeten unsere Applikation nur mit der ServletEngine vom W3C (Jigsaw). Trotzdem kann man dadurch Rückschlüsse auf die Leistungsfähigkeit einer Jini/Servlet Applikation erhalten. Im folgenden sei unser Testsystem kurz umrissen: Hardware: - Sun Ultra1 170MHZ - 192MB RAM - 2*IBM SCSI HD (2GB+4GB) - Onboard 10mbit Netzwerkkarte Software: - Solaris 7 Kernel patch 106541-07 - Jigsaw 2.03 - JDK 1.2.1_04 ohne HotSpot - JSDK 2.0 - Jini 1.0.1 - MySQL 3.22.26a Alle unsere Dienste liefen auf dieser Maschine. Mann kann sícher noch eine Performancesteigerung erreichen, wenn man diese Dienste auf verschiedene Maschinen verteilt. Auch die Begrenzung der Netzwerkkarte auf nur 10 MBit kann ein Bottleneck darstellen. Als Benchmark kam ein selbstentwickeltes Java-Tool zum Einsatz, das in mehreren Threads permanent Requests an eine bestimmte URL schickt. Das Benchmark-Tool lief dabei auf einer separaten Maschine. Wir führten 3 Tests mit jeweils 3 Durchgängen von jeweils zwei Minuten durch. Es wurden 150 Threads erzeugt, welche die Page-Views erzeugen. 1. Test: Jigsaw in Verbindung mit einer ganz normalen Html-Seite. 2. Test: Jigsaw in Verbindung mit einem Servlet das keinen jini-Dienst in Anspruch nimmt. 3. Test: Jigsaw in Verbindung mit einem Servlet das einen Jini-Dienst in Anspruch nimmt. Im Folgenden ist für jeden Testlauf (Run) die Anzahl der erreichten Page-Views zusammen mit dem Durchschnitt angegeben: Run1 Test1 Test2 Test3 Run2 18287 16576 12087 Run3 18305 16631 12275 Durchschnitt 18461 16404 12141 18351 16537 12168 Test1: Jigsaw in Verbindung mit einer ganz normalen Html-Seite 18500 Page-Views 18450 18400 18350 18300 18250 Du rch sc hn itt Ru n3 Ru n2 Ru n1 18200 Du rch sc hn itt Ru n3 Ru n2 16650 16600 16550 16500 16450 16400 16350 16300 16250 Ru n1 Page-Views Test2: Jigsaw in Verbindung mit einem Servlet das keinen jini-Dienst in Anspruch nimmt Test3: Jigsaw in Verbindung mit einem Servlet das einen Jini-Dienst in Anspruch nimmt 12300 12200 12150 12100 12050 12000 Du rch sc hn itt Ru n3 Ru n2 11950 Ru n1 Page-Views 12250 VIII. Zusammenfassung, Abschlußbericht VIII.) Zusammenfassung Die genannten Technolgien eignen sich unseres Erachtens sehr gut zur Entwicklung ausbaubarer, ausfallsicherer und skalierbarer, verteilter Webapplikationen. Jedoch befinden sich einige der Technologien noch in einem frühen Stadium. Daher entstehen oftmals Schwierigkeiten und schwer zu erklärende Fehler. Durch die Vielzahl der Technologien, die für eine derartige Applikation zusammenarbeiten müssen, ist der Aufbau einer einfach zu wartenden Laufzeitumgebung mitunter eine sehr komplexe Aufgabe. Weiterhin ist eine solche Umgebung für einen Systemadministrator, der nicht aktiv an der Entwicklung der Software beteiligt ist, schwer oder gar nicht nachzuvollziehen. Deswegen sollte man bei derartigen Projekten speziell auf ein stark ausgeprägtes Konfigurationsmanagement achten. Ohne diese Grundlage wird es unmöglich, den Zeitaufwand für die Entwicklung und den Test von Applikationen abzuschätzen. Weiterhin ist es empfehlenswert, im Laufe des Projektes eine Art Knowledge-Base für schwer zu findende Probleme einzurichten. Zudem sollte man soweit möglich im Vorfeld des Projektes Zielplattform und Tools (Virtual Machine, Servlet Engine etc) testen und auswählen und die Anwendungen in dem so entstehenden Umfeld entwickeln. IX. Anhang IX.) Anhang 1.) Übersicht Im Folgenden ist der Einfachheit halber die komplette API-Dokumentation sowie der Quellcode der Fallstudie abgedruckt. Der Quellcode sowie die komplette Dokumentation sind ebenfalls auf der beigelegten CD-ROM zu finden. WICHTIG: Die Software ist nur soweit implementiert, daß die für die Fallstudie wichtigen Schlüsse gezogen werden konnten. Sie ist nicht geeignet, um im Produktionsbetrieb eingesetzt zu werden. Viele Funktionen sind fehlerhaft oder fehlen. 2.) Packages Im folgenden eine kurze Übersicht über die Packages, aus denen die Software besteht. Package mm.jini mm.services mm.services.chat mm.services.files mm.services.game mm.services.mailer mm.services.msgboard mm.tools mm.webserver mm.webserver.servlets mm.webserver.sessions Bedeutung Klassen, die im unmittelbaren Zusammenhang mit Jini stehen Die Jini-Dienste Klassen des Chat-Dienstes Klassen des File-Dienstes Klassen des Spielverwaltungs-Dienstes Klassen des Mail-Dienstes Klassen des Messageboard-Dienstes Diverse Hilfsklassen Klassen, die im unmittelbaren Zusammenhang mit dem Webserver stehen Servlets Session-Management a.) mm.jini Dieses Paket enthält lediglich den Service-Controller. Er wird in diesem Projekt nur vom Webserver benutzt, anderer Einsatz ist jedoch denkbar. Klasse ServiceController Bedeutung Verwaltet die Verbindung zu mehreren Jini Services All Packages Class Hierarchy This Package Previous Next Index Class mm.jini.ServiceController mm.jini.ServiceController public class ServiceController Service Controller. Managed Discovery und Lookup und die Suche nach bestimmten Services. Erhält einen Thread am Leben, der ggf. ausgefallene Services automatisch wieder sucht und zur Verfügung stellt. Version: 1.0 Author: Stefan Zier ServiceController() Konstruktor. addSearchClass(String) Fügt eine Klasse zur Suchliste hinzu checkServices() Überprüft ob alle Services noch laufen. discarded(DiscoveryEvent) Wird aufgerufen wenn Lookup Services verschwinden discovered(DiscoveryEvent) Wird aufgerufen wenn ein Lookup Service gefunden wird findServices() Versucht Services die nicht mehr laufen wieder zu finden. getService(String) Liefert einen gefundenen Service zurück. removeSearchClass(String) Löscht eine Klasse aus der Suchliste searchClasses() Sucht alle bekannten Lookup Services nach den verlangten Klassen ab und fügt sie ggf. ServiceController public ServiceController() throws java.io.IOException Konstruktor. Startet das Lookup Discovery. getService public java.lang.Object getService(java.lang.String classname) Liefert einen gefundenen Service zurück. Parameters: classname - der Name der Serviceklasse Returns: einen Pointer auf die Serviceklasse addSearchClass public void addSearchClass(java.lang.String classname) Fügt eine Klasse zur Suchliste hinzu Parameters: classname - der Name der Klasse die gesucht werden soll removeSearchClass public void removeSearchClass(java.lang.String classname) Löscht eine Klasse aus der Suchliste Parameters: classname - der Name der Klasse die entfernt werden soll discovered public void discovered(net.jini.discovery.DiscoveryEvent event) Wird aufgerufen wenn ein Lookup Service gefunden wird Parameters: event - das Event discarded public void discarded(net.jini.discovery.DiscoveryEvent event) Wird aufgerufen wenn Lookup Services verschwinden Parameters: event - das Event checkServices public boolean checkServices() Überprüft ob alle Services noch laufen. Returns: true wenn alle Services noch laufen findServices public void findServices() Versucht Services die nicht mehr laufen wieder zu finden. searchClasses public void searchClasses() Sucht alle bekannten Lookup Services nach den verlangten Klassen ab und fügt sie ggf. zu dem Vector hinzu. All Packages Class Hierarchy This Package Previous Next Index package mm.jini; // Java core imports import java.io.IOException; import java.rmi.RemoteException; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; // Jini imports import net.jini.discovery.DiscoveryListener; import net.jini.discovery.DiscoveryEvent; import net.jini.discovery.LookupDiscovery; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.lookup.ServiceMatches; import net.jini.core.lookup.ServiceItem; // Project imports import mm.tools.AliveCheckable; /** * Service Controller. Managed Discovery und Lookup und die Suche nac h bestimmten Services. Services * Erhält einen Thread am Leben, der ggf. ausgefallene Services automatisc h wieder sucht * und zur Verfügung stellt. stellt * * @author Stefan Zier Zie * @version 1.0 1. */ public class ServiceController implements DiscoveryListener { protected Vector searched = null; protected Hashtable found = null; protected Vector registrars = null; protected Hashtable last_status = null; private final String STATUS_OK = "OK"; private final String STATUS_NEW = "NEW"; private final String STATUS_ERROR = "ERROR"; private final String MAIL_SERVER = "mm.services.mailer.MailServer"; private final String STATUS_MAIL_RECIPIENT = "[email protected]"; // 2 Minuten, länger darf nicht auf einen Dienst gewartet werden private final long timeout = 120000; private long start_time; /** * Konstruktor. Startet das Lookup Discovery. Discovery */ public ServiceController() throws IOException { // searched, found und registrars initialisieren searched = new Vector(); found = new Hashtable(); registrars = new Vector(); last_status = new Hashtable(); // lookup discoverer starten LookupDiscovery ld = new LookupDiscovery(LookupDiscovery.NO_GROUPS); // uns selbst als discovery listener hinzufügen ld.addDiscoveryListener(this); ld // lookup discoverer starten ld.setGroups(LookupDiscovery.ALL_GROUPS); ld // searcher thread starten new SearcherThread().start(); // Starttime festhalten start_time = System.currentTimeMillis(); } /** * Liefert einen gefundenen Service zurück. zurück * * @param classname der Name der Serviceklass e * @return einen Pointer auf die Serviceklass e */ public Object getService(String classname) { return found.get(classname); } /** * Fügt eine Klasse zur Suchliste hinz u * * @param classname der Name der Klasse die gesucht werden sol l */ public void addSearchClass(String classname) { searched.addElement(classname); searched last_status.put(classname, STATUS_NEW); last_status } /** * Löscht eine Klasse aus der Suchlist e * * @param classname der Name der Klasse die entfernt werden sol l */ public void removeSearchClass(String classname) { searched.removeElement(classname); searched found.remove(classname); found last_status.remove(classname); last_status } /** * Wird aufgerufen wenn ein Lookup Service gefunden wir d * * @param event das Event */ public void discovered(DiscoveryEvent event) { System.out.println("discovered() lookup service(s) discovered"); System // neue Lookup Services holen ServiceRegistrar[] regs = event.getRegistrars(); ServiceRegistrar // nach und nach in den Vector schreiben for(int i=0; i<regs.length ;i++) for registrars.addElement(regs[i]); registrars } /** * Wird aufgerufen wenn Lookup Services verschwinde n * * @param event das Event Even */ public void discarded(DiscoveryEvent event) { System.out.println("discarded() lookup service(s) discarded"); System // verschwundene Lookup Services holen ServiceRegistrar[] regs = event.getRegistrars(); ServiceRegistrar // nach und nach aus dem Vector löschen for(int i=0; i<regs.length ;i++) for registrars.removeElement(regs[i]); registrars } /** * Überprüft ob alle Services noch laufen . * * @return true wenn alle Services noch laufe n */ public boolean checkServices() { System.out.println("checkServices() invoked"); System // alle Services durchlaufen Enumeration enum = searched.elements(); while while(enum.hasMoreElements()) { String key = (String)enum.nextElement(); AliveCheckable ac = null; try { ac = (AliveCheckable) found.get(key); } catch(ClassCastException cce) catch { continue; continue } if if(ac == null) { reportError(key); reportError return false; } try { if(!ac.isAlive()) if return false; } catch(RemoteException re) catch { return false; } } // wenn einer nicht läuft false zurückliefern return true; } /** * Versucht Services die nicht mehr laufen wieder zu finden . */ public void findServices() { System.out.println("findServices() invoked"); System if if(registrars.size() == 0) { System.out.println("findServices() no lookup services found yet"); System return; return } // alle kaputten services aus der Hashtable werfen Enumeration enum = found.keys(); while while(enum.hasMoreElements()) { String key = (String)enum.nextElement(); AliveCheckable ac = null; try { ac = (AliveCheckable) found.get(key); } catch(ClassCastException cce) catch { System.out.println("findServices() " + System found.get(key).getClass().getName() + " does not implement AliveCheckable"); continue; continue } try { System.out.println("findServices() checking if " + System ac.getClass().getName() + " is still alive..."); if(!ac.isAlive()) if found.remove(key); found } catch(RemoteException re) catch { System.out.println("findServices() " + ac.getClass().getName() System + " is dead"); found.remove(key); found } } // wieder suchen searchClasses(); searchClasses } /** * Sucht alle bekannten Lookup Services nach den verlangten Klassen a b und fügt füg * sie ggf. zu dem Vector hinzu. hinzu */ public void searchClasses() { System.out.println("searchClasses() invoked"); System // Liste aller gesuchten Klassen durchgehen Enumeration enum = searched.elements(); while(enum.hasMoreElements()) while { // Klassennamen aus dem Vektor lesen String classname = (String) enum.nextElement(); if if(found.get(classname) == null) { System.out.println("searchClasses() no " + classname + " found System yet, looking for an instance"); Class[] classes = null; Class try { Class[] tmp = {Class.forName(classname)}; Class // stupid java compiler classes = tmp; } catch(ClassNotFoundException cnfe) catch { // hm. pech System.out.println("searchClasses() ClassNotFoundException System while looking for " + classname); continue; continue } // template erstellen ServiceTemplate template = new ServiceTemplate(null, classes, null); Enumeration reg_enum = registrars.elements(); while while(reg_enum.hasMoreElements()) { System.out.println("searchClasses() checking a lookup System service"); ServiceRegistrar reg = (ServiceRegistrar) reg_enum.nextElement(); ServiceMatches matches = null; try { // alle Dienste die der Lookup Service hat und die auf das Template passen lesen // max. 100 services matches = reg.lookup(template, 100); } catch(RemoteException re) catch { // tscha. pech. } if if(matches != null) { System.out.println("searchClasses() " + System matches.totalMatches + " matches found"); // durch alle gefundenen Services laufen for(int j=0; j<matches.totalMatches; j++) for { // service auslesen ServiceItem item = matches.items[j]; if(item != null && item.service == null) if System.out.println("searchClasses() item.service == System null"); // ggf. in die found hashtable schreiben if(item != null && found.get(classname) == null && if item.service != null) { // Prüfen ob der Service lebt try { AliveCheckable ac = (AliveCheckable) item.service; if if(!ac.isAlive()) { // Hm. Tot. System.out.println("searchClasses() System item.service.isAlive() == false"); continue; continue } } catch(ClassCastException cce) catch { } catch(RemoteException re) catch { // Hm. Tot. System.out.println("searchClasses() System item.service.isAlive() threw RemoteException"); continue; continue } found.put(classname, item.service); found } } } } } } } /** * Error-Melder Error-Melde * * @param classname der Name der Klasse bei der ein Fehler besteht . */ private void reportError(String classname) { String ls = (String) last_status.get(classname); if(ls.equals(STATUS_OK) || (ls.equals(STATUS_ERROR) && if System.currentTimeMillis() - start_time > timeout)) if(!classname.equals(MAIL_SERVER)) if new MailerThread(STATUS_MAIL_RECIPIENT, "[mm-projekt] " + classname + " is null", "previous status: " + ls).start(); last_status.put(classname, STATUS_ERROR); last_status } /** * Dieser Thread wird gestartet wenn ein Fehler aufgetreten ist. E r versucht mit Hilfe * des Mailservice eine Mail an den Systemadministrator zu verschicken . * * @author Stefan Zier Zie * @version 1.0 1. */ private class MailerThread extends Thread { private String rcp_email = null; private String subject = null; private String body = null; public MailerThread(String rcp_email, String subject, String body) { this.rcp_email = rcp_email; this this.subject = subject; this this.body = body; this } public void { // * * * // * * * // * * * // * * * // * * * } run() * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * } /** * ies ist der Thread, der nach den gesuchten Klassen sucht un d überwacht, * ob die Klassen verschwunden sind. sind * * @author Stefan Zier Zie * @version 1.0 1. */ private class SearcherThread extends Thread { public SearcherThread() {} public void run() { boolean all_ok = false; while while(true) { System.out.println("SearcherThread.run() main loop"); System try { if(all_ok) if { all_ok = checkServices(); if(all_ok) if sleep(30000); sleep } else { findServices(); findServices all_ok = true; sleep(3000); sleep } } catch(InterruptedException ie) catch { } } } } } b.) mm.services.chat Dieses Paket enthält alle Klassen des Chat-Service. Klasse Chat ChatController ChatEventData ChatIn ChatMain ChatOut ChatRoom ChatServer PageParameters SessionManager Bedeutung Remote Interface des Chat-Servers Rezipient für Chat-Events im Webserver Daten für die Event-Registrierung Servlet, das Eingabezeilen vom Benutzer entgegennimmt Instantiiert einen Chat-Service und registriert ihn beim Lookup-Service Servlet, das die Ausgabe des Chats erledigt Ein Chat-Raum Die Implementation des Chat-Servers Hilfsklasse, die die Auswertung von GETund POST-Parametern vereinfacht Managed ChatSessions All Packages Class Hierarchy This Package Previous Next Index Interface mm.services.chat.Chat public abstract interface Chat extends java.rmi.Remote, mm.tools.AliveCheckable Ueber dieses Interface koennen Methoden des ChatServers aufgerufen werden. Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann addChatListener(RemoteEventListener, MarshalledObject) Interface zur EventRegistration der Clients chatRoomCreated(String) Ueberprueft, ob der angegebene Chat-Room existiert createChatRoom(String) Erzeugt einen Chat-Room deleteChatRoom(String) Loescht den angegebenen Chat-Room getLastLines(int, String) Gibt die letzten Chatzeilen zurueck. getLine(String) Gibt die letzte Chat-Line des angegebenen Chat-Rooms zurueck insertLine(String, String) Fuegt eine Chat-Line beim angegebenen Chat-Room ein insertLine public void insertLine(java.lang.String cr, java.lang.String line) throws java.rmi.RemoteException Fuegt eine Chat-Line beim angegebenen Chat-Room ein Parameters: cr - Name des Chat-Rooms line - Die Chat-Line fuer den Chat-Room Returns: void getLine public java.lang.String getLine(java.lang.String cr) throws java.rmi.RemoteException Gibt die letzte Chat-Line des angegebenen Chat-Rooms zurueck Parameters: cr - Name des Chat-Rooms Returns: Die letzte Zeile des angegebenen ChatRooms getLastLines public java.util.Vector getLastLines(int anz, java.lang.String cr) throws java.rmi.RemoteException Gibt die letzten Chatzeilen zurueck. Anzahl der Zeilen kann uebergeben werden. Parameters: anz - Anzahl der Zeilen cr - Chatroom Returns: String[] Array mit den letzten Chatzeilen createChatRoom public void createChatRoom(java.lang.String cr) throws java.rmi.RemoteException Erzeugt einen Chat-Room Parameters: cr - Name des Chat-Rooms Returns: void deleteChatRoom public void deleteChatRoom(java.lang.String cr) throws java.rmi.RemoteException Loescht den angegebenen Chat-Room Parameters: cr - Name des Chat-Rooms Returns: void chatRoomCreated public boolean chatRoomCreated(java.lang.String cr) throws java.rmi.RemoteException Ueberprueft, ob der angegebene Chat-Room existiert Parameters: cr - Name des Chat-Rooms Returns: boolean true: ChatRoom existiert, false: ChatRoom existiert nicht addChatListener public net.jini.core.event.EventRegistration addChatListener(net.jini.core.event.RemoteEve java.rmi.MarshalledObject handbackObject) throws Interface zur EventRegistration der Clients Parameters: addChatListener - Client uebergibt RemoteEventListener. An diesen Listener wird der Event geschickt. handbackObject - Object das vom Client uebergeben wird und spaeter vom Listener des Clients mitgeschickt und ausgewertet werden kann. Returns: EventRegistration All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; import java.util.Vector; import java.rmi.MarshalledObject; import java.rmi.Remote; import java.rmi.RemoteException; import net.jini.core.event.EventRegistration; import net.jini.core.event.RemoteEventListener; import net.jini.core.lease.LeaseDeniedException; import mm.tools.AliveCheckable; /** * Ueber dieses Interface koennen Methoden des ChatServer s * aufgerufen werden. werden * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n */ public interface Chat extends Remote, AliveCheckable { /** * Fuegt eine Chat-Line beim angegebenen Chat-Room ei n * * @param cr Name des Chat-Rooms Chat-Room * @param line Die Chat-Line fuer den Chat-Roo m * @return void voi */ public void insertLine(String cr, String line) throws RemoteException; /** * Gibt die letzte Chat-Line des angegebenen Chat-Rooms zuruec k * * @param cr Name des Chat-Rooms Chat-Room * @return Die letzte Zeile des angegebenen ChatRoom s */ public String getLine(String cr) throws RemoteException; /** * Gibt die letzten Chatzeilen zurueck. Anzahl der Zeile n * kann uebergeben werden. werden * @param anz Anzahl der Zeilen Zeile * @param cr Chatroo Chatroom * @return String[] Array mit den letzten <anz> Chatzeilen Chatzeile */ public Vector getLastLines(int anz,String cr) throws RemoteException; /** * Erzeugt einen Chat-Room Chat-Roo * * @param cr Name des Chat-Rooms Chat-Room * @return void voi */ public void createChatRoom(String cr) throws RemoteException; /** * Loescht den angegebenen Chat-Room Chat-Roo * * @param cr Name des Chat-Rooms Chat-Room * @return void voi */ public void deleteChatRoom(String cr) throws RemoteException; /** * Ueberprueft, ob der angegebene Chat-Room existier t * * @param cr Name des Chat-Rooms Chat-Room * @return boolean true: ChatRoom existiert, false: ChatRoom existier t nicht nich */ public boolean chatRoomCreated(String cr) throws RemoteException; /** Interface zur EventRegistration der Clients * * @param addChatListener Client uebergibt RemoteEventListener. An diese n * Listener wird der Event geschickt. geschickt * @param handbackObject Object das vom Client uebergeben wird un und spaeter vom vo * Listener des Clients mitgeschickt und ausgewertet werden kann . * @return EventRegistration EventRegistratio */ public EventRegistration addChatListener( RemoteEventListener chatListener, MarshalledObject handbackObject ) throws RemoteException, LeaseDeniedException; } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatController mm.services.chat.ChatController public class ChatController Der ChatController uebernimmt die Registrierung fuer das RemoteEvent, welches beim einfuegen einer neuen Chatzeile auftritt und schreibt diese in einen Vector. Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann ChatController(String, Chat) Konstruktor: Hier findet die Registrierung fuer den RemoteEvent via addChatListener() statt, um Notifikationen ueber neu eingefuegte Chat-Zeilen im entsprechenden Chat-Room zu erhalten. getHashtable() Die Methode getHashTable gibt die Hashtable zurueck in der die Vectoren der einzelnen Chat-Clients mittels dem key "threadCount" eindeutig abgelegt sind. incrementThreadCount() Die Methode incrementThreadCount erhoeht den "long" Wert "threadCount" um 1. notify(LeaseRenewalEvent) Die Notify-Methode befaehigt den LeaseListener ein auftretendes LeaseRenewalEvent zu verarbeiten. notify(RemoteEvent) Diese Methode wird aufgerufen sobald ein RemoteEvent auftritt, fuer welches Interesse angemeldet wurde. ChatController public ChatController(java.lang.String room, Chat ourServer) Konstruktor: Hier findet die Registrierung fuer den RemoteEvent via addChatListener() statt, um Notifikationen ueber neu eingefuegte ChatZeilen im entsprechenden Chat-Room zu erhalten. Beim instanziieren des ChatControllers wird der ChatServer und der Name des gewuenschten ChatRooms uebergeben. Parameters: String - Der Name des ChatRooms Chat - Der ChatServer incrementThreadCount public long incrementThreadCount() Die Methode incrementThreadCount erhoeht den "long" Wert "threadCount" um 1. Damit wird eine eindeutige Zuordnung zwischen threadCount und Client erreicht. getHashtable public java.util.Hashtable getHashtable() Die Methode getHashTable gibt die Hashtable zurueck in der die Vectoren der einzelnen Chat-Clients mittels dem key "threadCount" eindeutig abgelegt sind. Returns: Hashtable Die Hashtable mit den Vectoren notify public void notify(com.sun.jini.lease.LeaseRenewalEvent lostLease) Die Notify-Methode befaehigt den LeaseListener ein auftretendes LeaseRenewalEvent zu verarbeiten. Parameters: LeaseRenewalEvent - Das aufgetretene LeasRenewalEvent Returns: void notify public void notify(net.jini.core.event.RemoteEvent e) Diese Methode wird aufgerufen sobald ein RemoteEvent auftritt, fuer welches Interesse angemeldet wurde. Parameters: e - das erzeugte RemoteEvent Returns: void All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; mm import java.util.Hashtable; import java.util.Vector; import java.util.Iterator; import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStream; import import import import import java.rmi.RMISecurityManager; java.rmi.MarshalledObject; java.rmi.Remote; java.rmi.RemoteException; java.rmi.server.UnicastRemoteObject; import import import import import import import import import net.jini.core.entry.Entry; net.jini.lookup.entry.Name; net.jini.core.lookup.ServiceTemplate; net.jini.core.lookup.ServiceRegistrar; net.jini.core.discovery.LookupLocator; net.jini.core.event.RemoteEventListener; net.jini.core.event.RemoteEvent; net.jini.core.event.EventRegistration; net.jini.core.lease.LeaseDeniedException; import com.sun.jini.lease.LeaseListener; import com.sun.jini.lease.LeaseRenewalEvent; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import mm.services.chat.Chat; /** * Der ChatController uebernimmt die Registrierung fuer das RemoteEvent , * welches beim einfuegen einer neuen Chatzeile auftritt und schreibt * diese in einen Vector. Vector * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n * **/ public class ChatController implements RemoteEventListener, LeaseListener { // ChatServer private Chat chatIf; // EventRegistration beim ChatServer private EventRegistration chatReg = null; // Ist registriert "false" oder "true" private boolean isRegistered = false; // Unser ChatRoom fuer den wir Verantwortlich sind. private String chatRoom = null; // Counter fuer die Clients private long threadCount = 0; // Hashtable mit den Clients als Vector private Hashtable jspVectors = null; /** * Konstruktor: Konstruktor * Hier findet die Registrierung fuer den RemoteEvent vi a addChatListener() statt, statt * um Notifikationen ueber neu eingefuegte Chat-Zeilen im entsprechende n Chat-Room Chat-Roo * zu erhalten. erhalten * Beim instanziieren des ChatControllers wird der ChatServer und * der Name des gewuenschten ChatRooms uebergeben. uebergeben * * @param String Der Name des ChatRooms ChatRoom * @param Chat Der ChatServer ChatServe * **/ public ChatController(String room, Chat ourServer) { chatRoom = room; chatIf = ourServer; jspVectors = new Hashtable(); if (chatIf != null) { try { RemoteEventListener notifyMe = (RemoteEventListener) UnicastRemoteObject.exportObject(this); MarshalledObject handbackObject = new MarshalledObject(chatRoom); chatReg = chatIf.addChatListener(this, handbackObject); isRegistered = true; } catch (LeaseDeniedException lde) { lde.printStackTrace(); lde } catch (IOException ioe ) { ioe.printStackTrace(); ioe } } else System.exit(1); System } /** * Die Methode incrementThreadCount erhoeht den "long" Wert "threadCount " um 1. 1 * Damit wird eine eindeutige Zuordnung zwischen threadCount und Clien t erreicht. erreicht * * @ return long Gibt den um eins erhoehten Wert von threadCoun t zurueck zuruec * **/ public long incrementThreadCount() { return ++threadCount; } /** * Die Methode getHashTable gibt die Hashtable zurueck * in der die Vectoren der einzelnen Chat-Clients mittels dem ke y "threadCount" "threadCount * eindeutig abgelegt sind. sind * * @return Hashtable Die Hashtable mit den Vectoren Vectore * **/ public Hashtable getHashtable() { return jspVectors; } /** * Die Notify-Methode befaehigt den LeaseListener ein ei * auftretendes LeaseRenewalEvent zu verarbeiten. verarbeiten * * @param LeaseRenewalEvent Das aufgetretene LeasRenewalEvent LeasRenewalEven * @return void voi * **/ public void notify(LeaseRenewalEvent lostLease) { Exception deniedReason = lostLease.getException(); if (deniedReason != null ) System.out.println( "Lease verloren, weil: " + deniedReason System ); else System.out.println( "Lease verloren, keine Ursache System feststellbar" ); } /** * Diese Methode wird aufgerufen sobald ein RemoteEvent auftritt , * fuer welches Interesse angemeldet wurde. wurde * * @param e das erzeugte RemoteEvent RemoteEven * @return void voi * **/ public void notify(RemoteEvent e) { try { String line = e.getRegistrationObject().get().toString(); Iterator lineIter; synchronized (jspVectors) { lineIter = jspVectors.values().iterator(); } while (lineIter.hasNext()) { ((Vector (( Vector) lineIter.next()).add(line); ); System.out.println("Die eingefuegte ChatLine: " + line ); System } } catch (Exception re) { System.out.println("insertLine fehlgeschlagen:"); System re.printStackTrace(); re } } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatEventData java.lang.Object | +----mm.services.chat.ChatEventData public class ChatEventData extends java.lang.Object Objekte dieser Klasse enthalten alle notwendigen Daten fuer die EventRegistrierung. Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann ChatEventData(long, RemoteEventListener, MarshalledObject) Konstruktor der Klasse ChatEventData getExpiration() Rueckgabe der Lease-Dauer getHandbackObject() Rueckgabe des handbackObjects. notifyListener(long, Object, long, MarshalledObject) Erzeugung eines RemoteEvent und Aufruf der Notify-Methode des registrierten RemoteEventListeners setDuration(long) Setzen der neuen Lease-Dauer setExpiration(long) Eintragen einer neuen Lease-Dauer ChatEventData public ChatEventData(long leaseExpiration, net.jini.core.event.RemoteEventListener eventListener, java.rmi.MarshalledObject handbackObject) Konstruktor der Klasse ChatEventData Parameters: leaseExpiration - Lease-Dauer eventListener - Objekt, das sich fuer das Event registriert handbackObject - Objekt, das beim Auftreten des Events wieder zurueck gegeben wird getHandbackObject public java.rmi.MarshalledObject getHandbackObject() Rueckgabe des handbackObjects. Returns: MarshalledObject Das handbackObject getExpiration public long getExpiration() Rueckgabe der Lease-Dauer Returns: leaseExpiration Dauer der Lease setExpiration public void setExpiration(long newLeaseExpiration) Eintragen einer neuen Lease-Dauer Parameters: newLeaseExpiration - Die neue Lease-Dauer Returns: void setDuration public void setDuration(long newLeaseDuration) Setzen der neuen Lease-Dauer Parameters: newLeaseDuration Returns: void notifyListener public void notifyListener(long eventID, java.lang.Object eventSource, long eventSequenceNumber, java.rmi.MarshalledObject chatLine) throws net.jini.core.event. Erzeugung eines RemoteEvent und Aufruf der Notify-Methode des registrierten RemoteEventListeners Parameters: eventID - Die ID das aufgetretenen Events eventSource - Event-Generator eventSequenceNumber - Sequenz-Nummer des aufgetretenen Events chatLine - Das Objekt wird mit der Notify-Methode an den Listener zurueckgeliefert Returns: leaseExpiration Dauer der Lease All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; import import import import java.rmi.MarshalledObject; java.rmi.RemoteException; java.util.List; java.util.Map; import net.jini.core.event.RemoteEvent; import net.jini.core.event.RemoteEventListener; import net.jini.core.event.UnknownEventException; /** * Objekte dieser Klasse enthalten alle notwendigen Daten fue r * die Event-Registrierung. Event-Registrierung * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n * **/ public class ChatEventData { MarshalledObject handbackObject = null; RemoteEventListener eventListener = null; long leaseExpiration = 0; /** * Konstruktor der Klasse ChatEventData ChatEventDat * * @param leaseExpiration Lease-Dauer Lease-Daue * @param eventListener Objekt, das sich fuer das Event registrier t * @param handbackObject Objekt, das beim Auftreten des Events wiede r zurueck gegeben wird wir * **/ public ChatEventData(long leaseExpiration, RemoteEventListener eventListener,MarshalledObject handbackObject) { this.leaseExpiration = leaseExpiration; this this.handbackObject = handbackObject; this this.eventListener = eventListener; this } /** * Rueckgabe des handbackObjects. handbackObjects * * @return MarshalledObject Das handbackObject handbackObjec * **/ public MarshalledObject getHandbackObject() { return handbackObject; } /** * Rueckgabe der Lease-Dauer Lease-Daue * * @return leaseExpiration Dauer der Lease Leas * **/ public long getExpiration() { return leaseExpiration; } /** * Eintragen einer neuen Lease-Dauer Lease-Daue * * @param newLeaseExpiration Die neue Lease-Dauer Lease-Daue * @return void voi * **/ public void setExpiration(long newLeaseExpiration) { leaseExpiration = newLeaseExpiration; } /** * Setzen der neuen Lease-Dauer Lease-Daue * * @param newLeaseDuration newLeaseDuratio * @return void voi * **/ public void setDuration(long newLeaseDuration) { setExpiration(newLeaseDuration + System.currentTimeMillis()); setExpiration } /** * Erzeugung eines RemoteEvent und Aufruf der Notify-Methode des * registrierten RemoteEventListeners RemoteEventListener * * @param eventID Die ID das aufgetretenen Event s * @param eventSource Event-Generator Event-Generato * @param eventSequenceNumber Sequenz-Nummer des aufgetretenen Events Event * @param chatLine Das Objekt wird mit der Notify-Methode an den Listene r zurueckgeliefert zurueckgeliefer * @return leaseExpiration Dauer der Lease Leas * **/ public void notifyListener(long eventID, Object eventSource, long eventSequenceNumber,MarshalledObject chatLine) throws UnknownEventException, RemoteException { RemoteEvent eventData = new RemoteEvent(eventSource, eventID, eventSequenceNumber, chatLine); eventListener.notify(eventData); eventListener } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatIn java.lang.Object | +----mm.webserver.jsp.JspSuperClass | +----mm.services.chat.ChatIn public class ChatIn extends mm.webserver.jsp.JspSuperClass Das ChatIn-Servlet dient zur Eingabe der Chatzeilen, welche an den ChatServer gesendet werden. Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann ChatIn() _jspService(HttpServletRequest, HttpServletResponse) In _jspService() wird ueber das HttpServletResponse-Objekt HTMLCode an den Client gesendet und die Chat-Zeile (mit Farbe und NickName) via insertLine() an den Server geschickt. jspInit() In jspInit() wird der Chat-Server mit Hilfe des Service-Controllers gesucht und ein Chat-Room erzeugt. ChatIn public ChatIn() jspInit public void jspInit() In jspInit() wird der Chat-Server mit Hilfe des Service-Controllers gesucht und ein Chat-Room erzeugt. Returns: void Overrides: jspInit in class mm.webserver.jsp.JspSuperClass _jspService public void _jspService(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse res) In _jspService() wird ueber das HttpServletResponse-Objekt HTMLCode an den Client gesendet und die Chat-Zeile (mit Farbe und Nick-Name) via insertLine() an den Server geschickt. Parameters: req - Das HttpServletRequest-Objekt res - Das HttpServletResponse-Objekt Returns: void Overrides: _jspService in class mm.webserver.jsp.JspSuperClass All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; mm import java.util.Hashtable; import java.util.Vector; import java.util.Iterator; import import import import java.io.IOException; java.io.PrintWriter; java.io.OutputStream; java.io.BufferedReader; import java.rmi.RemoteException; import import import import import import import import import javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; javax.servlet.ServletException; javax.servlet.ServletContext; javax.servlet.ServletInputStream; javax.servlet.http.HttpUtils; javax.servlet.RequestDispatcher; javax.servlet.jsp.JspPage; import mm.webserver.jsp.JspSuperClass; import mm.jini.ServiceController; /** * Das ChatIn-Servlet dient zur Eingabe der Chatzeilen , * welche an den ChatServer gesendet werden . * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n */ public class ChatIn extends JspSuperClass { Chat chatServer chatServer=null; /** * In jspInit() wird der Chat-Server mit Hilfe des Service-Controller s * gesucht und ein Chat-Room erzeugt. erzeugt * * @return void voi */ public void jspInit() { chatServer = (Chat) service_controller.getService(CHAT_SERVER); int i = 0; while (chatServer==null && i<10) { chatServer = (Chat) service_controller.getService(CHAT_SERVER); i++; try { Thread.currentThread().sleep(500); Thread } catch (InterruptedException ie) { ie.printStackTrace(); ie } } if (chatServer!=null) { try { chatServer.createChatRoom("room2"); chatServer } catch (RemoteException re) { re.printStackTrace(); re } } } /** * In _jspService() wird ueber das HttpServletResponse-Objekt HTML-Code a n den Client gesendet gesende * und die Chat-Zeile (mit Farbe und Nick-Name) via insertLine() an de n Server geschickt. geschickt * * @param req Das HttpServletRequest-Objekt HttpServletRequest-Objek * @param res Das HttpServletResponse-Objekt HttpServletResponse-Objek * @return void voi */ public void _jspService(HttpServletRequest req, HttpServletResponse res) { HttpSession session = null; PrintWriter out = null; Hashtable old_data = new Hashtable(); session = req.getSession(false); try { out = res.getWriter(); } catch (IOException ioe) { ioe.printStackTrace(); ioe } res.setContentType("text/html"); res out.println("<html><head><title>ChatEingabe</title><head><body out bgcolor=\"#FFFFFF\">"); out.println("<font color=\"#000000\" face=\"trebuchet ms, verdana, out arial\" size=\"+2\"><b>Chat-Zeilen:</b></font><p>"); out.println("<form name=\"inputLine\" out action=\"http: ://motion.ghb.fh-furtwangen.de:8001/servlet/ChatIn\" method=\"post\">"); out.println("<input type=\"text\" name=\"chatLine\" size=\"60\" out maxlength=\"500\">"); out.println("<input type=\"submit\">"); out out.println("</form></body></html>"); out out.flush(); out PageParameters params = new PageParameters(req); String chatLine = params.getString("chatLine"); if (chatLine.length() != 0) { if (chatServer!=null) { try{ try if (session==null){ String insertString = new String("<font color=\"#000000\"> guest > " + chatLine); chatServer.insertLine("room2",insertString); chatServer } else { String chatColor = (String) session.getValue("Farbe"); String chatName = (String) session.getValue("NickName"); String color = null; if( chatColor.equals("rot") ){ if color = new String("<font color=\"#FF0000\">"); } else if(chatColor.equals("blau")) { color = new String("<font color=\"#0000FF\">"); } else if(chatColor.equals("gruen")) { color = new String("<font color=\"#00FF00\">"); } else if(chatColor.equals("schwarz")) { color = new String("<font color=\"#000000\">"); } String insertString= new String(color + chatName +" > " + chatLine); chatServer.insertLine("room2",insertString); chatServer } } catch (RemoteException re) { re.printStackTrace(); re } } } } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatMain mm.services.chat.ChatMain public class ChatMain Die ChatMain-Klasse dient dazu den Chat-Server zu starten und an dem Jini-LookUp-Service mittels eines JoinManagers zu registrieren. Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann ChatMain() main(String[]) serviceIDNotify(ServiceID) Bei erfolgreicher Registrierung des Chat-Servers erhaelt dieser eine eindeutige ServiceID ChatMain public ChatMain() main public static void main(java.lang.String args[]) throws java.lang.Exception serviceIDNotify public void serviceIDNotify(net.jini.core.lookup.ServiceID id) Bei erfolgreicher Registrierung des Chat-Servers erhaelt dieser eine eindeutige ServiceID Parameters: id - Die ServiceID des registrierten Chat-Servers Returns: void All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.util.Hashtable; import net.jini.core.entry.Entry; import net.jini.core.lookup.ServiceID; import net.jini.lookup.entry.Name; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lookup.JoinManager; import com.sun.jini.lease.LeaseRenewalManager; /** * Die ChatMain-Klasse dient dazu den Chat-Server zu starte n * und an dem Jini-LookUp-Service mittels eines JoinManagers z u registrieren. registrieren * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n * **/ public class ChatMain implements ServiceIDListener { private ServiceID chatID; chatID public ChatMain() {} public static void main(String[] args) throws Exception { System System.setSecurityManager (new RMISecurityManager ()); ChatServer testServer = new ChatServer(); Entry Entry[] attribs = new Entry[1]; attribs[0]= new Name("ChatServer"); attribs JoinManager testManager = new JoinManager ( testServer, testServer attribs, attribs new ChatMain(), new LeaseRenewalManager() ); System.out.println ("ChatServer ist jetzt bereit."); System new Thread(testServer).start(); } /** * Bei erfolgreicher Registrierung des Chat-Servers Chat-Server * erhaelt dieser eine eindeutige ServiceID ServiceI * * @param id Die ServiceID des registrierten Chat-Server s * @return void voi * **/ public void serviceIDNotify(ServiceID id) { chatID=id; chatID System.out.println("ChatserverID: " + chatID); System } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatOut java.lang.Object | +----mm.webserver.jsp.JspSuperClass | +----mm.services.chat.ChatOut public class ChatOut extends mm.webserver.jsp.JspSuperClass Das ChatOut-Servlet dient der Anzeige aller Chat-Zeilen fuer die Clients Version: 1.0 Author: Oliver Hilss und Mirko Tochtermann ChatOut() _jspService(HttpServletRequest, HttpServletResponse) In _jspService() werden zunaechst eventuell bereits existierende ChatZeilen an den Client gesendet. jspInit() In jspInit() wird der Chat-Server mit Hilfe des Service-Controllers gesucht und ein ChatController fuer den entsprechenden Chat-Room und Chat-Server erzeugt. ChatOut public ChatOut() jspInit public void jspInit() In jspInit() wird der Chat-Server mit Hilfe des Service-Controllers gesucht und ein ChatController fuer den entsprechenden Chat-Room und Chat-Server erzeugt. Returns: void Overrides: jspInit in class mm.webserver.jsp.JspSuperClass _jspService public void _jspService(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse res) In _jspService() werden zunaechst eventuell bereits existierende Chat-Zeilen an den Client gesendet. Durch eine Endlos-Schleife wird garantiert, dass der HTTP-Stream "offen" bleibt und somit staendig neue Chat-Zeilen eingefuegt werden koennen. Eine Java-Skript-Funktion sorgt dafuer, dass das Fenster stets heruntergescrollt wird, wenn neue Zeilen eintreffen. Returns: void Overrides: _jspService in class mm.webserver.jsp.JspSuperClass All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; mm import java.util.Hashtable; import java.util.Vector; import java.util.Iterator; import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStream; import import import import import java.rmi.RMISecurityManager; java.rmi.MarshalledObject; java.rmi.Remote; java.rmi.RemoteException; java.rmi.server.UnicastRemoteObject; import import import import import import import import import net.jini.core.entry.Entry; net.jini.lookup.entry.Name; net.jini.core.lookup.ServiceTemplate; net.jini.core.lookup.ServiceRegistrar; net.jini.core.discovery.LookupLocator; net.jini.core.event.RemoteEventListener; net.jini.core.event.RemoteEvent; net.jini.core.event.EventRegistration; net.jini.core.lease.LeaseDeniedException; import com.sun.jini.lease.LeaseListener; import com.sun.jini.lease.LeaseRenewalEvent; import import import import import javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; javax.servlet.ServletException; javax.servlet.ServletContext; import mm.webserver.jsp.JspSuperClass; /** * Das ChatOut-Servlet dient der Anzeige aller Chat-Zeilen fue r * die Clients Client * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochterman n * **/ public class ChatOut extends JspSuperClass { Chat chatServer chatServer; ChatController cc = null; int i = 0; /** * In jspInit() wird der Chat-Server mit Hilfe des Service-Controller s * gesucht und ein ChatController fuer den entsprechenden Chat-Roo m * und Chat-Server erzeugt. erzeugt * * @return void voi **/ public void jspInit() { while (chatServer==null && i<10) { chatServer = (Chat) service_controller.getService(CHAT_SERVER); try { Thread.currentThread().sleep(500); Thread i++; } catch (InterruptedException ie) { ie.printStackTrace(); ie } } cc = (ChatController) context.getAttribute("chatController.room2"); if (cc == null) { synchronized(this) { synchronized cc = new ChatController("room2", chatServer); context.setAttribute("chatController.room2", cc); context } } } /** * In _jspService() werden zunaechst eventuell bereits existierend e Chat-Zeilen an a * den Client gesendet. gesendet * Durch eine Endlos-Schleife wird garantiert, dass der HTTP-Strea m "offen" bleibt bleib * und somit staendig neue Chat-Zeilen eingefuegt werden koennen. * Eine Java-Skript-Funktion sorgt dafuer, dass das Fenster stet s heruntergescrollt wird, wird * wenn neue Zeilen eintreffen. eintreffen * * @return void voi * **/ public void _jspService(HttpServletRequest req, HttpServletResponse res) { Hashtable hash = null; Long user = null; Vector ourVector = new Vector(); PrintWriter out = null; Vector buffer = null; hash = (Hashtable) cc.getHashtable(); user = new Long(cc.incrementThreadCount()); hash.put(user,ourVector); hash try { out = res.getWriter(); } catch (IOException ioe) { ioe.printStackTrace(); ioe } res.setContentType("text/html"); res try { buffer = chatServer.getLastLines(20,"room2"); } catch (RemoteException re) { re.printStackTrace(); re } out.println("<html><head><title>MM Chat</title><script><!--\n"); out out out.println("function doScroll(){timer=setTimeout(\"doScroll()\", 50);window.scrollBy(0,8);window.scrollBy(0,4);window.scrollBy(0,2);window.s crollBy(0,1);}\n"); out.println("doScroll();\n"); out out.println("//--></script>\n"); out out.println("</head><body bgcolor=\"#FFFFFF\">"); out out.println("<font color=\"#000000\" face=\"trebuchet ms, verdana, out arial\" size=\"+2\"><b>Chat-Zeilen:</b></font><p>"); out.flush(); out // Ausgeben bereits existierender Chat-Zeilen if (buffer != null) { for (int k=0;k < buffer.size();k++) { out.println(buffer.elementAt(k) + "<br>"+ out } } out.flush(); out "\n"); while (true) { try { Thread.currentThread().sleep(100); Thread } catch (InterruptedException ie) { ie.printStackTrace(); ie } if (ourVector.isEmpty() == false) { for (int y=0;y < ourVector.size();y++) { out.println( (String) ourVector.elementAt(y)+ "<br>" + "\n" out ); out.flush(); out } } else { out.println(" "); out } ourVector.removeAllElements(); ourVector out.flush(); out } } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatRoom java.lang.Object | +----mm.services.chat.ChatRoom public class ChatRoom extends java.lang.Object Diese Klasse implementiert einen oeffentlichen ChatRoom oder einen Gruppen-ChatRoom. Version: 1.0 Author: Mirko Tochtermann und Oliver Hilss ChatRoom(String) Konstruktor gibt dem Chat-Room seinen Namen getChatLine() Gibt die letzte Chat-Line des eigenen Chat-Rooms zurueck getChatLines(int) Gibt die letzen Chatzeilen aus. insertChatLine(String) Setzt eine Chat-Line in den eigenen Chat-Room ChatRoom public ChatRoom(java.lang.String name) Konstruktor gibt dem Chat-Room seinen Namen Parameters: name - Name des Chat-Rooms insertChatLine public void insertChatLine(java.lang.String line) Setzt eine Chat-Line in den eigenen Chat-Room Parameters: line - Die einzusetzende Chat-Line Returns: void getChatLine public java.lang.String getChatLine() Gibt die letzte Chat-Line des eigenen Chat-Rooms zurueck Returns: Die letzte Zeile im Chat-Room getChatLines public java.util.Vector getChatLines(int anz) Gibt die letzen Chatzeilen aus. Parameters: anz - Anzahl der auszugebenden Chatzeilen Returns: String[] Array der letzten Chatzeilen All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; import java.util.Vector; import java.util.Date; /** * Diese Klasse implementiert einen oeffentlichen ChatRoo m * oder einen Gruppen-ChatRoom. Gruppen-ChatRoom * * @version 1.0 1. * @author Mirko Tochtermann und Oliver Hils s * **/ public class ChatRoom { private String name = null; private Vector lines = null; private Vector time = null; /** * Konstruktor gibt dem Chat-Room seinen Namen Name * * @param name Name des Chat-Rooms Chat-Room * **/ public ChatRoom(String name) { lines = new Vector(); time = new Vector(); this.name=name; this new Cleanup().start(); } /** * Setzt eine Chat-Line in den eigenen Chat-Roo m * * @param line Die einzusetzende Chat-Line Chat-Lin * @return void voi * **/ public synchronized void insertChatLine(String line) { time.add(new Date()); time lines.add(line); lines } /** * Gibt die letzte Chat-Line des eigenen Chat-Rooms zuruec k * * @return Die letzte Zeile im Chat-Room Chat-Roo * **/ public String getChatLine() { if (!lines.isEmpty()) return (String) lines.lastElement(); return null; ; } /** * Gibt die letzen <anz> Chatzeilen aus. aus * * @param anz Anzahl der auszugebenden Chatzeilen Chatzeile * @return String[] Array der letzten Chatzeile n * **/ public Vector getChatLines(int anz) { int size = 0; Vector buffer=new Vector(); if ((size = lines.size()) != 0) { if (anz >= size) { for (int i = 0; i < size-1; i++) buffer.add((String) lines.elementAt(i)); buffer } else { for (int k = size - anz; k <= size -1; k++) buffer.add((String) lines.elementAt(k)); buffer } return buffer; } else return null; } /** * Gibt den Namen des ChatRooms zurueck zuruec * * @return name Name des ChatRooms ChatRoom * **/ private String getName() { return this.name; } /** * Innere Klasse, welche die alten Chat-Lines Chat-Line * alle 10 Minuten aus dem Vector loescht . * * @version 1.0 1. * @author Oliver Hilss Hils * **/ private class Cleanup extends Thread { public Cleanup() { super(); super } public void run() { while (true) { try { sleep(1000*60*10); sleep ); // alle 10 Mins } catch (InterruptedException ie) { ie.printStackTrace(); ie } synchronized (lines) { if (lines.size() == time.size() && lines.size() > 0) { try { long difference = getDifference(); while (difference > 1000*60*10) { lines.removeElementAt(0); lines time.removeElementAt(0); time System.out.println("Eine Chat-Zeile System geloescht...."); difference = getDifference(); } } catch (Exception e) { e.printStackTrace(); } } } } } private long getDifference() { long result = 0; if (time.size() > 0) { result = System.currentTimeMillis() - ((Date) time.firstElement()).getTime(); return result; } else return result; } } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.ChatServer mm.services.chat.ChatServer public class ChatServer implements Chat, java.lang.Runnable Diese Klasse implementiert einen ChatServer, der die verschiedenen ChatRooms managt anlegt und zerstoert. Version: 0.1 Author: Mirko und Oliver ChatServer() addChatListener(RemoteEventListener, MarshalledObject) Registriert einen Client als RemoteEventListener beim Chatserver cancel(Object) Landlord Methoden cancelAll(Object[]) chatRoomCreated(String) createChatRoom(String) Erzeugt einen Chat-Room deleteChatRoom(String) Loescht den angegebenen Chat-Room getLastLines(int, String) Gibt die letzten Chatzeilen zurueck. getLine(String) Gibt die letzte Chat-Line des angegebenen Chat-Rooms zurueck insertLine(String, String) Fuegt eine Chat-Line beim angegebenen Chat-Room ein isAlive() Hilfsmethoden renew(Object, long) renewAll(Object[], long[]) run() Methoden fuer den Thread ChatServer public ChatServer() throws java.rmi.RemoteException addChatListener public net.jini.core.event.EventRegistration addChatListener(net.jini.core.event.RemoteEve java.rmi.MarshalledObject handbackObject) throws Registriert einen Client als RemoteEventListener beim Chatserver Parameters: addChatListener - Client uebergibt RemoteEventListener. An diesen Listener wird der Event geschickt. handbackObject - Object das vom Client uebergeben wird und spaeter vom Listener des Clients mitgeschickt und ausgewertet werden kann. Returns: EventRegistration insertLine public void insertLine(java.lang.String cr, java.lang.String line) Fuegt eine Chat-Line beim angegebenen Chat-Room ein Parameters: cr - Name des Chat-Rooms line - Die Chat-Line fuer den Chat-Room p - Der User von dem die Zeile stammt Returns: void getLine public java.lang.String getLine(java.lang.String cr) Gibt die letzte Chat-Line des angegebenen Chat-Rooms zurueck Parameters: cr - Name des Chat-Rooms Returns: Die letzte Zeile des angegebenen ChatRooms getLastLines public java.lang.String[] getLastLines(int anz, java.lang.String cr) Gibt die letzten Chatzeilen zurueck. Anzahl der Zeilen kann uebergeben werden. Parameters: anz - Anzahl der Zeilen cr - Chatroom Returns: String[] Array mit den letzten Chatzeilen createChatRoom public void createChatRoom(java.lang.String cr) Erzeugt einen Chat-Room Parameters: cr - Name des Chat-Rooms Returns: void deleteChatRoom public void deleteChatRoom(java.lang.String cr) Loescht den angegebenen Chat-Room Parameters: cr - Name des Chat-Rooms Returns: void chatRoomCreated public boolean chatRoomCreated(java.lang.String cr) cancel public void cancel(java.lang.Object cookie) Landlord Methoden cancelAll public void cancelAll(java.lang.Object cookie[]) renew public long renew(java.lang.Object cookie, long extension) throws net.jini.core.lease.LeaseDeniedException renewAll public com.sun.jini.lease.landlord.Landlord.RenewResults renewAll(java.lang.Object cookie[ long extension[]) run public void run() Methoden fuer den Thread isAlive public boolean isAlive() Hilfsmethoden All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; import mm.services.chat.ChatRoom; import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import import import import import import import com.sun.jini.lookup.JoinManager; com.sun.jini.lease.LeaseRenewalManager; com.sun.jini.lease.landlord.Landlord; com.sun.jini.lease.landlord.LandlordLease.Factory; com.sun.jini.lease.landlord.LandlordLease; com.sun.jini.lease.landlord.LandlordLeaseFactory; com.sun.jini.lease.landlord.LeasedResource; import import import import import import import net.jini.core.event.EventRegistration; net.jini.core.event.RemoteEventListener; net.jini.core.event.UnknownEventException; net.jini.core.lease.Lease; net.jini.core.lease.LeaseDeniedException; net.jini.core.entry.Entry; net.jini.lookup.entry.Name; import import import import import import java.util.Hashtable; java.util.Iterator; java.util.List; java.util.Map; java.util.HashMap; java.util.Vector; /** * Diese Klasse implementiert einen ChatServer, der di e * verschiedenen ChatRooms managed, anlegt und zerstoert . * * @version 1.0 1. * @author Oliver Hilss und Mirko Tochtermann * **/ public class ChatServer extends UnicastRemoteObject implements Chat, Landlord, Runnable { Hashtable chatRooms; private static final long CHAT_ID = 0; private long insertEventSequence = 0; private final long maxLeaseDuration = 1000 * 60 * 120; ; //120 Minuten private int leasesCreated = 0; LandlordLeaseFactory leaseFactory; HashMap activeLeases; public ChatServer() throws RemoteException { chatRooms = new Hashtable(); leaseFactory = new LandlordLease.Factory(); activeLeases = new HashMap(); } /** * Registriert einen Client als RemoteEventListener beim Chatserver * * @param addChatListener Client uebergibt RemoteEventListener. An diese n * Listener wird der Event geschickt. geschickt * @param handbackObject Object das vom Client uebergeben wird und un spaeter vom vo * Listener des Clients mitgeschickt und ausgewertet werden kann . * @return chatReg Objekt von EventRegistration EventRegistratio * **/ public EventRegistration addChatListener(RemoteEventListener chatListener, MarshalledObject handbackObject) throws RemoteException, LeaseDeniedException { System.out.println ("Chat-Listener registrieren"); System // Neues Lease-Cookie mit laufender Integer-Nummer erstellen Object cookie = newLeaseCookie(); long leaseExpiration = maxLeaseDuration + now(); Lease eventLease = leaseFactory.newLease(cookie, this, leaseExpiration); // Event-Registration mit EventID, EventSource, Lease, EventSequenz EventRegistration chatReg = new EventRegistration(CHAT_ID, this, eventLease, insertEventSequence); // Event-Daten in eventData ablegen ChatEventData eventData = new ChatEventData(leaseExpiration, chatListener, handbackObject); // Event-Daten in der HashMap ablegen // mit cookie als Key und eventData als Value activeLeases.put(cookie, eventData); activeLeases printMessage("Registrierung beendet"); printMessage return chatReg; } /** * Informiert alle registrierten RemoteEventListener * ueber einen aufgetretenen insertEvent. insertEvent * * @param MarshalledObject Die Chat-Zeile, welche die Clients ausgebe n sollen solle * @param String String in dem der ChatRoom steht, der den Even t ausloest ausloes * @return void voi * **/ private void notifyChatListener(MarshalledObject chatLine, String cr) { long now = now(); // Alle registrierten Listener in in einem Iterator ablegen... Iterator listeners = activeLeases.values().iterator(); // ...und der Reihe nach ueber das Event informieren, // falls die Lease noch nicht abgelaufen ist while (listeners.hasNext()) { ChatEventData eventInfo = (ChatEventData) listeners.next(); try { printMessage("Send notify"); printMessage if (eventInfo.getExpiration() > now) { if (eventInfo.getHandbackObject().get().toString().equals(cr)) { printMessage("Marshalled printMessage Object: " + eventInfo.getHandbackObject().get().toString()); eventInfo eventInfo.notifyListener(CHAT_ID, this, insertEventSequence, chatLine); printMessage("Send notify for : " + cr); printMessage } } } catch (UnknownEventException doesntKnow) { printMessage("UnknownEventException" + printMessage doesntKnow.getMessage()); } catch (RemoteException rmiProblem) { printMessage("RemoteException" + rmiProblem.getMessage() printMessage ); } catch (Exception e) { e.printStackTrace(); e } } } /** * Fuegt eine Chat-Line beim angegebenen Chat-Room ei n * * @param cr Name des Chat-Rooms Chat-Room * @param line Die Chat-Line fuer den Chat-Roo m * @param p Der User von dem die Zeile stammt stamm * @return void voi * **/ public void insertLine(String cr, String line) { insertEventSequence insertEventSequence++; ChatRoom c; c c=(ChatRoom) chatRooms.get(cr); c c.insertChatLine("> "+line); printMessage printMessage("Line eingefuegt"); try { notifyChatListener(new MarshalledObject(line), cr); notifyChatListener } catch (java.io.IOException ex) { ex.printStackTrace(); ex } } /** * Gibt die letzte Chat-Line des angegebenen Chat-Rooms zuruec k * * @param cr Name des Chat-Rooms Chat-Room * @return Die letzte Zeile des angegebenen ChatRoom s * **/ public String getLine(String cr) { ChatRoom c; c c=(ChatRoom)chatRooms.get(cr); if (c != null) return c.getChatLine(); return "ChatRoom is empty or non-existent..."; } /** * Gibt die letzten Chatzeilen zurueck. Anzahl der Zeile n * kann uebergeben werden. werden * * @param anz Anzahl der Zeilen Zeile * @param cr Chatroo Chatroom * @return Vector Vector mit den <anz> Chat-Zeilen Chat-Zeile * **/ public Vector getLastLines(int anz, String cr) { ChatRoom c=(ChatRoom)chatRooms.get(cr); if (c != null) return c.getChatLines(anz); return null; null } /** * Erzeugt einen Chat-Room Chat-Roo * * @param cr Name des Chat-Rooms Chat-Room * @return void voi * **/ public void createChatRoom(String cr) { if (cr==null) return; return if (!chatRooms.containsKey(cr)) { System.out.println("Chatroom " + cr +" created"); System chatRooms.put(cr,new ChatRoom(cr)); chatRooms } } /** * Loescht den angegebenen Chat-Room Chat-Roo * * @param cr Name des Chat-Rooms Chat-Room * @return void voi * **/ public void deleteChatRoom(String cr) { if (cr==null) return; return Object obj = chatRooms.get(cr); All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.PageParameters java.lang.Object | +----mm.services.chat.PageParameters public class PageParameters extends java.lang.Object This is a tool class that simplifies the otherwise painful handling of POST and GET parameters in the servlet API. Version: 1.0 Author: Stefan Zier PageParameters(HttpServletRequest) Constructor. asHashtable() Creates an Hashtable that coitains key/value pairs of the parameters. count(String) Counts how many times the parameter has been passed. exists(String) Checks whether a value exists or not. getNumber(String) Reads a value as a Number. getNumber(String, int) Reads a parameter as a Number. getString(String) Reads a parameter's value as a String. getString(String, int) Reads a parameters value as String. PageParameters public PageParameters(javax.servlet.http.HttpServletRequest request) Constructor. Parameters: request - the HttpServletRequest that came with the service() call of the servlet exists public boolean exists(java.lang.String name) Checks whether a value exists or not. Parameters: name - the name of the value getString public java.lang.String getString(java.lang.String name, int number) Reads a parameters value as String. This is the more complex method. It only has to be used when we expect multiple parameters with the same name to come in. Parameters: name - the name of the parameter number - the position of the parameter (0...n-1) Returns: the value of the parameter or an empty String if it doesn't exist getString public java.lang.String getString(java.lang.String name) Reads a parameter's value as a String. Parameters: name - the name of the parameter Returns: the parameter's value as a String of an empty String if the parameter does not exist getNumber public int getNumber(java.lang.String name, int number) Reads a parameter as a Number. This is the more complex method. It only has to be used when we expect multiple parameters with the same name to come in. Parameters: name - the name of the parameter number - the position of the parameter (0..n-1) Returns: the paramters value as a int or -1 if the parameter does not exist getNumber public int getNumber(java.lang.String name) Reads a value as a Number. Parameters: name - the name of the parameter Returns: the value as a String or -1 if the parameter does not exist count public int count(java.lang.String name) Counts how many times the parameter has been passed. Parameters: name - the name of the parameter Returns: the number of occurences asHashtable public java.util.Hashtable asHashtable() Creates an Hashtable that coitains key/value pairs of the parameters. Returns: the Hashtable with all values All Packages Class Hierarchy This Package Previous Next Index if (obj != null) { System.out.println("Chatroom " + cr +" removed"); System chatRooms.remove(cr); chatRooms } } /** * Erzeugt einen Chat-Roo Chat-Room * * @param cr Name des Chat-Rooms Chat-Room * @return void voi * **/ public boolean chatRoomCreated(String cr) { if (cr == null) return false; if (chatRooms.containsKey(cr)) )) return true; return false; ; } /** * Löscht ein Lease aus unserer HashMap. * * @param cookie Cookie Object unseres Leases Lease * @return void voi * **/ public void cancel(java.lang.Object cookie) { printMessage( "Cancel wurde aufgerufen" ); printMessage if ( !activeLeases.containsKey( cookie ) ) return; return synchronized(activeLeases) { activeLeases.remove( cookie ); activeLeases } } /** * Löscht alle Leases aus unserer HashMap HashMa * * @param cookie Array aus unseren Cookies Cookie * @return void voi * **/ public void cancelAll(java.lang.Object[] cookie) { for ( int k = 0; k < cookie.length; k++ ) cancel(cookie[k]); cancel } /** * Erneuert die Lease-Time eines Objects. Objects * * @param cookie Cookie zu unserem Lease Leas * @param extension Zeitdauer um die der Lease verlängert wird wir * @return long Neue Zeitdauer des Leaes Leae * **/ public long renew(Object cookie,long extension) throws LeaseDeniedException { printMessage( "Renew called " + extension ); printMessage synchronized (activeLeases) { if (!activeLeases.containsKey(cookie)) throw new LeaseDeniedException("Requested resourse does not exist"); long newDuration = Math.min(extension, maxLeaseDuration); ChatEventData eventLease = (ChatEventData) activeLeases.get(cookie); eventLease.setDuration(newDuration); eventLease return newDuration; } } /** * Erneuert alle Lease-Times unserer Objekte. Objekte * * @param cookie Cookie-Array zu unserem Leases Lease * @param extension Array aus der Zeitdauer um die jeder Leas e verlängert wird wir * @return long Neue Zeitdauer des Leaes Leae * **/ public Landlord.RenewResults renewAll(java.lang.Object[] cookie,long[] extension) { long renewalGranted[] = new long[ cookie.length ]; Exception renewalDenied[] = new Exception[ cookie.length ]; for ( int k = 0; k < cookie.length; k++ ) { try { renewalGranted[k] = renew(cookie[k], extension[k]); renewalGranted renewalDenied[k] = null; renewalDenied } catch (LeaseDeniedException deny ) { renewalGranted[k] = -1; renewalGranted renewalDenied[k] = deny; renewalDenied } } return new Landlord.RenewResults(renewalGranted, renewalDenied ); } /** * Run-Methode fuer den Thread, welcher nach der Haelft e * von maxLeaseDuration alle Leases checkt check * */ public void run() run { try { while (true) { Thread.sleep( maxLeaseDuration/2 ); Thread printMessage ("CheckAllLeases"); checkAllLeases(); checkAllLeases } } catch (InterruptedException ie) { ie.printStackTrace(); ie } } /** * Dient zum Check aller vergebenen Leases. Leases * Die HashMap "activeLeases" wird dabei synchronisiert. synchronisiert * * @return void voi * **/ private void checkAllLeases() { synchronized ( activeLeases ) { Iterator counterKeys = activeLeases.keySet().iterator(); while (counterKeys.hasNext()) { Object key = counterKeys.next(); if (hasLeaseExpired(key)) { counterKeys.remove(); counterKeys printMessage( "lease expired " + key ); printMessage } } } } /** * Dient zum Test, ob eine Lease bereits abgelaufen is t * * @param cookie Cookie, welches als Schluessel fuer die HashMa p "activeLeases" dient dien * @return boolean true, falls Lease abgelaufen; false, falls Lease noc h besteht besteh * **/ private boolean hasLeaseExpired(Object cookie) { ChatEventData aCounter = (ChatEventData) activeLeases.get(cookie); long expiration = aCounter.getExpiration(); printMessage("lease check " + cookie printMessage (expiration - now()) ); if ( now() >= expiration ) return true; else return false; } + " duration " + // Hilfsmethoden /** * Diese Methode dient dazu den Zustand des Servlet s * zu checken (AliveCheck). (AliveCheck) * * @return boolean Gibt true zurück wenn das Servlet noch reagier t * **/ public boolean isAlive() { return true; } /** * Erzeugt ein neues Lease-Cooki Lease-Cookie * * @return Integer Unser neues Lease-Cookie Lease-Cooki * **/ private Integer newLeaseCookie() { return new Integer(leasesCreated++); } /** * Stellt die momentane Zeit in Millisekunden fes t * * @return long Die momentane Zeit in Millisekunden Millisekunde * **/ private long now() { return System.currentTimeMillis(); } /** * Schreibt einen String nach Standard Ou Out * * @param message Der Message-String der nach Standard Ou Out geschrieben werden soll sol * **/ private void printMessage(String message) { System.out.println(toString()+ " " + message); System } } package mm.services.chat; // Servlet API imports import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpUtils; import javax.servlet.ServletInputStream; // Core Java imports import java.util.Enumeration; import java.util.Hashtable; import java.io.IOException; /** * This is a tool class that simplifies the otherwise painful handling o f POST and GET parameters parameter * in the servlet API. * * @author Stefan Zier Zie * @version 1.0 1. */ public class PageParameters { // this is where we store the parsed values protected Hashtable parsed_values = null; // we implement "lazy parsing" meaning we parse on demand protected boolean parsed = false; // this is where we store the servlet request - needed for lazy parsing protected HttpServletRequest request = null; /** * Constructor. Constructor * * @param request the HttpServletRequest that came with the service( ) call of the servlet servle */ public PageParameters(HttpServletRequest request) { // for now we only store the HttpServletRequest this.request = request; this } /** * This method does the actual parsing - when we need it. + */ protected void parse() { // get the querystring and parse it String querystring = request.getQueryString(); Hashtable qs_parsed = null; if(querystring != null) if qs_parsed = HttpUtils.parseQueryString(querystring); // get the POSTed data and parse it ServletInputStream data = null; try { data = request.getInputStream(); } catch(IOException ioe) catch { } Hashtable post_parsed = null; if if(data != null && (request.getContentLength() != -1)) post_parsed = HttpUtils.parsePostData(request.getContentLength(), data); // now try to merge both if(qs_parsed == null && post_parsed == null) if parsed_values = new Hashtable(); else if(qs_parsed == null && post_parsed != null) parsed_values = post_parsed; else if(qs_parsed != null && post_parsed == null) parsed_values = qs_parsed; else { // well, now we really have to merge. Shouldn't be much of a Problem though. Enumeration enum = qs_parsed.keys(); while(enum.hasMoreElements()) while { String key = (String)enum.nextElement(); if(post_parsed.get(key) == null) if post_parsed.put(key, qs_parsed.get(key)); post_parsed else { // jeeze. The value existed already in the posted data, let's merge both arrays String[] qs_values = (String[]) qs_parsed.get(key); String String[] post_values = (String[]) post_parsed.get(key); String // create our new array String[] merged_values = new String[qs_values.length + String post_values.length]; // insert the query string values for(int i=0; i < qs_values.length; i++) for merged_values[i] = qs_values[i]; merged_values // insert the post string values for(int i=0; i < post_values.length; i++) for merged_values[i + qs_values.length] = post_values[i]; merged_values // insert our new array post_parsed.put(key, merged_values); post_parsed } } parsed_values = post_parsed; } // make sure we don't have to parse again parsed = true; } /** * Checks whether a value exists or not . * * @param name the name of the valu e */ public boolean exists(String name) { // lazy parsing if(!parsed) if parse(); parse // get the string array String[] values = (String[]) parsed_values.get(name); String if(values == null) if return false; if(values.length == 0) if return false; if(values[0] == null) if return false; return true; } /** * Reads a parameters value as String. This is the more complex method . It only has to be used use * when we expect multiple parameters with the same name to come in . * * @param name the name of the paramete r * @param number the position of the parameter (0...n-1 ) * @return the value of the parameter or an empty String if it doesn' t exist exis */ public String getString(String name, int number) { // lazy parsing if(!parsed) if parse(); parse // try to read something String[] values = (String[]) parsed_values.get(name); String if( if ( values == null) return new String(); String value = null; // try to read the String try { value = values[number]; ]; } catch(ArrayIndexOutOfBoundsException aioobe) catch { // in case the number is not in the arrays range return an empty String return new String(); } // ok, let's see if we've got something if(value == null) if return new String(); // yup, return it return value; } /** * Reads a parameter's value as a String . * * @param name the name of the paramete r * @return the parameter's value as a String of an empty String if th e parameter does not exist exis */ public String getString(String name) { return getString(name, 0); } /** * Reads a parameter as a Number. This is the more complex method. I t only has to be used use * when we expect multiple parameters with the same name to come in . * * @param name the name of the paramete r * @param number the position of the parameter (0..n-1 ) * @return the paramters value as a int or -1 if the parameter does no t exist exis */ public int getNumber(String name, int number) { // lazy parsing if(!parsed) if parse(); parse // try to read something String[] values = (String[]) parsed_values.get(name); String if( if ( values == null) return -1; String value = null; // try to read the String try { value = values[number]; ]; } catch(ArrayIndexOutOfBoundsException aioobe) catch { // in case the number is not in the arrays range return -1 return -1; } // ok, let's see if we've got something if(value == null) if return -1; int num_value = -1; // yup, convert to integer return it try { num_value = Integer.parseInt(value.trim()); } catch(NumberFormatException nfe) catch { return -1; } return num_value; } /** * Reads a value as a Number. * * @param name the name of the parameter * @return the value as a String or -1 if the parameter does not exis t */ public int getNumber(String name) { return getNumber(name, 0); } /** * Counts how many times the parameter has been passed . * * @param name the name of the paramete r * @return the number of occurences occurence */ public int count(String name) { // lazy parsing if(!parsed) if parse(); parse String[] values = (String[]) parsed_values.get(name); String if(values == null) if return 0; return values.length; } /** * Creates an Hashtable that coitains key/value pairs of the * parameters. parameters * * @return the Hashtable with all value s */ public Hashtable asHashtable() { // the result Hashtable result = new Hashtable(); // lazy parsing if(!parsed) if parse(); parse // write the first of all values into the new Hashtable Enumeration enum = parsed_values.keys(); while(enum.hasMoreElements()) while { String key = (String) enum.nextElement(); String String[] values = (String[]) parsed_values.get(key); if(values != null) if { String value = values[0]; if(value != null) if result.put(key, value); result } } // return the hashtable return result; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.chat.SessionManager java.lang.Object | +----mm.webserver.jsp.JspSuperClass | +----mm.services.chat.SessionManager public class SessionManager extends mm.webserver.jsp.JspSuperClass Das SessionManager-Servlet dient dazu, die Sessions der einzelnen Clients zu managen. Es wird nur fuer noch nicht angemeldete Clients eine neue Session erzeugt. Die Default-Werte fuer NickName ist "guest" und fuer Farbe "schwarz" Version: 1.0 Author: Mirko Tochtermann SessionManager() _jspService(HttpServletRequest, HttpServletResponse) In _jspService() wird gegebenenfalls eine neue HttpSession mit den entsprechenden Werten von "NickName" bzw. jspInit() SessionManager public SessionManager() jspInit public void jspInit() Overrides: jspInit in class mm.webserver.jsp.JspSuperClass _jspService public void _jspService(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse res) In _jspService() wird gegebenenfalls eine neue HttpSession mit den entsprechenden Werten von "NickName" bzw. "Farbe" erzeugt. Parameters: req - Das HttpServletRequest-Objekt res - Das HttpServletResponse-Objekt Returns: void Overrides: _jspService in class mm.webserver.jsp.JspSuperClass All Packages Class Hierarchy This Package Previous Next Index package mm.services.chat; mm import java.util.Hashtable; import java.util.Vector; import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStream; import import import import import import import import javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; javax.servlet.ServletException; javax.servlet.ServletContext; javax.servlet.ServletInputStream; javax.servlet.http.HttpUtils; javax.servlet.RequestDispatcher; import mm.webserver.jsp.JspSuperClass; import mm.jini.ServiceController; /** * Das SessionManager-Servlet dient dazu, die Sessions der einzelne n * Clients zu managen. * Es wird nur fuer noch nicht angemeldete Clients eine neue Sessio n erzeugt. erzeugt * Die Default-Werte fuer NickName ist "guest" und fuer Farbe "schwarz " * * @version 1.0 1. * @author Mirko Tochtermann Tochterman * **/ public class SessionManager extends JspSuperClass { public void jspInit() { } /** * In _jspService() wird gegebenenfalls eine neue HttpSessio n * mit den entsprechenden Werten von "NickName" bzw. "Farbe" erzeugt . * * @param req Das HttpServletRequest-Objekt HttpServletRequest-Objek * @param res Das HttpServletResponse-Objekt HttpServletResponse-Objek * @return void voi * **/ public void _jspService(HttpServletRequest req, HttpServletResponse res) { PrintWriter out = null; String chatName = null; String chatFarbe = null; HttpSession oursession = req.getSession(true); ); PageParameters params = new PageParameters(req); if (oursession.isNew()) { chatName=new String("guest"); chatName chatFarbe=new String("schwarz"); chatFarbe } else { chatName = params.getString("NickName"); chatFarbe = params.getString("Farbe"); } try { out = res.getWriter(); } catch (IOException ioe) { ioe.printStackTrace(); ioe } oursession.putValue("NickName",chatName); oursession oursession.putValue("Farbe",chatFarbe); oursession out.println("<!DOCTYPE HTML PUBLIC \"out -//W3C//DTD HTML 4.0 Transitional//EN\">"); out.println("<html> <head><title>MM Chat Options</title></head>"); out out.println("<body><form out action=\"http://motion.ghb.fh-furtwangen.de:8001/servlet/Sessionmanager\" method=\"POST\">"); out.println("Nickname: <br>"); out out.println("<input type=\"Text\" name=\"NickName\" out value=\""+chatName + "\" size=\"20\" maxlength=\"30\" title=\"Nickname\"><br>"); out.println("<input type=\"Radio\" name=\"Farbe\" checked out value=\"rot\" title=\"Farbe\"> <font color=\"#FF0000\"> Rot <br>"); out.println("<input type=\"Radio\" name=\"Farbe\" value=\"blau\" out title=\"Farbe\"><font color=\"#0000FF\"> Blau <br>"); out.println("<input type=\"Radio\" name=\"Farbe\" value=\"gruen\" out title=\"Farbe\"><font color=\"#00FF00\"> Gruen <br>"); out.println("<input type=\"Radio\" name=\"Farbe\" value=\"schwarz\" out title=\"Farbe\"><font color=\"#000000\"> Schwarz <br>"); out.println("<input type=\"Submit\" name=\"submit\" out title=\"Abschicken\">"); out.println("</form></body></html>"); out out.flush(); out (); } } c.) mm.services.files Dieses Package enthält Klassen des Fileserver-Dienstes. Klasse FileInfo FileServer FileServerImpl RegisterFileServer Bedeutung Speichert Informationen zu einer Datei (Name, Datum, etc.) Das Remote Interface des Fileservers Implementation des Fileservers Hilfsklasse, die einen Fileserver instantiiert und beim Lookup Service anmeldet All Packages Class Hierarchy This Package Previous Next Index Class mm.services.files.FileInfo java.lang.Object | +----mm.services.files.FileInfo public class FileInfo extends java.lang.Object implements java.io.Serializable Diese Klasse repräsentiert eine Datei die in einem Place gespeichert werden kann. Sie kapselt Zusatzinformationen wie Name, Datum etc., jedoch nicht den Inhalt. Version: 1.0 Author: Stefan Zier FileInfo(String, long, Date, String) Erstellt ein FileInfo-Objekt. getCreated() Accessor für getLength() Accessor für getName() Accessor für getUploader() Accessor für created. length. name. uploader. FileInfo public FileInfo(java.lang.String name, long length, java.util.Date created, java.lang.String uploader) Erstellt ein FileInfo-Objekt. Parameters: name - der Name der Datei length - die Lände der Datei created - das Datum an dem die Datei hochgeladen/erstellt wurde getName public java.lang.String getName() Accessor für name. Liefert den Namen der Datei zurück. Returns: die Länge der Datei getLength public long getLength() Accessor für length. Liefert die Länge der Datei zurück. Returns: die Länge der Datei. getCreated public java.util.Date getCreated() Accessor für created. Liefert das Datum zurück an dem die Datei angelegt wurde. Returns: das Datum an dem die Datei angelegt wurde. getUploader public java.lang.String getUploader() Accessor für uploader. Liefert den Namen des Benutzers der die Datei hochgeladen hat. Returns: den Namen des Benutzers der die Datei hochgeladen hat. All Packages Class Hierarchy This Package Previous Next Index package mm.services.files; import java.util.Date; import java.io.Serializable; /** * Diese Klasse repräsentiert eine Datei die in einem Place gespeicher t werden werde * kann. Sie kapselt Zusatzinformationen wie Name, Datum etc., jedoch nich t den * Inhalt. * * @author Stefan Zier Zie * @version 1.0 1. */ public class FileInfo implements Serializable { private String name = null; private long length = -1; private Date created = null; private String uploader = null; /** * Erstellt ein FileInfo-Objekt. * @param name der Name der Datei Date * @param length die Lände der Date i * @param created das Datum an dem die Datei hochgeladen/erstellt wurd e */ public FileInfo(String name, long length, Date created, String uploader) { this.name = name; this this.length = length; this this.created = created; this this.uploader = uploader; this } /** * Accessor für name. Liefert den Namen der Datei zurück . * * @return die Länge der Datei Date */ public String getName() { return name; } /** * Accessor für length. Liefert die Länge der Datei zurück . * * @return die Länge der Datei. Datei */ public long getLength() { return length; } /** * Accessor für created. Liefert das Datum zurück an dem die Date i angelegt angeleg * wurde. wurde * * @return das Datum an dem die Datei angelegt wurde . */ public Date getCreated() { return created; } /** * Accessor für uploader. Liefert den Namen des Benutzers der die Date i hochgeladen hat. hat * * @return den Namen des Benutzers der die Datei hochgeladen hat . */ public String getUploader() { return uploader; } } All Packages Class Hierarchy This Package Previous Next Index Interface mm.services.files.FileServer public interface FileServer extends java.rmi.Remote, AliveCheckable Dies ist das Interface zum Fileserver der Anwendung. Er verwaltet alle Dateien in sogenannten file places, also in Ablagen. Dabei besteht keine Möglichkeit, diese Ablagen zu strukturieren, z.B. durch Unterverzeichnisse o.Ä. Places werden als Strings identifiziert. Version: 1.0 Author: Stefan Zier deletePlace(String) Diese Methode löscht einen Place samt allen darin enthaltenen Dateien. getFile(String, String) Diese Methode liefert den Inhalt einer Datei zurück. listFiles(String) Diese Methode listet all Dateien in einem Place auf. putFile(String, String, String, byte[]) Speichert eine Datei ein einem place. putFile public void putFile(java.lang.String place, java.lang.String uploader, java.lang.String name, byte content[]) throws java.rmi.RemoteException Speichert eine Datei ein einem place. Existiert bereits eine Datei mit dem gleichen Namen so wird sie überschrieben Parameters: place - Der identifier für den Place. Existiert der Place nicht bereits so wird er erstellt. uploader - Der Mitspieler der die Datei hochgeladen hat name - Der Name der Datei content - Der Inhalt der Datei listFiles public java.util.Vector listFiles(java.lang.String place) throws java.rmi.RemoteException Diese Methode listet all Dateien in einem Place auf. Dabei gibt sie einen sortierten Vector zurück, der Einträge der Klasse FileInfo enthält. Parameters: place - der Place der gelistet werden soll Returns: einen Vector mit FileInfo records getFile public byte[] getFile(java.lang.String place, java.lang.String name) throws java.rmi.RemoteException Diese Methode liefert den Inhalt einer Datei zurück. Parameters: place - der Place von dem die Datei heruntergeladen werden soll name - der Name der Datei die heruntergeladen werden soll Returns: der Inhalt der Datei deletePlace public void deletePlace(java.lang.String place) throws java.rmi.RemoteException Diese Methode löscht einen Place samt allen darin enthaltenen Dateien. Parameters: place - der Place der gelöscht werden soll. All Packages Class Hierarchy This Package Previous Next Index package mm.services.files; import java.util.Vector; import java.rmi.Remote; import java.rmi.RemoteException; import mm.tools.AliveCheckable; /** * Dies ist das Interface zum Fileserver der Anwendung. Er verwaltet all e Dateien Dateie * in sogenannten file places, also in Ablagen. Dabei besteht kein e Möglichkeit, Möglichkeit * diese Ablagen zu strukturieren, z.B. durch Unterverzeichnisse o.Ä . Places werden werde * als Strings identifiziert. identifiziert * * @author Stefan Zier Zie * @version 1.0 1. */ public interface FileServer extends Remote, AliveCheckable { /** * Speichert eine Datei ein einem place. Existiert bereits eine Date i mit dem de * gleichen Namen so wird sie überschriebe n * * @param place Der identifier für den Place. Existiert der Place nich t bereits bereit * so wird er erstellt . * @param uploader Der Mitspieler der die Datei hochgeladen ha t * @param name Der Name der Datei Date * @param content Der Inhalt der Date i */ public void putFile(String place, String uploader, String name, byte[] content) throws RemoteException; /** * Diese Methode listet all Dateien in einem Place auf. Dabei gibt si e einen * sortierten Vector zurück, der Einträge der Klasse FileInfo enthält. * * @param place der Place der gelistet werden sol l * @return einen Vector mit FileInfo record s */ public Vector listFiles(String place) throws RemoteException; /** * Diese Methode liefert den Inhalt einer Datei zurück . * * @param place der Place von dem die Datei heruntergeladen werden sol l * @param name der Name der Datei die heruntergeladen werden sol l * @return der Inhalt der Datei Date */ public byte[] getFile(String place, String name) throws RemoteException; /** * Diese Methode löscht einen Place samt allen darin enthaltene n Dateien. Dateien * * @param place der Place der gelöscht werden soll . */ public void deletePlace(String place) throws RemoteException; } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.files.FileServerImpl mm.services.files.FileServerImpl public class FileServerImpl implements FileServer Diese Klasse implementiert den Fileserver. Sie liest die Konfigurationsdatei fileserver.properties. Version: 1.0 Author: Stefan Zier FileServerImpl() Konstruktur. deletePlace(String) Diese Metode löscht einen Place samt allen darin enthaltenen Dateien. getFile(String, String) Diese Methode liefert den Inhalt einer Datei zurück. isAlive() Gibt true zurück wenn alles mit dem FileServer in Ordnung ist. listFiles(String) Diese Methode listet all Dateien in einem Place auf. putFile(String, String, String, byte[]) Speichert eine Datei ein einem place. serviceIDNotify(ServiceID) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. FileServerImpl public FileServerImpl() throws java.rmi.RemoteException, java.sql.SQLException, java.io.IO Konstruktur. Liest die Konfigurationsdatei und baut den DatenbankVerbindungs-Pool auf. Throws: java.sql.SQLException - Wenn was mit den Datenbankverbindungen schiefgeht. java.io.IOException - Wenn was mit dem Configfile schiefgeht. java.lang.ClassNotFoundException - Wenn das mit dem Connection Pool Manager schiefgeht. java.lang.InstantiationException - Wenn das mit dem Connection Pool Manager schiefgeht. java.lang.IllegalAccessException - Wenn das mit dem Connection Pool Manager schiefgeht. putFile public void putFile(java.lang.String place, java.lang.String uploader, java.lang.String name, byte content[]) throws java.rmi.RemoteException Speichert eine Datei ein einem place. Existiert bereits eine Datei mit dem gleichen Namen so wird sie überschrieben Parameters: place - Der identifier für den Place. Existiert der Place nicht bereits so wird er erstellt. uploader - Der Mitspieler der die Datei hochgeladen hat name - Der Name der Datei content - Der Inhalt der Datei listFiles public java.util.Vector listFiles(java.lang.String place) throws java.rmi.RemoteException Diese Methode listet all Dateien in einem Place auf. Dabei gibt sie einen sortierten Vector zurück, der Einträge der Klasse FileInfo enthält. Parameters: place - der Place der gelistet werden soll Returns: einen Vector mit FileInfo records getFile public byte[] getFile(java.lang.String place, java.lang.String name) throws java.rmi.RemoteException Diese Methode liefert den Inhalt einer Datei zurück. Parameters: place - der Place von dem name - der Name der Datei die Datei heruntergeladen werden soll die heruntergeladen werden soll Returns: der Inhalt der Datei oder null wenn die Datei nicht gelesen werden konnte deletePlace public void deletePlace(java.lang.String place) throws java.rmi.RemoteException Diese Metode löscht einen Place samt allen darin enthaltenen Dateien. Parameters: place - der Place der gelöscht werden soll. serviceIDNotify public void serviceIDNotify(net.jini.core.lookup.ServiceID service_id) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. Parameters: service_id - Service ID isAlive public boolean isAlive() throws java.rmi.RemoteException Gibt true zurück wenn alles mit dem FileServer in Ordnung ist. Returns: true wenn alles mit dem FileServer in Ordnung ist. All Packages Class Hierarchy This Package Previous Next Index package mm.services.files; // Java core imports import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; import java.io.IOException; import java.io.File; import java.io.FileOutputStream; import java.io.FileInputStream; import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; import java.util.Vector; import java.util.Date; // Jini extension imports import com.sun.jini.lookup.ServiceIDListener; import net.jini.core.lookup.ServiceID; // Database connection imports import com.bitmechanic.sql.ConnectionPoolManager; import com.bitmechanic.sql.ConnectionPool; // MM Project imports import mm.tools.ConfigFile; import mm.tools.Static; /** * Diese Klasse implementiert den Fileserver. Sie liest di e Konfigurationsdatei * fileserver.properties. * * @author Stefan Zier Zie * @version 1.0 1. */ public class FileServerImpl extends UnicastRemoteObject implements FileServer, ServiceIDListener { // database connections private ConnectionPoolManager cpm = null; private ConnectionPool pool = null; // file locations private String basepath = null; /** * Konstruktur. Liest die Konfigurationsdatei und baut de n Datenbank-Verbindungs-Pool * auf. auf * * @exception SQLException Wenn was mit den Datenbankverbindunge n schiefgeht. schiefgeht * @exception IOException Wenn was mit dem Configfile schiefgeht . * @exception ClassNotFoundException Wenn das mit dem Connection Poo l Manager schiefgeht. schiefgeht * @exception InstantiationException Wenn das mit dem Connection Poo l Manager schiefgeht. schiefgeht * @exception IllegalAccessException Wenn das mit dem Connection Poo l Manager schiefgeht. schiefgeht */ public FileServerImpl() throws RemoteException, SQLException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException InstantiationException { super(); super ConfigFile configfile = new ConfigFile("f:\\mm\\services\\files\\fileserver.properties"); // create a connection pool manager cpm = new ConnectionPoolManager(120); // get String String String url, username and password from the configuration file url = configfile.get("fileserver.database.URL"); username = configfile.get("fileserver.database.username"); password = configfile.get("fileserver.database.password"); basepath = configfile.get("fileserver.basepath"); if if(basepath == null || basepath.equals("")) { throw new IllegalArgumentException("mm: no } if(url == null || url.equals("")) if { throw new IllegalArgumentException("mm: no } if(username == null || username.equals("")) if { throw new IllegalArgumentException("mm: no } if(password == null || password.equals("")) if { throw new IllegalArgumentException("mm: no given"); } base path given"); database URL given"); database login given"); database password // add the alias for "our" database cpm.addAlias("fileserver", "org.gjt.mm.mysql.Driver", url, username, cpm password, 120, 300, 120); pool = cpm.getPool("fileserver"); } /** * Speichert eine Datei ein einem place. Existiert bereits eine Date i mit dem de * gleichen Namen so wird sie überschriebe n * * @param place Der identifier für den Place. Existiert der Place nich t bereits bereit * so wird er erstellt . * @param uploader Der Mitspieler der die Datei hochgeladen ha t * @param name Der Name der Datei Date * @param content Der Inhalt der Date i */ public void putFile(String place, String uploader, String name, byte[] content) throws RemoteException { // checken ob die Parameter korrekt sind if(Static.empty(place)) if throw new IllegalArgumentException("mm: place was empty in putFile()"); if(Static.empty(uploader)) if throw new IllegalArgumentException("mm: uploader was empty in putFile()"); if(Static.empty(name)) if throw new IllegalArgumentException("mm: name was empty in putFile()"); if(content == null) if throw new IllegalArgumentException("mm: content was null in putFile()"); // checken ob der place existiert und ein Verzeichnis ist File directory = new File(basepath + "/" + place); if if(!directory.exists()) { // create the place if(!directory.mkdir()) if throw new IllegalArgumentException("mm: the place could not be created"); } else { if(!directory.isDirectory()) if throw new IllegalArgumentException("mm: the place is not a directory!"); } // Jetzt können wir sicher sein ein existierendes Verzeichnis zu haben. // ist die Datei schon da? File file = new File(basepath + "/" + place + "/" + name); // ist die Datei schon da, dann löschen if(file.exists()) if file.delete(); file try { // outputstream öffnen FileOutputStream out = new FileOutputStream(file); // inhalt in die datei schreiben out.write(content); out // stream schliessen out.close(); out } catch(IOException ioe) catch { throw new RemoteException("mm: the file could not be written", ioe); } // ok, die Datei ist auf der Platte, jetzt müssen wir noch den Uploader // in die Datenbank schreiben try { // ggf. vorhandene alte Einträge entfernen String query = "DELETE FROM files WHERE place=" + Static.sqlString(place) + " AND name=" + Static.sqlString(name); // datenbank-verbindung aus dem pool holen Connection conn = pool.getConnection(); Statement sDeleteFile = conn.createStatement(); sDeleteFile.execute(query); sDeleteFile sDeleteFile.close(); sDeleteFile // neuen eintrag einfügen query = "INSERT INTO files (place, name, uploader) values("; query += Static.sqlString(place); query += ", "; query += Static.sqlString(name); query += ", "; query += Static.sqlString(uploader); query += ")"; Statement sInsertFile = conn.createStatement(); sInsertFile.execute(query); sInsertFile sInsertFile.close(); sInsertFile // datenbank-verbindung in den pool zurückstellen conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: the file could not be written to the database", sql); } } /** * Diese Methode listet all Dateien in einem Place auf. Dabei gibt si e einen * sortierten Vector zurück, der Einträge der Klasse FileInfo enthält. * * @param place der Place der gelistet werden sol l * @return einen Vector mit FileInfo record s */ public Vector listFiles(String place) throws RemoteException { // checken ob die Parameter korrekt sind if(Static.empty(place)) if throw new IllegalArgumentException("mm: place was empty in putFile()"); Vector out = new Vector(); // checken ob der place existiert und ein Verzeichnis ist File directory = new File(basepath + "/" + place); // wenn es den Place nicht gibt den leeren Vector zurückgeben if(!directory.exists()) if return out; // filelist holen String[] files = directory.list(); String // filelist sortieren Static.sortStrings(files); Static // durch die filelist gehen for(int i=0; i<files.length;i++) for { File file = new File(directory.getPath() + "/" + files[i]); if(file.exists() && !file.isDirectory()) if { // uploader aus der Datenbank lesen String uploader = null; try { String query = "SELECT uploader FROM files WHERE place="; query += Static.sqlString(place); query += " AND name="; query += Static.sqlString(files[i]); // connection aus dem pool holen Connection conn = pool.getConnection(); Statement sGetFile = conn.createStatement(); ResultSet rsGetFile = sGetFile.executeQuery(query); // wenn records gelesen wurden if(rsGetFile.next()) if { uploader = rsGetFile.getString("uploader"); } sGetFile.close(); sGetFile // connection in den pool zurückstellen conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: uploader konnte nicht aus der Datenbank gelesen werden.", sql); } FileInfo fi = new FileInfo(files[i], file.length(), new Date(file.lastModified()), uploader); out.addElement(fi); out } } return out; } /** * Diese Methode liefert den Inhalt einer Datei zurück . * * @param place der Place von dem die Datei heruntergeladen werden sol l * @param name der Name der Datei die heruntergeladen werden sol l * @return der Inhalt der Datei oder null wenn die Datei nicht gelese n werden konnte konnt */ public byte[] getFile(String place, String name) throws RemoteException { File file = new File(basepath + "/" + place + "/" + name); // prüfen ob die Datei existiert und kein Verzeichnis ist if(!file.exists() || file.isDirectory()) if return null; byte[] result = null; byte try { // datei öffnen FileInputStream in = new FileInputStream(file); // einen puffer in grösse der Datei anlegen int size = (int) file.length(); result = new byte[size]; // Datei in den Puffer lesen in.read(result); in // datei schliessen in.close(); in } catch(IOException ioe) catch { throw new RemoteException("mm: could not read the file from disk" , ioe); } // den Puffer zurückgeben return result; } /** * Private Methode, die eine Datei/ein Verzeichnis rekursiv löscht . * * @param file die Datei/das Verzeichnis das gelöscht werden sol l */ private void deleteFile(File file) throws IOException { if(file.isFile()) if { file.delete(); file } if if(file.isDirectory()) { // fileliste des verzeichnisses lesen String[] filenames = file.list(); String // durch die Files gehen und alle löschen for(int i=0; i<filenames.length; i++) for deleteFile(new File(file.getPath() + "/" + filenames[i])); deleteFile // verzeichnis löschen file.delete(); file } } /** * Diese Metode löscht einen Place samt allen darin enthaltenen Dateien . * * @param place der Place der gelöscht werden soll . */ public void deletePlace(String place) throws RemoteException { try { // Place löschen deleteFile(new File(basepath + "/" + place)); deleteFile } catch(IOException ioe) catch { throw new RemoteException("mm: error deleting place", ioe); } } /** * Diese Methode wird vom Join Manager aufgerufen um dem Service sein e Service ID mitzuteilen mitzuteile * so bald der Service registriert ist . * * @param service_id Service ID I */ public void serviceIDNotify(ServiceID service_id) { System.out.print("Service registered, ID: "); System System.out.println(service_id); System } /** * Gibt true zurück wenn alles mit dem FileServer in Ordnung ist . * * @return true wenn alles mit dem FileServer in Ordnung ist . */ public boolean isAlive() throws RemoteException { // check the database connection try { // kleine Testquery Connection conn = pool.getConnection(); Statement s = conn.createStatement(); ResultSet rs = s.executeQuery("SELECT checkvalue FROM checktable"); if(rs.next()) if { String weltherrschaft = rs.getString("checkvalue"); if(weltherrschaft == null || if !weltherrschaft.equalsIgnoreCase("WELTHERRSCHAFT")) return false; } else return false; s.close(); conn.close(); conn } catch(SQLException sql) catch { return false; } return true; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.files.RegisterFileServer java.lang.Object | +----mm.services.files.RegisterFileServer public class RegisterFileServer extends java.lang.Object Diese Klasse instantiiert den File server und registriert ihn beim Lookup Service. Version: 1.0 Author: Stefan Zier RegisterFileServer() main(String[]) Main methode. RegisterFileServer public RegisterFileServer() main public static void main(java.lang.String argsv[]) Main methode. All Packages Class Hierarchy This Package Previous Next Index package mm.services.files; // Java core imports import java.rmi.*; // Jini extension imports import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; import net.jini.lookup.entry.Name; import net.jini.lookup.entry.ServiceInfo; import net.jini.core.entry.Entry; /** * Diese Klasse instantiiert den File server und registriert ihn bei m Lookup Service. Service * * @author Stefan Zier Zie * @version 1.0 1. */ public class RegisterFileServer { static JoinManager jm; static FileServerImpl fileserver_instance; /** * Main methode. */ public static void main(String[] argsv) { System.out.println("RegisterFileServer (c) 1999 MM Jahresprojekt"); System try { // install security manager so that we'll be able to talk to server System.out.println(">> installiere RMI Security manager..."); System System.setSecurityManager(new RMISecurityManager()); System // create service System.out.println(">> instantiiere File Server ..."); System fileserver_instance = new FileServerImpl(); // this name will be seen to users that browse services Name nameEntry = new Name("MM File Server"); // some addtl. serivice information such as manufacterer etc. ServiceInfo serviceInfo = new ServiceInfo("MM File Server", "CE5 WS 1999/2000", "CE5 WS 1999/2000", "1.0", "", ""); // gather all information that we're going to pass on to the lookup service Entry entries[] = new Entry[] {nameEntry, serviceInfo}; System.out.println(">> registriere den File Server beim Lookup System Service..."); // create a new join manager and have it register the jts_instance. jm = new JoinManager(fileserver_instance, entries, (ServiceIDListener) fileserver_instance, new LeaseRenewalManager()); System.out.println(">> File Server bereit"); System } catch(Exception e) catch { System.out.println("##### mm: exception caught: "); System e.printStackTrace(); } } } d.) mm.services.game Dieses Package enthält Klassen des Spielmanager-Dienstes. Klasse Game GameMaster GameServer GameServerImpl Group Player RegisterGameServer Bedeutung Stellt ein Spiel dar Stellt einen Spielleiter dar Das Remote-Interface des SpielmanagerDienstes Die Implementation des SpielmanagerDienstes Stellt eine Gruppe dar Stellt einen Spieler dar Instantiiert einen Spielmanager-Dienst und registriert ihn beim Lookup Service All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.Game java.lang.Object | +----mm.services.game.Game public class Game extends java.lang.Object implements java.io.Serializable Diese Klasse speichert alle Informationen, die zu einem Spiel gehöeren, wie Name der Spielübergreifenden File-places, Chatgroups, Messageboards, etc. Version: 1.0 Author: Stefan Zier Game(String, String, String, String, String, String) Konstruktor. getChatRoom() Accessor für Chatroom getFromGameMasterPlace() Accessor für from_gm_place getGameMaster() Accessor für Gamemaster. getName() Accessor für Name getNiceName() Accessor für Nicename getToGameMasterPlace() Accessor für to_gm_place Game public Game(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster) Konstruktor. Parameters: name - der Name des Spiels nicename - der Name des Spiels der auf dem Bildschirm angezeigt wird chatroom - der Name des Spielweiten Chatrooms to_gm_place - der Name des File places auf den Dateien gelegt werden die zum Spielleiter geschickt werden from_gm_place - der Name des File places auf die der Spielleiter Dateien legt die für alle Spieler da sind gamemaster - der login des Gamemasters der das Spiel verwaltet getName public java.lang.String getName() Accessor für Name Returns: den Namen des Spiels getNiceName public java.lang.String getNiceName() Accessor für Nicename Returns: den Namen des Spieles wie er auf dem Bildschirm angezeigt werden soll. getChatRoom public java.lang.String getChatRoom() Accessor für Chatroom Returns: den Namen des Spielweiten Chatrooms getToGameMasterPlace public java.lang.String getToGameMasterPlace() Accessor für to_gm_place Returns: den Namen des File Places der Benutzt wird um Daten von der Gruppe an den Spielleiter zu schicken getFromGameMasterPlace public java.lang.String getFromGameMasterPlace() Accessor für from_gm_place Returns: den Namen des File Places der benutzt wird um Dateien vom Spielleiter an die Gruppe zu schicken getGameMaster public java.lang.String getGameMaster() Accessor für Gamemaster. Returns: den Login des Gamemasters der das Spiel verwaltet All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; import java.io.Serializable; /** * Diese Klasse speichert alle Informationen, die zu einem Spiel gehöeren , wie Name Nam * der Spielübergreifenden File-places, Chatgroups, Messageboards, etc . * * @author Stefan Zier Zie * @version 1.0 1. */ public class Game implements Serializable { private String name = null; private String nicename = null; private String chatroom = null; private String to_gm_place = null; private String from_gm_place = null; private String gamemaster = null; /** * Konstruktor. Konstruktor * * @param name der Name des Spiel s * @param nicename der Name des Spiels der auf dem Bildschirm angezeig t wird wir * @param chatroom der Name des Spielweiten Chatroom s * @param to_gm_place der Name des File places auf den Dateien geleg t werden die * zum Spielleiter geschickt werde n * @param from_gm_place der Name des File places auf die der Spielleite r Dateien legt leg * die für alle Spieler da sin d * @param gamemaster der login des Gamemasters der das Spiel verwalte t */ public Game(String name, String nicename, String chatroom, String to_gm_place, String from_gm_place, String gamemaster) { this.name = name; this this.nicename = name; this this.chatroom = chatroom; this this.to_gm_place = to_gm_place; this this.from_gm_place = from_gm_place; this this.gamemaster = gamemaster; this } /** * Accessor für Name Nam * * @return den Namen des Spiels Spiel */ public String getName() { return name; } /** * Accessor für Nicename Nicenam * * @return den Namen des Spieles wie er auf dem Bildschirm angezeig t werden soll. soll */ public String getNiceName() { return nicename; } /** * Accessor für Chatroom Chatroo * * @return den Namen des Spielweiten Chatroom s */ public String getChatRoom() { return chatroom; } /** * Accessor für to_gm_place to_gm_plac * * @return den Namen des File Places der Benutzt wird um Daten von de r Gruppe an den de * Spielleiter zu schicke n */ public String getToGameMasterPlace() { return to_gm_place; } /** * Accessor für from_gm_place from_gm_plac * * @return den Namen des File Places der benutzt wird um Dateien vo m Spielleiter an a * die Gruppe zu schicke n */ public String getFromGameMasterPlace() { return from_gm_place; } /** * Accessor für Gamemaster. Gamemaster * * @return den Login des Gamemasters der das Spiel verwalte t */ public String getGameMaster() { return gamemaster; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.GameMaster java.lang.Object | +----mm.services.game.GameMaster public class GameMaster extends java.lang.Object implements java.io.Serializable Der Spielleiter. Version: 1.0 Author: Stefan Zier GameMaster(String, String, String, String, boolean) Konstruktor getEmail() Accessor für getLanguage() Accessor für getLogin() Accessor für getName() Accessor für isFirstLogin() Accessor für email. Sprache Login. Name. First Login. GameMaster public GameMaster(java.lang.String login, java.lang.String name, java.lang.String email, java.lang.String language, boolean first_login) Konstruktor Parameters: login - der Login des Spielleiters name - der Name des Spielleiters email - die Email-Addresse des Spielleiters getLogin public java.lang.String getLogin() Accessor für Login. Returns: den Login des Spielleiters getLanguage public java.lang.String getLanguage() Accessor für Sprache Returns: die Sprache des Spielleiters getName public java.lang.String getName() Accessor für Name. Returns: den Namen des Spielleiters. getEmail public java.lang.String getEmail() Accessor für email. Returns: die Email Adresse des Spielleiters. isFirstLogin public boolean isFirstLogin() Accessor für First Login. Returns: true wenn dies der erste Login des Spielleiters ist All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; import java.io.Serializable; /** * Der Spielleiter. Spielleiter * * @author Stefan Zier Zie * @version 1.0 1. */ public class GameMaster implements Serializable { private String login = null; private String name = null; private String email = null; private String language = null; private boolean first_login; /** * Konstruktor Konstrukto * * @param login der Login des Spielleiter s * @param name der Name des Spielleiter s * @param email die Email-Addresse des Spielleiter s */ public GameMaster(String login, String name, String email, String language, boolean first_login) { this.login = login; this this.name = name; this this.email = email; this this.language = language; this this.first_login = first_login; this } /** * Accessor für Login. Login * * @return den Login des Spielleiters Spielleiter */ public String getLogin() { return login; } /** * Accessor für Sprache Sprach * * @return die Sprache des Spielleiters Spielleiter */ public String getLanguage() { return language; } /** * Accessor für Name. Name * * @return den Namen des Spielleiters. Spielleiters */ public String getName() { return name; } /** * Accessor für email. email * * @return die Email Adresse des Spielleiters . */ public String getEmail() { return email; } /** * Accessor für First Login. Login * * @return true wenn dies der erste Login des Spielleiters is t */ public boolean isFirstLogin() { return first_login; } } All Packages Class Hierarchy This Package Previous Next Index Interface mm.services.game.GameServer public interface GameServer extends java.rmi.Remote, AliveCheckable Diese ist das Interface zum Spiel-Jini-Service. Der Webserver kommuniziert ausschließlich über diese Schnittstelle zum Spielserver. Regeln: Spielernamen sind Applikationsweit eindeutig. - Spielnamen sind Applikationsweit eindeutig. - Gruppennamen sind innerhalb eines Spieles eindeutig. Deswegen muß hier immer noch das Spiel mit angegebeben werden. Version: 1.0 Author: Stefan Zier authenticateGameMaster(String, String) Diese Methode authentifiziert einen Spielleiter. authenticatePlayer(String, String) Diese Methode dient dazu einen Spieler zu authentifizieren. changeGameMaster(String, String) Teilt einem Spiel einen neuen Spielleiter zu. changeGameMasterEmail(String, String) Ändert die Email Adresse eines Spielleiters. changeGameMasterLanguage(String, String) Ändert die Sprache eines Spielleiters. changeGameMasterPassword(String, String) Ändert das Passwort eines Spielleiters. changePlayerEmail(String, String) Ändert die Email-Addresse eines Spielers. changePlayerLanguage(String, String) Diese Methode ändert die Sprache eines Spielers. changePlayerPassword(String, String) Diese Methode ändert das Passwort eines Spielers. createGame(String, String, String) Diese Methode erstellt ein neues Spiel. createGameMaster(String, String, String, String) Fügt einen GameMaster hinzu createGroup(String, String, String) Diese Methode erstellt eine Gruppe. createPlayer(String, String, String, String, String, String) Diese Methode erstellt einen neuen Spieler. deleteGame(String) Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen. deleteGameMaster(String) Löscht einen GameMaster deleteGroup(String, String) Diese Methode löscht eine Gruppe und all deren Mitspieler. deletePlayer(String) Löscht einen Spieler. getGame(String) Diese Methode sucht informationen zu einem Spiel und liefert es zurück. getGameMaster(String) Liest einen Spielleiter aus der Datenbank getGroup(String, String) Diese Methode sucht Informationen zu einer Gruppe und liefert ein Group-Objekt zurück. getPlayer(String) Sucht informationen über einen Spieler und liefert sie in Form eines Player-Objektes zurück. listGameMasters() Generiert eine Liste der GameMasters listGames() Listet alle Spiele applikationsweit auf. listGroups() Listet alle Gruppen applikationsweit auf. listGroups(String) Listet alle Gruppen in einem Spiel auf. listPlayers() Listet alle Spieler applikationsweit auf. listPlayers(String) Listet alle Spieler eines Spiels auf. listPlayers(String, String) Listet alle Spieler einer Gruppe auf. sendGameMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an alle Mitglieder eines Spiels sendGameMasterMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an einen SpielLeiter sendGroupMail(String, String, String, String, String, Hashtable) Verschickt ein Mail Template an alle Mitlieder einer Gruppe sendPasswordMail(String) Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ihm sein Passwort mit. sendPlayerMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an einen Spieler authenticatePlayer public Player authenticatePlayer(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExceptio Diese Methode dient dazu einen Spieler zu authentifizieren. Sie liefert als Rückgabewert ein Player Objekt, mit dem alle Informationen zu einem Spieler zugänglich sind. Parameters: login - Der username password - Das Passwort Returns: null wenn der Nutzer unbekannt ist, ansonsten das Player-Objekt authenticateGameMaster public GameMaster authenticateGameMaster(java.lang.String login, java.lang.String password) throws java.rmi.Remote Diese Methode authentifiziert einen Spielleiter. Der Rückgabewert ist ein GameMaster objekt. Parameters: login - der Login des Spielleiters password - das Passwort Returns: null wenn der Nutzer unbekannt ist, ansonsten das GameMasterObjekt createGame public Game createGame(java.lang.String name, java.lang.String nicename, java.lang.String gamemaster) throws java.rmi.RemoteException Diese Methode erstellt ein neues Spiel. Parameters: name - der Name des Spiels nicename - der Name des Spiels der auf dem Bildschirm dargestellt wird gamemaster - das Login des Spielleiters, der das Spiel leitet Returns: das Game-Objekt deleteGame public void deleteGame(java.lang.String name) throws java.rmi.RemoteException Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen. Parameters: name - der Name des Spiels changeGameMaster public void changeGameMaster(java.lang.String game, java.lang.String gamemaster) throws java.rmi.RemoteException Teilt einem Spiel einen neuen Spielleiter zu. Parameters: game - der gamemaster Name des Spiels - der Name des neuen Spielleiters getGame public Game getGame(java.lang.String name) throws java.rmi.RemoteException Diese Methode sucht informationen zu einem Spiel und liefert es zurück. Parameters: name - der Name des Spiels Returns: das Game-Objekt oder null wenn das Spiel nicht existiert listGames public java.util.Vector listGames() throws java.rmi.RemoteException Listet alle Spiele applikationsweit auf. Returns: einen Vector der Game-Objekte enthält. createGroup public Group createGroup(java.lang.String name, java.lang.String nicename, java.lang.String game) throws java.rmi.RemoteException Diese Methode erstellt eine Gruppe. Parameters: name - der Name der Gruppe nicename - der Name der Gruppe wie er auf dem Bildschirm dargestellt wird game - der Name des Spiels zu der die Gruppe gehört deleteGroup public void deleteGroup(java.lang.String name, java.lang.String game) throws java.rmi.RemoteException Diese Methode löscht eine Gruppe und all deren Mitspieler. Parameters: name - der Name der Gruppe game - der Name des SPiels zu dem die Gruppe gehört getGroup public Group getGroup(java.lang.String name, java.lang.String game) throws java.rmi.RemoteException Diese Methode sucht Informationen zu einer Gruppe und liefert ein Group-Objekt zurück. Parameters: name - der Name der Gruppe game - der Name des Spiels zu dem die Gruppe gehört Returns: null, wenn die Gruppe nicht gefunden wurde, sonst das GroupObjekt listGroups public java.util.Vector listGroups(java.lang.String game) throws java.rmi.RemoteException Listet alle Gruppen in einem Spiel auf. Parameters: game - das Spiel Returns: einen Vector, der Group-Objekte enthält listGroups public java.util.Vector listGroups() throws java.rmi.RemoteException Listet alle Gruppen applikationsweit auf. Returns: einen Vector, der Group-Objekte enthält createPlayer public Player createPlayer(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String login, name, email, group, game, language) throws java.rmi.RemoteException Diese Methode erstellt einen neuen Spieler. Parameters: login - der login des Spielers name - der Name des Spielers emael - die E-Mail-Addresse des Spielers group - der Name der Gruppe zu der der Spieler gehört game - der Name des Spiels zu dem die Gruppe gehört language - das 2-Buchstaben-Kürzel der Sprache in der der Spieler die Webseiten anschaut changePlayerPassword public void changePlayerPassword(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExceptio Diese Methode ändert das Passwort eines Spielers. Parameters: login - der Login des Spielers password - das neue Passwort des Spielers changePlayerLanguage public void changePlayerLanguage(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExceptio Diese Methode ändert die Sprache eines Spielers. Parameters: login - der Login des Spielers language - die neue Sprache des Spielers changePlayerEmail public void changePlayerEmail(java.lang.String login, java.lang.String email) throws java.rmi.RemoteException Ändert die Email-Addresse eines Spielers. Parameters: login - der Login des Spielers email - die neue Email des Spielers deletePlayer public void deletePlayer(java.lang.String login) throws java.rmi.RemoteException Löscht einen Spieler. Parameters: login - der login des Spielers. getPlayer public Player getPlayer(java.lang.String login) throws java.rmi.RemoteException Sucht informationen über einen Spieler und liefert sie in Form eines Player-Objektes zurück. Parameters: login - der login des Spielers listPlayers public java.util.Vector listPlayers(java.lang.String group, java.lang.String game) throws java.rmi.RemoteException Listet alle Spieler einer Gruppe auf. Parameters: group - die Gruppe game - das Spiel Returns: einen Vector mit Player-Objekten listPlayers public java.util.Vector listPlayers(java.lang.String game) throws java.rmi.RemoteException Listet alle Spieler eines Spiels auf. Parameters: game - das Spiel Returns: einen Vector mit Player-Objekten listPlayers public java.util.Vector listPlayers() throws java.rmi.RemoteException Listet alle Spieler applikationsweit auf. Returns: einen Vector mit Player-Objekten createGameMaster public GameMaster createGameMaster(java.lang.String java.lang.String java.lang.String java.lang.String login, email, name, language) throws java.rmi.RemoteExcept Fügt einen GameMaster hinzu Parameters: login - der Login des neuen GameMasters email - die Email des neuen GameMasters name - der Name des neuen GameMasters language - die Sprache des neuen GameMasters Returns: den neuen GameMaster oder null deleteGameMaster public void deleteGameMaster(java.lang.String login) throws java.rmi.RemoteException Löscht einen GameMaster Parameters: login - der Login des GameMasters der gelöscht werden soll listGameMasters public java.util.Vector listGameMasters() throws java.rmi.RemoteException Generiert eine Liste der GameMasters Returns: einen Vector mit GameMaster-Objekten changeGameMasterPassword public void changeGameMasterPassword(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExce Ändert das Passwort eines Spielleiters. Parameters: login - der Login des Spielleiters password - das neue Passwort des Spielleiters changeGameMasterEmail public void changeGameMasterEmail(java.lang.String login, java.lang.String email) throws java.rmi.RemoteException Ändert die Email Adresse eines Spielleiters. Parameters: login - der Login des Spielleiters email - die neue Email des Spielleiters changeGameMasterLanguage public void changeGameMasterLanguage(java.lang.String login, java.lang.String language) throws java.rmi.RemoteExce Ändert die Sprache eines Spielleiters. Parameters: login - der Login des Spielleiters language - die neue Sprache des Spielleiters getGameMaster public GameMaster getGameMaster(java.lang.String login) throws java.rmi.RemoteException Liest einen Spielleiter aus der Datenbank Parameters: login - der Login des Spielleiters sendGameMail public void sendGameMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String game, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an alle Mitglieder eines Spiels Parameters: snd_email - der Absender der email snd_name - der Name des Absenders game - der Name des Spiels template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendGroupMail public void sendGroupMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String game, java.lang.String group, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an alle Mitlieder einer Gruppe Parameters: snd_email - der Absender der email snd_name - der Name des Absenders game - der Name des Spiels group - der Name der Gruppe template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendPlayerMail public void sendPlayerMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String login, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an einen Spieler Parameters: snd_email - der Absender der email snd_name - der Name des Absenders login - Login des Spielers template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendGameMasterMail public void sendGameMasterMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String login, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteExceptio Verschickt ein Mail Template an einen SpielLeiter Parameters: snd_email - der Absender der email snd_name - der Name des Absenders login - Login des Spielleiters template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendPasswordMail public void sendPasswordMail(java.lang.String login) throws java.rmi.RemoteException Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ihm sein Passwort mit. Parameters: login - der Login des Spielleiters oder Spielers All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; import import import import java.util.Hashtable; java.util.Vector; java.rmi.Remote; java.rmi.RemoteException; import mm.tools.AliveCheckable; /** * Diese ist das Interface zum Spiel-Jini-Service. Der Webserve r * kommuniziert ausschließlich über diese Schnittstelle zum * Spielserver. * * Regeln: Regeln * * - Spielernamen sind Applikationsweit eindeutig. eindeutig * - Spielnamen sind Applikationsweit eindeutig. * - Gruppennamen sind innerhalb eines Spieles eindeutig. Deswegen muß hie r immer imme * noch das Spiel mit angegebeben werden . * * @author Stefan Zier Zie * @version 1.0 1. */ public interface GameServer extends Remote, AliveCheckable { /** * Diese Methode dient dazu einen Spieler zu authentifizieren. Si e * liefert als Rückgabewert ein Player Objekt, mit dem all e Informationen Informatione * zu einem Spieler zugänglich sind. * * @param login Der username usernam * @param password Das Passwort Passwor * @return null wenn der Nutzer unbekannt ist, ansonsten da s Player-Objekt Player-Objek */ public Player authenticatePlayer(String login, String password) throws RemoteException; /** * Diese Methode authentifiziert einen Spielleiter. Der Rückgabewert is t ei ein * GameMaster objekt. objekt * * @param login der Login des Spielleiter s * @param password das Passwort Passwor * @return null wenn der Nutzer unbekannt ist, ansonsten da s GameMaster-Objekt GameMaster-Objek */ public GameMaster authenticateGameMaster(String login, String password) throws RemoteException; /** * Diese Methode erstellt ein neues Spiel. * * @param name der Name des Spiel s * @param nicename der Name des Spiels der auf dem Bildschir m dargestellt wird wir * @param gamemaster das Login des Spielleiters, der das Spiel leite t * @return das Game-Objekt Game-Objek */ public Game createGame(String name, String nicename, String gamemaster) throws RemoteException; /** * Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen . * * @param name der Name des Spiel s */ public void deleteGame(String name name) throws RemoteException; /** * Teilt einem Spiel einen neuen Spielleiter zu . * * @param game der Name des Spiel s * @param gamemaster der Name des neuen Spielleiter s */ public void changeGameMaster(String game, String gamemaster) throws RemoteException; /** * Diese Methode sucht informationen zu einem Spiel und liefert e s * zurück. zurück * * @param name der Name des Spiel s * @return das Game-Objekt oder null wenn das Spiel nicht existier t */ public Game getGame(String name) throws RemoteException; /** * Listet alle Spiele applikationsweit auf. auf * * @return einen Vector der Game-Objekte enthält . */ public Vector listGames() throws RemoteException; /** * Diese Methode erstellt eine Gruppe. Gruppe * * @param name der Name der Grupp e * @param nicename der Name der Gruppe wie er auf dem Bildschir m dargestellt wird wir * @param game der Name des Spiels zu der die Gruppe gehör t */ public Group createGroup(String name, String nicename, String game) throws RemoteException; /** * Diese Methode löscht eine Gruppe und all deren Mitspieler . * * @param name der Name der Grupp e * @param game der Name des SPiels zu dem die Gruppe gehör t */ public void deleteGroup(String name, String game) throws RemoteException; /** * Diese Methode sucht Informationen zu einer Gruppe und liefert ei n * Group-Objekt zurück. zurück * * @param name der Name der Grupp e * @param game der Name des Spiels zu dem die Gruppe gehör t * @return null, wenn die Gruppe nicht gefunden wurde, sonst da s Group-Objekt Group-Objek */ public Group getGroup(String name, String game) throws RemoteException; /** * Listet alle Gruppen in einem Spiel auf . * * @param game das Spiel Spie * @return einen Vector, der Group-Objekte enthäl t */ public Vector listGroups(String game) throws RemoteException; /** * Listet alle Gruppen applikationsweit auf. auf * * @return einen Vector, der Group-Objekte enthäl t */ public Vector listGroups() throws RemoteException; /** * Diese Methode erstellt einen neuen Spieler. * * @param login der login des Spieler s * @param name der Name des Spieler s * @param emael die E-Mail-Addresse des Spieler s * @param group der Name der Gruppe zu der der Spieler gehör t * @param game der Name des Spiels zu dem die Gruppe gehör t * @param language das 2-Buchstaben-Kürzel der Sprache in der de r Spieler die Webseiten anschaut anschau */ public Player createPlayer(String login, String name, String email, String group, String game, String language) throws RemoteException; /** * Diese Methode ändert das Passwort eines Spielers . * * @param login der Login des Spieler s * @param password das neue Passwort des Spieler s */ public void changePlayerPassword(String login, String password) throws RemoteException; /** * Diese Methode ändert die Sprache eines Spielers . * * @param login der Login des Spieler s * @param language die neue Sprache des Spieler s */ public void changePlayerLanguage(String login, String password) throws RemoteException; /** * Ändert die Email-Addresse eines Spielers. Spielers * * @param login der Login des Spieler s * @param email die neue Email des Spieler s */ public void changePlayerEmail(String login, String email) throws RemoteException; /** * Löscht einen Spieler. Spieler * * @param login der login des Spielers . */ public void deletePlayer(String login) throws RemoteException; /** * Sucht informationen über einen Spieler und liefert sie in Form eine s * Player-Objektes zurück. zurück * * @param login der login des Spieler s */ public Player getPlayer(String login) throws RemoteException; /** * Listet alle Spieler einer Gruppe auf . * * @param group die Gruppe Grupp * @param game das Spiel Spie * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers(String group, String game) throws RemoteException; /** * Listet alle Spieler eines Spiels auf . * * @param game das Spiel Spie * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers(String game) throws RemoteException; /** * Listet alle Spieler applikationsweit auf. auf * * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers() throws RemoteException; /** * Fügt einen GameMaster hinzu hinz * * @param login der Login des neuen GameMaster s * @param email die Email des neuen GameMaster s * @param name der Name des neuen GameMaster s * @param language die Sprache des neuen GameMaster s * @return den neuen GameMaster oder nul l */ public GameMaster createGameMaster(String login, String email, String name, String language) throws RemoteException; /** * Löscht einen GameMaster GameMaste * * @param login der Login des GameMasters der gelöscht werden sol l */ public void deleteGameMaster(String login) throws RemoteException; /** * Generiert eine Liste der GameMasters GameMaster * * @return einen Vector mit GameMaster-Objekten GameMaster-Objekte */ public Vector listGameMasters() throws RemoteException; /** * Ändert das Passwort eines Spielleiters. Spielleiters * * @param login der Login des Spielleiter s * @param password das neue Passwort des Spielleiter s */ public void changeGameMasterPassword(String login, String password) throws RemoteException; /** * Ändert die Email Adresse eines Spielleiters . * * @param login der Login des Spielleiter s * @param email die neue Email des Spielleiter s */ public void changeGameMasterEmail(String login, String email) throws RemoteException; /** * Ändert die Sprache eines Spielleiters. Spielleiters * * @param login der Login des Spielleiter s * @param language die neue Sprache des Spielleiter s */ public void changeGameMasterLanguage(String login, String language) throws RemoteException; /** * Liest einen Spielleiter aus der Datenban k * * @param login der Login des Spielleiter s */ public GameMaster getGameMaster(String login) throws RemoteException; /** * Verschickt ein Mail Template an alle Mitglieder eines Spiel s * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param game der Name des Spiels * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGameMail(String snd_email, String snd_name, String game, String template, Hashtable strings) throws RemoteException; /** * Verschickt ein Mail Template an alle Mitlieder einer Grupp e * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param game der Name des Spiels * @param group der Name der Gruppe * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGroupMail(String snd_email, String snd_name, String game, String group, String template, Hashtable strings) throws RemoteException; /** * Verschickt ein Mail Template an einen Spiele r * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param login Login des Spielers Spieler * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendPlayerMail(String snd_email, String snd_name, String login, String template, Hashtable strings) throws RemoteException; /** * Verschickt ein Mail Template an einen SpielLeite r * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param login Login des Spielleiters Spielleiter * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGameMasterMail(String snd_email, String snd_name, String login, String template, Hashtable strings) throws RemoteException; /** * Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ih m sein Passwort mit. mit * * @param login der Login des Spielleiters oder Spieler s */ public void sendPasswordMail(String login) throws RemoteException; } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.GameServerImpl mm.services.game.GameServerImpl public class GameServerImpl implements GameServer Implementation des Gameservers. Im großen und ganzen nur 1.000de Datenbank-queries Version: 1.0 Author: Stefan Zier WEBMASTER_EMAIL WEBMASTER_NAME GameServerImpl() Konstruktor. authenticateGameMaster(String, String) Diese Methode authentifiziert einen Spielleiter. authenticatePlayer(String, String) Diese Methode dient dazu einen Spieler zu authentifizieren. changeGameMaster(String, String) Teilt einem Spiel einen neuen Spielleiter zu. changeGameMasterEmail(String, String) Ändert die Email Adresse eines Spielleiters. changeGameMasterLanguage(String, String) Ändert die Sprache eines Spielleiters. changeGameMasterPassword(String, String) Ändert das Passwort eines Spielleiters. changePlayerEmail(String, String) Ändert die Email-Addresse eines Spielers. changePlayerLanguage(String, String) Diese Methode ändert die Sprache eines Spielers. changePlayerPassword(String, String) Diese Methode ändert das Passwort eines Spielers. createGame(String, String, String) Diese Methode erstellt ein neues Spiel. createGameMaster(String, String, String, String) Fügt einen GameMaster hinzu createGroup(String, String, String) Diese Methode erstellt eine Gruppe. createPlayer(String, String, String, String, String, String) Diese Methode erstellt einen neuen Spieler. deleteGame(String) Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen. deleteGameMaster(String) Löscht einen GameMaster deleteGroup(String, String) Diese Methode löscht eine Gruppe und all deren Mitspieler. deletePlayer(String) Löscht einen Spieler. getGame(String) Diese Methode sucht informationen zu einem Spiel und liefert es zurück. getGameMaster(String) Liest einen Spielleiter aus der Datenbank getGroup(String, String) Diese Methode sucht Informationen zu einer Gruppe und liefert ein Group-Objekt zurück. getPlayer(String) Sucht informationen über einen Spieler und liefert sie in Form eines Player-Objektes zurück. isAlive() Gibt true zurück wenn alles mit dem FileServer in Ordnung ist. listGameMasters() Generiert eine Liste der GameMasters listGames() Listet alle Spiele applikationsweit auf. listGroups() Listet alle Gruppen applikationsweit auf. listGroups(String) Listet alle Gruppen in einem Spiel auf. listPlayers() Listet alle Spieler applikationsweit auf. listPlayers(String) Listet alle Spieler eines Spiels auf. listPlayers(String, String) Listet alle Spieler einer Gruppe auf. sendGameMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an alle Mitglieder eines Spiels sendGameMasterMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an einen SpielLeiter sendGroupMail(String, String, String, String, String, Hashtable) Verschickt ein Mail Template an alle Mitlieder einer Gruppe sendPasswordMail(String) Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ihm sein Passwort mit. sendPlayerMail(String, String, String, String, Hashtable) Verschickt ein Mail Template an einen Spieler serviceIDNotify(ServiceID) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. WEBMASTER_EMAIL public final java.lang.String WEBMASTER_EMAIL WEBMASTER_NAME public final java.lang.String WEBMASTER_NAME GameServerImpl public GameServerImpl() throws java.rmi.RemoteException, java.sql.SQLException, java.io.IO auf. Konstruktor. Baut Verbindungen zur Datenbank und zum Mailserver authenticatePlayer public Player authenticatePlayer(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExceptio Diese Methode dient dazu einen Spieler zu authentifizieren. Sie liefert als Rückgabewert ein Player Objekt, mit dem alle Informationen zu einem Spieler zugänglich sind. Setzt ggf. FirstLogin auf false. Parameters: login - Der username password - Das Passwort Returns: null wenn der Nutzer unbekannt ist, ansonsten das Player-Objekt authenticateGameMaster public GameMaster authenticateGameMaster(java.lang.String login, java.lang.String password) throws java.rmi.Remote Diese Methode authentifiziert einen Spielleiter. Der Rückgabewert ist ein GameMaster objekt. Parameters: login - der Login des Spielleiters password - das Passwort Returns: null wenn der Nutzer unbekannt ist, ansonsten das GameMasterObjekt createGame public Game createGame(java.lang.String name, java.lang.String nicename, java.lang.String gamemaster) throws java.rmi.RemoteException Diese Methode erstellt ein neues Spiel. Parameters: name - der Name des Spiels nicename - der Name des Spiels der auf dem Bildschirm dargestellt wird gamemaster - das Login des Spielleiters, der das Spiel leitet Returns: das Game-Objekt deleteGame public void deleteGame(java.lang.String name) throws java.rmi.RemoteException Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen. Parameters: name - der Name des Spiels changeGameMaster public void changeGameMaster(java.lang.String game, java.lang.String gamemaster) throws java.rmi.RemoteException Teilt einem Spiel einen neuen Spielleiter zu. Parameters: game - der Name des Spiels gamemaster - der Name des neuen Spielleiters getGame public Game getGame(java.lang.String name) throws java.rmi.RemoteException Diese Methode sucht informationen zu einem Spiel und liefert es zurück. Parameters: name - der Name des Spiels Returns: das Game-Objekt oder null wenn das Spiel nicht existiert listGames public java.util.Vector listGames() throws java.rmi.RemoteException Listet alle Spiele applikationsweit auf. Returns: einen Vector der Game-Objekte enthält. createGroup public Group createGroup(java.lang.String name, java.lang.String nicename, java.lang.String game) throws java.rmi.RemoteException Diese Methode erstellt eine Gruppe. Parameters: name - der Name der Gruppe nicename - der Name der Gruppe wie er auf dem Bildschirm dargestellt wird game - der Name des Spiels zu der die Gruppe gehört deleteGroup public void deleteGroup(java.lang.String name, java.lang.String game) throws java.rmi.RemoteException Diese Methode löscht eine Gruppe und all deren Mitspieler. Parameters: name - der Name der Gruppe game - der Name des SPiels zu dem die Gruppe gehört getGroup public Group getGroup(java.lang.String name, java.lang.String game) throws java.rmi.RemoteException Diese Methode sucht Informationen zu einer Gruppe und liefert ein Group-Objekt zurück. Parameters: name - der Name der Gruppe game - der Name des Spiels zu dem die Gruppe gehört Returns: null, wenn die Gruppe nicht gefunden wurde, sonst das GroupObjekt listGroups public java.util.Vector listGroups(java.lang.String game) throws java.rmi.RemoteException Listet alle Gruppen in einem Spiel auf. Parameters: game - das Spiel Returns: einen Vector, der Group-Objekte enthält listGroups public java.util.Vector listGroups() throws java.rmi.RemoteException Listet alle Gruppen applikationsweit auf. Returns: einen Vector, der Group-Objekte enthält createPlayer public Player createPlayer(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String login, name, email, group, game, language) throws java.rmi.RemoteException Diese Methode erstellt einen neuen Spieler. Parameters: login - der login des Spielers name - der Name des Spielers email - die E-Mail-Addresse des Spielers group - der Name der Gruppe zu der der Spieler gehört game - der Name des Spiels zu dem die Gruppe gehört language - das 2-Buchstaben-Kürzel der Sprache in der der Spieler die Webseiten anschaut changePlayerPassword public void changePlayerPassword(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExceptio Diese Methode ändert das Passwort eines Spielers. Parameters: login - der Login des Spielers password - das neue Passwort des Spielers changePlayerLanguage public void changePlayerLanguage(java.lang.String login, java.lang.String language) throws java.rmi.RemoteExceptio Diese Methode ändert die Sprache eines Spielers. Parameters: login - der Login des Spielers language - die neue Sprache des Spielers changePlayerEmail public void changePlayerEmail(java.lang.String login, java.lang.String email) throws java.rmi.RemoteException Ändert die Email-Addresse eines Spielers. Parameters: login - der Login des Spielers email - die neue Email des Spielers deletePlayer public void deletePlayer(java.lang.String login) throws java.rmi.RemoteException Löscht einen Spieler. Parameters: login - der login des Spielers. getPlayer public Player getPlayer(java.lang.String login) throws java.rmi.RemoteException Sucht informationen über einen Spieler und liefert sie in Form eines Player-Objektes zurück. Parameters: login - der login des Spielers listPlayers public java.util.Vector listPlayers(java.lang.String group, java.lang.String game) throws java.rmi.RemoteException Listet alle Spieler einer Gruppe auf. Parameters: group - die Gruppe game - das Spiel Returns: einen Vector mit Player-Objekten listPlayers public java.util.Vector listPlayers(java.lang.String game) throws java.rmi.RemoteException Listet alle Spieler eines Spiels auf. Parameters: game - das Spiel Returns: einen Vector mit Player-Objekten listPlayers public java.util.Vector listPlayers() throws java.rmi.RemoteException Listet alle Spieler applikationsweit auf. Returns: einen Vector mit Player-Objekten createGameMaster public GameMaster createGameMaster(java.lang.String java.lang.String java.lang.String java.lang.String login, email, name, language) throws java.rmi.RemoteExcept Fügt einen GameMaster hinzu Parameters: login - der Login des neuen GameMasters email - die Email des neuen GameMasters name - der Name des neuen GameMasters language - die Sprache des neuen Spielleiters Returns: den neuen GameMaster oder null deleteGameMaster public void deleteGameMaster(java.lang.String login) throws java.rmi.RemoteException Löscht einen GameMaster Parameters: login - der Login des GameMasters der gelöscht werden soll listGameMasters public java.util.Vector listGameMasters() throws java.rmi.RemoteException Generiert eine Liste der GameMasters Returns: einen Vector mit GameMaster-Objekten changeGameMasterPassword public void changeGameMasterPassword(java.lang.String login, java.lang.String password) throws java.rmi.RemoteExce Ändert das Passwort eines Spielleiters. Parameters: login - der Login des Spielleiters password - das neue Passwort des Spielleiters changeGameMasterEmail public void changeGameMasterEmail(java.lang.String login, java.lang.String email) throws java.rmi.RemoteException Ändert die Email Adresse eines Spielleiters. Parameters: login - der Login des Spielleiters email - die neue Email des Spielleiters changeGameMasterLanguage public void changeGameMasterLanguage(java.lang.String login, java.lang.String language) throws java.rmi.RemoteExce Ändert die Sprache eines Spielleiters. Parameters: login - der Login des Spielleiters language - die neue Sprache des Spielleiters getGameMaster public GameMaster getGameMaster(java.lang.String login) throws java.rmi.RemoteException Liest einen Spielleiter aus der Datenbank Parameters: login - der Login des SPielleiters Returns: den Spielleiter sendGameMail public void sendGameMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String game, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an alle Mitglieder eines Spiels Parameters: snd_email - der Absender der email snd_name - der Name des Absenders game - der Name des Spiels template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendGroupMail public void sendGroupMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String game, java.lang.String group, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an alle Mitlieder einer Gruppe Parameters: snd_email - der Absender der email snd_name - der Name des Absenders game - der Name des Spiels group - der Name der Gruppe template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendPlayerMail public void sendPlayerMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String login, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteException Verschickt ein Mail Template an einen Spieler Parameters: snd_email - der Absender der email snd_name - der Name des Absenders login - Login des Spielers template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendGameMasterMail public void sendGameMasterMail(java.lang.String snd_email, java.lang.String snd_name, java.lang.String login, java.lang.String template, java.util.Hashtable strings) throws java.rmi.RemoteExceptio Verschickt ein Mail Template an einen SpielLeiter Parameters: snd_email - der Absender der email snd_name - der Name des Absenders login - Login des SpielLeiters template - das Template das geschickt werden soll strings - Werte die in die Mail geparst werden sollen oder null wenn keine Werte geparst werden sollen sendPasswordMail public void sendPasswordMail(java.lang.String login) throws java.rmi.RemoteException Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ihm sein Passwort mit. Parameters: login - der Login des Spielleiters oder Spielers serviceIDNotify public void serviceIDNotify(net.jini.core.lookup.ServiceID service_id) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. Parameters: service_id - Service ID isAlive public boolean isAlive() throws java.rmi.RemoteException Gibt true zurück wenn alles mit dem FileServer in Ordnung ist. Returns: true wenn alles mit dem FileServer in Ordnung ist. All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; // Java core imports import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; import java.io.IOException; import java.util.Vector; import java.util.Random; import java.util.Hashtable; import java.util.Properties; import java.util.Enumeration; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; // Database connection imports import com.bitmechanic.sql.ConnectionPoolManager; import com.bitmechanic.sql.ConnectionPool; // Jini extension imports import com.sun.jini.lookup.ServiceIDListener; import net.jini.core.lookup.ServiceID; // MM Project imports import mm.tools.ConfigFile; import mm.tools.Static; import mm.jini.ServiceController; // MM Services imports import mm.services.files.FileServer; import mm.services.mailer.MailServer; /** * Implementation des Gameservers. Im großen und ganzen nur 1.000d e Datenbank-queries Datenbank-querie * * @author Stefan Zier Zie * @version 1.0 1. */ public class GameServerImpl extends UnicastRemoteObject implements GameServer, ServiceIDListener { // name und email für die Passwort-Vergessen-Mails public final String WEBMASTER_EMAIL = "[email protected]"; public final String WEBMASTER_NAME = "System"; // namen der Service-Klassen private final String FILE_SERVER = "mm.services.files.FileServer"; private final String MAIL_SERVER = "mm.services.mailer.MailServer"; // database connections private ConnectionPoolManager cpm = null; private ConnectionPool pool = null; ; // jini service controller private ServiceController service_controller = null; private Random random = null; /** * Konstruktor. Baut Verbindungen zur Datenbank und zum Mailserver auf . */ public GameServerImpl() throws RemoteException, SQLException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException InstantiationException { super(); super ConfigFile configfile = new ConfigFile("f:\\mm\\services\\game\\gameserver.properties"); // create a connection pool manager cpm = new ConnectionPoolManager(120); // get String String String url, username and password from the configuration file url = configfile.get("gameserver.database.URL"); username = configfile.get("gameserver.database.username"); password = configfile.get("gameserver.database.password"); if if(url == null || url.equals("")) { throw new IllegalArgumentException("mm: no database URL given"); } if(username == null || username.equals("")) if { throw new IllegalArgumentException("mm: no database login given"); } if(password == null || password.equals("")) if { throw new IllegalArgumentException("mm: no database password given"); } // add the alias for "our" database cpm.addAlias("gameserver", "org.gjt.mm.mysql.Driver", url, username, cpm password, 120, 300, 120); pool = cpm.getPool("gameserver"); // service controller instantiieren service_controller = new ServiceController(); // anmelden welche Services wir brauchen service_controller.addSearchClass(MAIL_SERVER); service_controller service_controller.addSearchClass(FILE_SERVER); service_controller // Zufallsgenerator initialisieren random = new Random(); } /** * Diese Methode dient dazu einen Spieler zu authentifizieren. Si e * liefert als Rückgabewert ein Player Objekt, mit dem all e Informationen Informatione * zu einem Spieler zugänglich sind. Setzt ggf. FirstLogin auf false . * * @param login Der username usernam * @param password Das Passwort Passwor * @return null wenn der Nutzer unbekannt ist, ansonsten da s Player-Objekt Player-Objek */ public Player authenticatePlayer(String login, String password) throws RemoteException { Player result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, pgroup, language, game, email, first_login FROM player WHERE login="; query += Static.sqlString(login) + " AND password=" + Static.sqlString(password); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String name = rs.getString("name"); String group = rs.getString("group"); String language = rs.getString("language"); String game = rs.getString("game"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); result = new Player(login, name, group, game, email, language, first_login == 1); } // Statement schliessen s.close(); (); // Prüfen ob dies der erste login des Spielers ist if(result.isFirstLogin()) if { // flag in der datenbank auf 0 setzen query = "UPDATE players SET first_login=0 WHERE login=" + Static.sqlString(login); s = conn.createStatement(); s.execute(query); s.close(); } // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Diese Methode authentifiziert einen Spielleiter. Der Rückgabewert is t ei ein * GameMaster objekt. objekt * * @param login der Login des Spielleiter s * @param password das Passwort Passwor * @return null wenn der Nutzer unbekannt ist, ansonsten da s GameMaster-Objekt GameMaster-Objek */ public GameMaster authenticateGameMaster(String login, String password) throws RemoteException { GameMaster result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, email, first_login, language FROM gamemasters WHERE login="; query += Static.sqlString(login) + " AND password=" + Static.sqlString(password); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String name = rs.getString("name"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); String language = rs.getString("language"); // GameMaster erzeugen result = new GameMaster(login, name, email, language, first_login == 1); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Diese Methode erstellt ein neues Spiel. * * @param name der Name des Spiel s * @param nicename der Name des Spiels der auf dem Bildschir m dargestellt wird wir * @param gamemaster das Login des Spielleiters, der das Spiel leite t * @return das Game-Objekt Game-Objek */ public Game createGame(String name, String nicename, String gamemaster) throws RemoteException { // Daten für das Spiel erzeugen String chatroom = name; String to_gm_place = gamemaster + "/to_" + name; String from_gm_place = gamemaster + "/from_" + name; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "INSERT INTO games (name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster) values("; query += Static.sqlString(name); query += ", "; query += Static.sqlString(nicename); query += ", "; query += Static.sqlString(chatroom); query += ", "; query += Static.sqlString(to_gm_place); query += ", "; query += Static.sqlString(from_gm_place); query += ", "; query += Static.sqlString(gamemaster); query += ")"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return new Game(name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster); } /** * Diese Methode löscht ein Spiel und alle teilnehmenden Gruppen . * * @param name der Name des Spiel s */ public void deleteGame(String name name) throws RemoteException { // game holen Game game = getGame(name); // verbindung zum fileserver holen FileServer fs = (FileServer)service_controller.getService(FILE_SERVER); if(fs != null) if { // Places löschen fs.deletePlace(game.getToGameMasterPlace()); fs fs.deletePlace(game.getFromGameMasterPlace()); fs } // Liste der Gruppen holen Vector groups = listGroups(name); // jede Gruppe löschen Enumeration enum = groups.elements(); while(enum.hasMoreElements()) while { // Gruppe holen Group group = (Group) enum.nextElement(); // Gruppe löschen deleteGroup(group.getName(), name); deleteGroup } // Nun noch die Gruppe aus der Datenbank löschen try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "DELETE FROM games WHERE name=" + Static.sqlString(name); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Teilt einem Spiel einen neuen Spielleiter zu . * * @param game der Name des Spiel s * @param gamemaster der Name des neuen Spielleiter s */ public void changeGameMaster(String game, String gamemaster) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE games SET gamemaster=" + Static.sqlString(gamemaster) + "WHERE name=" + Static.sqlString(game); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Diese Methode sucht informationen zu einem Spiel und liefert e s * zurück. zurück * * @param name der Name des Spiel s * @return das Game-Objekt oder null wenn das Spiel nicht existier t */ public Game getGame(String name) throws RemoteException { Game result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT nicename, chatroom, to_gm_place, from_gm_place, gamemaster FROM games WHERE name="; query += Static.sqlString(name); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String nicename = rs.getString("nicename"); String chatroom = rs.getString("chatroom"); String to_gm_place = rs.getString("to_gm_place"); String from_gm_place = rs.getString("from_gm_place"); String gamemaster = rs.getString("gamemaster"); // Game erstellen result = new Game(name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Listet alle Spiele applikationsweit auf. auf * * @return einen Vector der Game-Objekte enthält . */ public Vector listGames() throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster FROM games ORDER BY games"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen while(rs.next()) while { String name = rs.getString("name"); String nicename = rs.getString("nicename"); String chatroom = rs.getString("chatroom"); String to_gm_place = rs.getString("to_gm_place"); String from_gm_place = rs.getString("from_gm_place"); String gamemaster = rs.getString("gamemaster"); // Game erstellen Game game = new Game(name, nicename, chatroom, to_gm_place, from_gm_place, gamemaster); result.add(game); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Diese Methode erstellt eine Gruppe. Gruppe * * @param name der Name der Grupp e * @param nicename der Name der Gruppe wie er auf dem Bildschir m dargestellt wird wir * @param game der Name des Spiels zu der die Gruppe gehör t */ public Group createGroup(String name, String nicename, String game) throws RemoteException { String group_place = game + "/" + name; String master_place = game + "/" + name + "to_gm"; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "INSERT INTO pgroups (name, nicename, game, group_place, master_place) values("; query += Static.sqlString(name); query += ", "; query += Static.sqlString(nicename); query query query query query query query += += += += += += += ", "; Static.sqlString(game); ", "; Static.sqlString(group_place); ", "; Static.sqlString(master_place); ")"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return new Group(name, nicename, game, group_place, master_place); } /** * Diese Methode löscht eine Gruppe und all deren Mitspieler . * * @param name der Name der Grupp e * @param game der Name des SPiels zu dem die Gruppe gehör t */ public void deleteGroup(String name, String game) throws RemoteException { // gruppe holen Group group = getGroup(name, game); // verbindung zum fileserver holen FileServer fs = (FileServer)service_controller.getService(FILE_SERVER); if if(fs != null) { // Places löschen fs.deletePlace(group.getGroupPlace()); fs fs.deletePlace(group.getMasterPlace()); fs } // Mitspieler der Gruppe holen Vector players = listPlayers(name, game); Enumeration enum = players.elements(); while(enum.hasMoreElements()) while { Player player = (Player) enum.nextElement(); // Spieler löschen deletePlayer(player.getLogin()); deletePlayer } // Gruppe aus der Datenbank löschen try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "DELETE FROM pgroups WHERE name=" + Static.sqlString(name) + " AND game=" + Static.sqlString(game); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Diese Methode sucht Informationen zu einer Gruppe und liefert ei n * Group-Objekt zurück. zurück * * @param name der Name der Grupp e * @param game der Name des Spiels zu dem die Gruppe gehör t * @return null, wenn die Gruppe nicht gefunden wurde, sonst da s Group-Objekt Group-Objek */ public Group getGroup(String name, String game) throws RemoteException { Group result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT nicename, group_place, master_place FROM pgroups WHERE name="; ; query += Static.sqlString(name) + " AND game=" + Static.sqlString(game); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String nicename = rs.getString("nicename"); String group_place = rs.getString("group_place"); String master_place = rs.getString("master_place"); // Group anlegen result = new Group(name, nicename, game, group_place, master_place); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Listet alle Gruppen in einem Spiel auf . * * @param game das Spiel Spie * @return einen Vector, der Group-Objekte enthäl t */ public Vector listGroups(String game) throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, nicename, group_place, master_place FROM pgroups WHERE game="; ; query += Static.sqlString(game); query += " ORDER BY name"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen while(rs.next()) while { String name = rs.getString("name"); String nicename = rs.getString("nicename"); String group_place = rs.getString("group_place"); String master_place = rs.getString("master_place"); // Group anlegen Group group = new Group(name, nicename, game, group_place, master_place); result.addElement(group); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; ; } /** * Listet alle Gruppen applikationsweit auf. auf * * @return einen Vector, der Group-Objekte enthäl t */ public Vector listGroups() throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, nicename, game, group_place, master_place FROM pgroups ORDER BY name"; ; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen while(rs.next()) while { String name = rs.getString("name"); String nicename = rs.getString("nicename"); String game = rs.getString("game"); String group_place = rs.getString("group_place"); String master_place = rs.getString("master_place"); // Group anlegen Group group = new Group(name, nicename, game, group_place, master_place); result.addElement(group); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; ; } /** * Diese Methode erstellt einen neuen Spieler. * * @param login der login des Spieler s * @param name der Name des Spieler s * @param email die E-Mail-Addresse des Spieler s * @param group der Name der Gruppe zu der der Spieler gehör t * @param game der Name des Spiels zu dem die Gruppe gehör t * @param language das 2-Buchstaben-Kürzel der Sprache in der de r Spieler die Webseiten anschaut anschau */ public Player createPlayer(String login, String name, String email, String group, String game, String language) throws RemoteException { // Passwort generieren StringBuffer password = new StringBuffer(); byte[] numbers = new byte[10]; byte random.nextBytes(numbers); random int count = 0; while while(count < 10) { // append the char representation of this number to the buffer String number = (new Integer(numbers[count])).toString(); password.append(number.substring(number.length() - 1)); password count++; count // Striche um die Zahl in Blöcke zu je 5 Zahlen zu Strukturieren if(count == 5) if password.append("-"); password } // Name und Email des Game Masters String gm_name = null; String gm_email = null; try { // Connection holen Connection conn = pool.getConnection(); // erst mal schauen ob der login schon bei den spielleitern existiert String query = "SELECT login FROM gamemasters WHERE login=" + Static.sqlString(login); Statement s = conn.createStatement(); ResultSet rs = s.executeQuery(query); if(rs.next()) if { s.close(); conn.close(); conn return null; } s.close(); // Query zusammenbauen query = "INSERT INTO players (login, password, name, pgroup, language, game, email, first_login) values("; query += Static.sqlString(login); query += ", "; query += Static.sqlString(password.toString()); query += ", "; query += Static.sqlString(name); query += ", "; query += Static.sqlString(group); query += ", "; query += Static.sqlString(language); query += ", "; query += Static.sqlString(game); query += ", "; query += Static.sqlString(email); query += ", 1)"; // Statement erstellen lassen s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Namen und Email des Gamemasters herausfinden query = "SELECT gamemasters.name AS gm_name, gamemasters.email AS gm_email FROM gamemasters, games WHERE gamemasters.login=games.gamemaster AND games.name=" + Static.sqlString(game); s = conn.createStatement(); rs = s.executeQuery(query); if(rs.next()) if { gm_name = rs.getString("gm_name"); gm_email = rs.getString("gm_email"); } s.close(); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } // passwort per mail an den spieler schicken Properties strings = new Properties(); strings.put("password", password); strings strings.put("gm_email", gm_email); strings strings.put("gm_name", gm_name); strings sendPlayerMail(gm_email, gm_name, login, "newplayer-" + language, sendPlayerMail strings); return new Player(login, name, group, game, email, language, true); } /** * Diese Methode ändert das Passwort eines Spielers . * * @param login der Login des Spieler s * @param password das neue Passwort des Spieler s */ public void changePlayerPassword(String login, String password) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE players SET password=" + Static.sqlString(password) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Diese Methode ändert die Sprache eines Spielers . * * @param login der Login des Spieler s * @param language die neue Sprache des Spieler s */ public void changePlayerLanguage(String login, String language) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE players SET language=" + Static.sqlString(language) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Ändert die Email-Addresse eines Spielers. Spielers * * @param login der Login des Spieler s * @param email die neue Email des Spieler s */ public void changePlayerEmail(String login, String email) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE players SET email=" + Static.sqlString(email) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Löscht einen Spieler. Spieler * * @param login der login des Spielers . */ public void deletePlayer(String login) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "DELETE FROM players WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Sucht informationen über einen Spieler und liefert sie in Form eine s * Player-Objektes zurück. zurück * * @param login der login des Spieler s */ public Player getPlayer(String login) throws RemoteException { Player result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, pgroup, language, game, email, first_login FROM players WHERE login="; query += Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String name = rs.getString("name"); String group = rs.getString("group"); String language = rs.getString("language"); String game = rs.getString("game"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); // Player erzeugen result = new Player(login, name, group, game, email, language, first_login == 1); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Listet alle Spieler einer Gruppe auf . * * @param group die Gruppe Grupp * @param game das Spiel Spie * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers(String group, String game) throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT login, name, language, email, first_login FROM players WHERE game="; query += Static.sqlString(game) + " AND pgroup=" + Static.sqlString(group); query += " ORDER BY login"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String login = rs.getString("login"); String name = rs.getString("name"); String language = rs.getString("language"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); // Player erzeugen Player player = new Player(login, name, group, game, email, language, first_login == 1); result.addElement(player); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; ; } /** * Listet alle Spieler eines Spiels auf . * * @param game das Spiel Spie * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers(String game) throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT login, name, pgroup, language, email, first_login FROM players WHERE game="; query += Static.sqlString(game); query += " ORDER BY login"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String login = rs.getString("login"); String name = rs.getString("name"); String group = rs.getString("pgroup"); String language = rs.getString("language"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); // Player erzeugen Player player = new Player(login, name, group, game, email, language, first_login == 1); result.addElement(player); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; ; } /** * Listet alle Spieler applikationsweit auf. auf * * @return einen Vector mit Player-Objekten Player-Objekte */ public Vector listPlayers() throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT login, name, pgroup, game, language, email, first_login FROM players "; query += " ORDER BY login"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen if(rs.next()) if { String login = rs.getString("login"); String name = rs.getString("name"); String group = rs.getString("pgroup"); String game = rs.getString("game"); String language = rs.getString("language"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); // Player erzeugen Player player = new Player(login, name, group, game, email, language, first_login == 1); result.addElement(player); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; ; } /** * Fügt einen GameMaster hinzu hinz * * @param login der Login des neuen GameMaster s * @param email die Email des neuen GameMaster s * @param name der Name des neuen GameMaster s * @param language die Sprache des neuen Spielleiter s * @return den neuen GameMaster oder nul l */ public GameMaster createGameMaster(String login, String email, String name, String language) throws RemoteException { // Passwort generieren StringBuffer password = new StringBuffer(); byte[] numbers = new byte[10]; byte random.nextBytes(numbers); random int count = 0; while while(count < 10) { // append the char representation of this number to the buffer String number = (new Integer(numbers[count])).toString(); password.append(number.substring(number.length() - 1)); password count++; count // Striche um die Zahl in Blöcke zu je 5 Zahlen zu Strukturieren if(count == 5) if password.append("-"); password } try { // Connection holen Connection conn = pool.getConnection(); // erst mal schauen ob der login schon bei den spielleitern existiert String query = "SELECT login FROM players WHERE login=" + Static.sqlString(login); Statement s = conn.createStatement(); ResultSet rs = s.executeQuery(query); if(rs.next()) if { s.close(); conn.close(); conn return null; } s.close(); // Query zusammenbauen query = "INSERT INTO gamemasters (login, password, name, email, first_login, language) values("; query += Static.sqlString(login); query += ", "; query += Static.sqlString(password.toString()); query += ", "; query += Static.sqlString(name); query += ", "; query += Static.sqlString(email); query += ",1 , "; query += Static.sqlString(language); query += ")"; // Statement erstellen lassen s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return new GameMaster(login, name, email, language, true); ); } /** * Löscht einen GameMaster GameMaste * * @param login der Login des GameMasters der gelöscht werden sol l */ public void deleteGameMaster(String login) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "DELETE FROM gamemasters WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Generiert eine Liste der GameMasters GameMaster * * @return einen Vector mit GameMaster-Objekten GameMaster-Objekte */ public Vector listGameMasters() throws RemoteException { Vector result = new Vector(); try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT login, name, email, first_login, language FROM gamemasters"; // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); // ResultSet auslesen while(rs.next()) while { String login = rs.getString("login"); String name = rs.getString("name"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); String language = rs.getString("language"); // GameMaster game_master = new GameMaster(login, name, email, language, first_login==1); result.addElement(game_master); result } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Ändert das Passwort eines Spielleiters. Spielleiters * * @param login der Login des Spielleiter s * @param password das neue Passwort des Spielleiter s */ public void changeGameMasterPassword(String login, String password) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE gamemasters SET password=" + Static.sqlString(password) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Ändert die Email Adresse eines Spielleiters . * * @param login der Login des Spielleiter s * @param email die neue Email des Spielleiter s */ public void changeGameMasterEmail(String login, String email) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE gamemasters SET email=" + Static.sqlString(email) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Ändert die Sprache eines Spielleiters. Spielleiters * * @param login der Login des Spielleiter s * @param language die neue Sprache des Spielleiter s */ public void changeGameMasterLanguage(String login, String language) throws RemoteException { try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "UPDATE gamemasters SET language=" + Static.sqlString(language) + "WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen s.execute(query); // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Liest einen Spielleiter aus der Datenban k * * @param login der Login des SPielleiter s * @return den Spielleiter Spielleite */ public GameMaster getGameMaster(String login) throws RemoteException { GameMaster result = null; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT name, email, first_login, language FROM gamemasters WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); if(rs.next()) if { String name = rs.getString("name"); String email = rs.getString("email"); int first_login = rs.getInt("first_login"); String language = rs.getString("language"); result = new GameMaster(login, name, email, language, first_login == 1); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } return result; } /** * Verschickt ein Mail Template an alle Mitglieder eines Spiel s * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param game der Name des Spiels * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGameMail(String snd_email, String snd_name, String game, String template, Hashtable strings) throws RemoteException { // Liste der Spieler holen Vector players = listPlayers(game); Enumeration enum = players.elements(); while(enum.hasMoreElements()) while { Player player = (Player) enum.nextElement(); sendPlayerMail(snd_email, snd_name, player.getLogin(), template, sendPlayerMail strings); } // Auch den Spielleiter mit einer Mail versehen Game the_game = getGame(game); GameMaster game_master = getGameMaster(the_game.getGameMaster()); sendGameMasterMail(snd_email, snd_name, game_master.getLogin(), sendGameMasterMail template, strings); } /** * Verschickt ein Mail Template an alle Mitlieder einer Grupp e * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param game der Name des Spiels * @param group der Name der Gruppe * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGroupMail(String snd_email, String snd_name, String game, String group, String template, Hashtable strings) throws RemoteException { // Liste der Spieler holen Vector players = listPlayers(game, group); Enumeration enum = players.elements(); while(enum.hasMoreElements()) while { Player player = (Player) enum.nextElement(); sendPlayerMail(snd_email, snd_name, player.getLogin(), template, sendPlayerMail strings); } } /** * Verschickt ein Mail Template an einen Spiele r * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param login Login des Spielers Spieler * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendPlayerMail(String snd_email, String snd_name, String login, String template, Hashtable strings) throws RemoteException { Player player = getPlayer(login); MailServer ms = (MailServer)service_controller.getService(MAIL_SERVER); if(ms != null) if ms.sendTemplate(template + "-" + player.getLanguage(), snd_email, ms snd_name, player.getEmail(), player.getName(), strings); } /** * Verschickt ein Mail Template an einen SpielLeite r * * @param snd_email der Absender der emai l * @param snd_name der Name des Absender s * @param login Login des SpielLeiters SpielLeiter * @param template das Template das geschickt werden sol l * @param strings Werte die in die Mail geparst werden sollen oder nul l wenn * keine Werte geparst werden solle n */ public void sendGameMasterMail(String snd_email, String snd_name, String login, String template, Hashtable strings) throws RemoteException { GameMaster game_master = getGameMaster(login); MailServer ms = (MailServer)service_controller.getService(MAIL_SERVER); if(ms != null) if ms.sendTemplate(template + "-" + game_master.getLanguage(), ms snd_email, snd_name, game_master.getEmail(), game_master.getName(), strings); } /** * Verschickt eine Mail an einen Spielleiter oder Spieler und teilt ih m sein Passwort mit. mit * * @param login der Login des Spielleiters oder Spieler s */ public void sendPasswordMail(String login) throws RemoteException { boolean found = false; try { // Connection holen Connection conn = pool.getConnection(); // Query zusammenbauen String query = "SELECT password, email, name, language FROM players WHERE login=" + Static.sqlString(login); // Statement erstellen lassen Statement s = conn.createStatement(); // Query machen ResultSet rs = s.executeQuery(query); if(rs.next()) if { String password = rs.getString("password"); String email = rs.getString("email"); String name = rs.getString("name"); String language = rs.getString("language"); // found auf true setzen found = true; // mail verschicken Hashtable strings = new Hashtable(); strings.put("password", password); strings sendPlayerMail(WEBMASTER_EMAIL, WEBMASTER_NAME, login, sendPlayerMail "forgotpassword-" + language, strings); } // Statement schliessen s.close(); (); // Connection zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error while using database", sql); } } /** * Diese Methode wird vom Join Manager aufgerufen um dem Service sein e Service ID mitzuteilen mitzuteile * so bald der Service registriert ist . * * @param service_id Service ID I */ public void serviceIDNotify(ServiceID service_id) { System.out.print("Service registered, ID: "); System System.out.println(service_id); System } /** * Gibt true zurück wenn alles mit dem FileServer in Ordnung ist . * * @return true wenn alles mit dem FileServer in Ordnung ist . */ public boolean isAlive() throws RemoteException { // check the database connection try { // kleine Testquery Connection conn = pool.getConnection(); Statement s = conn.createStatement(); ResultSet rs = s.executeQuery("SELECT checkvalue FROM checktable"); if(rs.next()) if { String weltherrschaft = rs.getString("checkvalue"); if(weltherrschaft == null || if !weltherrschaft.equalsIgnoreCase("WELTHERRSCHAFT")) return false; } else return false; s.close(); conn.close(); conn } catch(SQLException sql) catch { return false; } return true; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.Group java.lang.Object | +----mm.services.game.Group public class Group extends java.lang.Object implements java.io.Serializable Diese Klasse speichert alle Informationen zu einer Gruppe wie Name der gruppen- internen Fileplaces, Messageboards und Chatrooms. Version: 1.0 Author: Stefan Zier Group(String, String, String, String, String) Dieser Konstruktor generiert das Objekt. getGame() Liefert das Spiel zu dem die Gruppe gehört. getGroupPlace() Liefert den Place auf dem die Gruppe untereinander Daten austauscht getMasterPlace() Liefert den Place mit dem die Gruppe Daten mit dem Spielleiter austauscht getName() Liefert den Namen der Gruppe. getNiceName() Liefert den Namen der Gruppe zur Darstellung auf dem Bildschirm Group public Group(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String name, nicename, game, group_place, master_place) Dieser Konstruktor generiert das Objekt. Alle informationen werden hier an die Klasse übergeben. Parameters: name - der Name der Gruppe nicename - der Name der Gruppe zur Darstellung auf dem Bildschirm game - das Spiel zu dem die Gruppe gehört group_place - der File-place, der zur Gruppe gehört master_place - der File-place mit dem die Gruppe Dateien mit dem Spielleiter austauscht getName public java.lang.String getName() Liefert den Namen der Gruppe. Returns: den Namen der Gruppe. getNiceName public java.lang.String getNiceName() Liefert den Namen der Gruppe zur Darstellung auf dem Bildschirm Returns: den Namen der Gruppe zur Darstellung auf dem Bildschirm getGame public java.lang.String getGame() Liefert das Spiel zu dem die Gruppe gehört. Returns: das Spiel zu dem die Gruppe gehört. getGroupPlace public java.lang.String getGroupPlace() Liefert den Place auf dem die Gruppe untereinander Daten austauscht Returns: den Place auf dem die Gruppe untereinander Daten austauscht getMasterPlace public java.lang.String getMasterPlace() Liefert den Place mit dem die Gruppe Daten mit dem Spielleiter austauscht Returns: den Place mit dem die Gruppe Daten mit dem Spielleiter austauscht All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; import java.io.Serializable; /** * Diese Klasse speichert alle Informationen zu einer Gruppe wie Name de r gruppengruppen * internen Fileplaces, Messageboards und Chatrooms. Chatrooms * * @author Stefan Zier Zie * @version 1.0 1. */ public class Group implements Serializable { private String name = null; private String nicename = null; private String game = null; private String group_place = null; private String master_place = null; /** * Dieser Konstruktor generiert das Objekt. Alle informationen werde n hier * an die Klasse übergeben. übergeben * * @param name der Name der Grupp e * @param nicename der Name der Gruppe zur Darstellung auf de m Bildschirm Bildschir * @param game das Spiel zu dem die Gruppe gehör t * @param group_place der File-place, der zur Gruppe gehör t * @param master_place der File-place mit dem die Gruppe Dateien mit de m * Spielleiter austauscht austausch */ public Group(String name, String nicename, String game, String group_place, String master_place) { this.name = name; this this.nicename = nicename; this this.game = game; this this.group_place = group_place; this this.master_place = master_place; this } /** * Liefert den Namen der Gruppe. Gruppe * * @return den Namen der Gruppe. Gruppe */ public String getName() { return name; } /** * Liefert den Namen der Gruppe zur Darstellung auf dem Bildschir m * * @return den Namen der Gruppe zur Darstellung auf dem Bildschir m */ public String getNiceName() { return nicename; } /** * Liefert das Spiel zu dem die Gruppe gehört . * * @return das Spiel zu dem die Gruppe gehört . */ public String getGame() { return game; } /** * Liefert den Place auf dem die Gruppe untereinander Daten austausch t * * @return den Place auf dem die Gruppe untereinander Daten austausch t */ public String getGroupPlace() { return group_place; } /** * Liefert den Place mit dem die Gruppe Daten mit dem Spielleite r austauscht austausch * * @return den Place mit dem die Gruppe Daten mit dem Spielleite r austauscht austausch */ public String getMasterPlace() { return master_place; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.Player java.lang.Object | +----mm.services.game.Player public class Player extends java.lang.Object implements java.io.Serializable Diese Klasse enthält alle Informationen über einen Mitspieler des Planspiels. Sie wird vom Spielserver verwaltet. Der Webserver greift darauf über RMI zu. Version: 1.0 Author: Stefan Zier Player(String, String, String, String, String, String, boolean) Konstruktor. getEmail() Accessor für getGame() Accessor für getGroup() Accessor für getLanguage() Accessor für getLogin() Accessor für getName() Accessor für isFirstLogin() Accessor für email. das Spiel die Gruppe Sprache. Login den Namen First Login. Player public Player(java.lang.String login, java.lang.String name, java.lang.String group, java.lang.String game, java.lang.String email, java.lang.String language, boolean first_login) Konstruktor. Parameters: login - der Login des Spielers name - der Name des Spielers group - die Gruppe zu der der Spieler gehört game - das Spiel an dem der Spieler teilnimmt email - die Email-Addresse des Spielers language - die Sprache in der der Spieler die Webseiten betrachten will getLogin public java.lang.String getLogin() Accessor für Login Returns: den Login des Spielers getName public java.lang.String getName() Accessor für den Namen Returns: den Namen des Spielers getGroup public java.lang.String getGroup() Accessor für die Gruppe Returns: die Gruppe des Spielers getGame public java.lang.String getGame() Accessor für das Spiel Returns: den Namen des Spiels an dem der Spieler teilnimmt getEmail public java.lang.String getEmail() Accessor für email. Returns: die Email Adresse des Spielers getLanguage public java.lang.String getLanguage() Accessor für Sprache. Returns: die Sprache in der der Spieler die Webseiten betrachten will. isFirstLogin public boolean isFirstLogin() Accessor für First Login. Returns: true wenn sich der Spieler zum ersten mal einloggt. All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; import java.io.Serializable; /** * Diese Klasse enthält alle Informationen über einen Mitspieler de s Planspiels. Planspiels * Sie wird vom Spielserver verwaltet. Der Webserver greift darauf über RM I zu. zu * * @author Stefan Zier Zie * @version 1.0 1. */ public class Player implements Serializable { private String login = null; private String name = null; private String group = null; private String game = null; private String email = null; private String language = null; private boolean first_login; /** * Konstruktor. Konstruktor * * @param login der Login des Spieler s * @param name der Name des Spieler s * @param group die Gruppe zu der der Spieler gehör t * @param game das Spiel an dem der Spieler teilnimm t * @param email die Email-Addresse des Spieler s * @param language die Sprache in der der Spieler die Webseite n betrachten will wil * @paran first_login wahr wenn sich der Spieler zum ersten ma l eingeloggt hat ha */ public Player(String login, String name, String group, String game, String email, String language, boolean first_login) { this.login = login; this this.name = name; this this.group = group; this this.game = game; this this.email = email; this this.language = language; this this.first_login = first_login; this } /** * Accessor für Login Logi * * @return den Login des Spielers Spieler */ public String getLogin() { return login; } /** * Accessor für den Namen Name * * @return den Namen des Spielers Spieler */ public String getName() { return name; } /** * Accessor für die Gruppe Grupp * * @return die Gruppe des Spielers Spieler */ public String getGroup() { return group; } /** * Accessor für das Spiel Spie * * @return den Namen des Spiels an dem der Spieler teilnimm t */ public String getGame() { return game; } /** * Accessor für email. email * * @return die Email Adresse des Spieler s */ public String getEmail() { return email; } /** * Accessor für Sprache. Sprache * * @return die Sprache in der der Spieler die Webseiten betrachten will . */ public String getLanguage() { return language; } /** * Accessor für First Login. Login * * @return true wenn sich der Spieler zum ersten mal einloggt . */ public boolean isFirstLogin() { return first_login; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.game.RegisterGameServer java.lang.Object | +----mm.services.game.RegisterGameServer public class RegisterGameServer extends java.lang.Object Diese Klasse instantiiert den Game server und registriert ihn beim Lookup Service. Version: 1.0 Author: Stefan Zier RegisterGameServer() main(String[]) Main methode. RegisterGameServer public RegisterGameServer() main public static void main(java.lang.String argsv[]) Main methode. All Packages Class Hierarchy This Package Previous Next Index package mm.services.game; // Java core imports import java.rmi.*; // Jini extension imports import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; import net.jini.lookup.entry.Name; import net.jini.lookup.entry.ServiceInfo; import net.jini.core.entry.Entry; /** * Diese Klasse instantiiert den Game server und registriert ihn bei m Lookup Service. Service * * @author Stefan Zier Zie * @version 1.0 1. */ public class RegisterGameServer { static JoinManager jm; static GameServerImpl gameserver_instance; /** * Main methode. */ public static void main(String[] argsv) { System.out.println("RegisterGameServer (c) 1999 MM Jahresprojekt"); System try { // install security manager so that we'll be able to talk to server System.out.println(">> installiere RMI Security manager..."); System System.setSecurityManager(new RMISecurityManager()); System // create service System.out.println(">> instantiiere Game Server ..."); System gameserver_instance = new GameServerImpl(); // this name will be seen to users that browse services Name nameEntry = new Name("MM Game Server"); // some addtl. serivice information such as manufacterer etc. ServiceInfo serviceInfo = new ServiceInfo("MM Game Server", "CE5 WS 1999/2000", "CE5 WS 1999/2000", "1.0", "", ""); // gather all information that we're going to pass on to the lookup service Entry entries[] = new Entry[] {nameEntry, serviceInfo}; System.out.println(">> registriere den Game Server beim Lookup System Service..."); // create a new join manager and have it register the jts_instance. jm = new JoinManager(gameserver_instance, entries, (ServiceIDListener) gameserver_instance, new LeaseRenewalManager()); System.out.println(">> Game Server bereit"); System } catch(Exception e) catch { System.out.println("##### mm: exception caught: "); System e.printStackTrace(); } } } e.) mm.services.mailer Dieses Package enthält Klassen des Mailer-Dienstes. Klasse MailServer MailServerImpl RegisterMailServer Bedeutung Das Remote-Interface des Mailer-Dienstes Die Implementation des Mailer-Dienstes Instantiiert einen Spielmanager-Dienst und registriert ihn beim Lookup Service All Packages Class Hierarchy This Package Previous Next Index Interface mm.services.mailer.MailServer public interface MailServer extends java.rmi.Remote, AliveCheckable Dies ist das Interface zu einem Mailserver der Mails verschicken kann. Version: 1.0 Author: Stefan Zier sendMail(String, String, String, String, String, String) Versendet eine Mail. sendTemplate(String, String, String, String, String, Hashtable) Versendet ein Mail Template aus der Datenbank. sendMail public void sendMail(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String snd_email, snd_name, rcp_email, rcp_name, subject, body) throws java.rmi.RemoteException Versendet eine Mail. Parameters: snd_email - die email-addresse des absenders snd_name - der name des absenders rcp_email - die email-addresse des empfängers rcp_name - der name des empfängers subject - das Subject der email body - der Körper der email sendTemplate public void sendTemplate(java.lang.String template, java.lang.String snd_email, java.lang.String snd_name, java.lang.String rcp_email, java.lang.String rcp_name, java.util.Hashtable strings) throws java.rmi.RemoteException Versendet ein Mail Template aus der Datenbank. Parameters: template - der Namen des Templates snd_email - die email-addresse des absenders snd_name - der name des absenders rcp_email - die email-addresse des empfängers rcp_name - der name des empfängers strings - Strings die in dem Template in der Form angegeben werden können. In der Hashtable müssen Wertepaare in mit tagname als Schlüssel und dem Wert der an Stelle des Tags eingesetzt werden soll stehen. Vordefinierte Tags sind: die Email des Absenders der Name des Absenders die Email des Empfängers der Name des Empfängers All Packages Class Hierarchy This Package Previous Next Index package mm.services.mailer; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Hashtable; import mm.tools.AliveCheckable; /** * Dies ist das Interface zu einem Mailserver der Mails verschicken kann . * * @author Stefan Zier Zie * @version 1.0 1. */ public interface MailServer extends Remote, AliveCheckable { /** * Versendet eine Mail. * * @param snd_email die email-addresse des absender s * @param snd_name der name des absender s * @param rcp_email die email-addresse des empfänger s * @param rcp_name der name des empfänger s * @param subject das Subject der emai l * @param body der Körper der emai l */ public void sendMail(String snd_email, String snd_name, String rcp_email, String rcp_name, String subject, String body) throws RemoteException; /** * Versendet ein Mail Template aus der Datenbank . * * @param template der Namen des Template s * @param snd_email die email-addresse des absender s * @param snd_name der name des absender s * @param rcp_email die email-addresse des empfänger s * @param rcp_name der name des empfänger s * @param strings Strings die in dem Template in der Form <tagname > * angegeben werden können. In der Hashtable müssen Wertepaare i n * mit tagname als Schlüssel und dem Wert der an Stelle des Tag s * eingesetzt werden soll stehen. Vordefinierte Tags sind : * <snd_email> die Email des Absender s * <snd_name> der Name des Absender s * <rcp_email> die Email des Empfänger s * <rcp_name> der Name des Empfänger s */ public void sendTemplate(String template, String snd_email, String snd_name, String rcp_email, String rcp_name, Hashtable strings) throws RemoteException; } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.mailer.MailServerImpl mm.services.mailer.MailServerImpl public class MailServerImpl implements MailServer Dies ist die Implementation des Mail Servers. Er benutzt die Mail API von Sun. Version: 1.0 Author: Stefan Zier MailServerImpl() Konstruktor. isAlive() Liefert true wenn alles in Ordnung ist mit dem Mailserver. sendMail(String, String, String, String, String, String) Versendet eine Mail. sendTemplate(String, String, String, String, String, Hashtable) Versendet ein Mail Template aus der Datenbank. serviceIDNotify(ServiceID) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. MailServerImpl public MailServerImpl() throws java.rmi.RemoteException, java.io.IOException, java.sql.SQL Konstruktor. Liest die Konfigurationsdatei mailserver.properties und bereitet den Versand der Mails vor. sendMail public void sendMail(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String snd_email, snd_name, rcp_email, rcp_name, subject, body) throws java.rmi.RemoteException Versendet eine Mail. Parameters: snd_email - die email-addresse des absenders snd_name - der name des absenders rcp_email - die email-addresse des empfängers rcp_name - der name des empfängers subject - das Subject der email body - der Körper der email sendTemplate public void sendTemplate(java.lang.String template, java.lang.String snd_email, java.lang.String snd_name, java.lang.String rcp_email, java.lang.String rcp_name, java.util.Hashtable strings) throws java.rmi.RemoteException Versendet ein Mail Template aus der Datenbank. Parameters: template - der Namen des Templates snd_email - die email-addresse des absenders snd_name - der name des absenders rcp_email - die email-addresse des empfängers rcp_name - der name des empfängers strings - Strings die in dem Template in der Form angegeben werden können. In der Hashtable müssen Wertepaare in mit tagname als Schlüssel und dem Wert der an Stelle des Tags eingesetzt werden soll stehen. Vordefinierte Tags sind: die Email des Absenders der Name des Absenders die Email des Empfängers der Name des Empfängers isAlive public boolean isAlive() throws java.rmi.RemoteException Liefert true wenn alles in Ordnung ist mit dem Mailserver. Returns: true wenn alles in Ordnung ist mit dem Mailserver. serviceIDNotify public void serviceIDNotify(net.jini.core.lookup.ServiceID service_id) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. Parameters: service_id - Service ID All Packages Class Hierarchy This Package Previous Next Index package mm.services.mailer; // Java Core imports import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; import java.io.IOException; import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; import java.util.Properties; import java.util.Hashtable; import java.util.Enumeration; // Database connection imports import com.bitmechanic.sql.ConnectionPoolManager; import com.bitmechanic.sql.ConnectionPool; // Mail extensions imports import javax.mail.Session; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Transport; import javax.mail.internet.MimeMessage; import javax.mail.internet.InternetAddress; import javax.mail.internet.AddressException; // Jini extension imports import com.sun.jini.lookup.ServiceIDListener; import net.jini.core.lookup.ServiceID; // MM imports import mm.tools.Static; import mm.tools.ConfigFile; /** * Dies ist die Implementation des Mail Servers. Er benutzt die Mail AP I von Sun. Sun * * @author Stefan Zier Zie * @version 1.0 1. */ public class MailServerImpl extends UnicastRemoteObject implements MailServer, ServiceIDListener { // database connections private ConnectionPoolManager cpm = null; private ConnectionPool pool = null; // mail session private javax.mail.Session mail_session = null; /** * Konstruktor. Liest die Konfigurationsdatei mailserver.properties un d bereitet den de * Versand der Mails vor. vor */ public MailServerImpl() throws RemoteException, IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException { // Konstruktor von UnicastRemoteObject aufrufen super(); super // Mailserver aus der Konfigurationsdatei lesen ConfigFile configfile = new ConfigFile("F:/mm/services/mailer/mailserver.properties"); String smtp = configfile.get("mailserver.smtp"); if(Static.empty(smtp)) if throw new IllegalArgumentException("mm: the mailserver has not been specified"); // Properties für die Mailsession festlegen Properties mail_props = new Properties(); mail_props.put("mail.smtp.host", smtp); mail_props // Mail session anlegen mail_session = javax.mail.Session.getInstance(mail_props, null); // create a connection pool manager cpm = new ConnectionPoolManager(120); // get String String String url, username and password from the configuration file url = configfile.get("mailserver.database.URL"); username = configfile.get("mailserver.database.username"); password = configfile.get("mailserver.database.password"); if if(url == null || url.equals("")) { throw new IllegalArgumentException("mm: no database URL given"); } if(username == null || username.equals("")) if { throw new IllegalArgumentException("mm: no database login given"); } if(password == null || password.equals("")) if { throw new IllegalArgumentException("mm: no database password given"); } // add the alias for "our" database cpm.addAlias("mailserver", "org.gjt.mm.mysql.Driver", url, username, cpm password, 120, 300, 120); pool = cpm.getPool("mailserver"); } /** * Versendet eine Mail. * * @param snd_email die email-addresse des absender s * @param snd_name der name des absender s * @param rcp_email die email-addresse des empfänger s * @param rcp_name der name des empfänger s * @param subject das Subject der emai l * @param body der Körper der emai l */ public void sendMail(String snd_email, String snd_name, String rcp_email, String rcp_name, String subject, String body) throws RemoteException { // neue Mail erzeugen Message msg = new MimeMessage(mail_session); // Empfängeraddressen in richtige Form bringen InternetAddress[] rcps = new InternetAddress[1]; InternetAddress try { rcps[0] = new InternetAddress(rcp_email); rcps } catch(AddressException a) catch { throw new RemoteException("mm: invalid recipient mail address", a); } try { // empfänger festlegen msg.setRecipients(Message.RecipientType.TO, rcps); msg // absender festlegen msg.setFrom(new InternetAddress(snd_email)); msg // subject festlegen msg.setSubject(subject); msg // body festlegen msg.setText(body); msg } catch(MessagingException me) catch { throw new RemoteException("mm: error setting email data", me); } try { Transport.send(msg); Transport } catch(MessagingException me) catch { throw new RemoteException("mm: error sending email", me); } } /** * Versendet ein Mail Template aus der Datenbank . * * @param template der Namen des Template s * @param snd_email die email-addresse des absender s * @param snd_name der name des absender s * @param rcp_email die email-addresse des empfänger s * @param rcp_name der name des empfänger s * @param strings Strings die in dem Template in der Form <tagname > * angegeben werden können. In der Hashtable müssen Wertepaare i n * mit tagname als Schlüssel und dem Wert der an Stelle des Tag s * eingesetzt werden soll stehen. Vordefinierte Tags sind : * <snd_email> die Email des Absender s * <snd_name> der Name des Absender s * <rcp_email> die Email des Empfänger s * <rcp_name> der Name des Empfänger s */ public void sendTemplate(String template, String snd_email, String snd_name, String rcp_email, String rcp_name, Hashtable strings) throws RemoteException { try { // conn holen Connection conn = pool.getConnection(); // query bauen String query="SELECT subject, body FROM mail_templates WHERE template=" + Static.sqlString(template); // query machen Statement sGetTemplate = conn.createStatement(); ResultSet rsGetTemplate = sGetTemplate.executeQuery(query); if(rsGetTemplate.next()) if { // record auslesen String subject = rsGetTemplate.getString("template"); String body = rsGetTemplate.getString("body"); // Standardvariablen parsen subject = Static.replace(subject, subject = Static.replace(subject, subject = Static.replace(subject, subject = Static.replace(subject, body body body body = = = = Static.replace(body, Static.replace(body, Static.replace(body, Static.replace(body, "<snd_email>", snd_email); "<snd_name>", snd_name); "<rcp_email>", rcp_email); "<rcp_name>", rcp_name); "<snd_email>", snd_email); "<snd_name>", snd_name); "<rcp_email>", rcp_email); "<rcp_name>", rcp_name); // hashtable durchlaufen und alles parsen Enumeration enum = strings.keys(); while(enum.hasMoreElements()) while { // Key aus der enumeration holen String key = (String) enum.nextElement(); // value aus der Hashtable lesen String value = (String) strings.get(key); // replace in subject und body machen subject = Static.replace(subject, "<" + key + ">", value); body = Static.replace(body, "<" + key + ">", value); } // so, nun die mail wegschicken sendMail(snd_email, snd_name, rcp_email, rcp_name, subject, sendMail body); } else throw new RemoteException("mm: template not found in database"); sGetTemplate.close(); sGetTemplate // conn zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error reading mail template from database", sql); } } /** * Liefert true wenn alles in Ordnung ist mit dem Mailserver . * * @return true wenn alles in Ordnung ist mit dem Mailserver . */ public boolean isAlive() throws RemoteException { return true; } /** * Diese Methode wird vom Join Manager aufgerufen um dem Service sein e Service ID mitzuteilen mitzuteile * so bald der Service registriert ist . * * @param service_id Service ID I */ public void serviceIDNotify(ServiceID service_id) { System.out.print("Service registered, ID: "); System System.out.println(service_id); System } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.mailer.RegisterMailServer java.lang.Object | +----mm.services.mailer.RegisterMailServer public class RegisterMailServer extends java.lang.Object Diese Klasse instantiiert den Mail server und registriert ihn beim Lookup Service. Version: 1.0 Author: Stefan Zier RegisterMailServer() main(String[]) Main methode. RegisterMailServer public RegisterMailServer() main public static void main(java.lang.String argsv[]) Main methode. All Packages Class Hierarchy This Package Previous Next Index package mm.services.mailer; // Java core imports import java.rmi.*; // Jini extension imports import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; import net.jini.lookup.entry.Name; import net.jini.lookup.entry.ServiceInfo; import net.jini.core.entry.Entry; /** * Diese Klasse instantiiert den Mail server und registriert ihn bei m Lookup Service. Service * * @author Stefan Zier Zie * @version 1.0 1. */ public class RegisterMailServer { static JoinManager jm; static MailServerImpl mailserver_instance; /** * Main methode. */ public static void main(String[] argsv) { System.out.println("RegisterMailServer (c) 1999 MM Jahresprojekt"); System try { // install security manager so that we'll be able to talk to server System.out.println(">> installiere RMI Security manager..."); System System.setSecurityManager(new RMISecurityManager()); System // create service System.out.println(">> instantiiere Mail Server ..."); System mailserver_instance = new MailServerImpl(); // this name will be seen to users that browse services Name nameEntry = new Name("MM Mail Server"); // some addtl. serivice information such as manufacterer etc. ServiceInfo serviceInfo = new ServiceInfo("MM Mail Server", "CE5 WS 1999/2000", "CE5 WS 1999/2000", "1.0", "", ""); // gather all information that we're going to pass on to the lookup service Entry entries[] = new Entry[] {nameEntry, serviceInfo}; System.out.println(">> registriere den Mail Server beim Lookup System Service..."); // create a new join manager and have it register the jts_instance. jm = new JoinManager(mailserver_instance, entries, (ServiceIDListener) mailserver_instance, new LeaseRenewalManager()); System.out.println(">> Mail Server bereit"); System } catch(Exception e) catch { System.out.println("##### mm: exception caught: "); System e.printStackTrace(); } } } f.) mm.services.msgboard Dieses Package enthält Klassen des Messageboard-Dienstes. Klasse Message MessageBoardServer MessageBoardServerImpl RegisterMessageBoardServer Bedeutung Stellt eine Nachricht dar Das Remote-Interface des MessageboardDienstes Die Implementation des MessageboardDienstes Instantiiert einen Messageboard-Dienst und registriert ihn beim Lookup Service All Packages Class Hierarchy This Package Previous Next Index Class mm.services.msgboard.Message java.lang.Object | +----mm.services.msgboard.Message public class Message extends java.lang.Object implements java.io.Serializable Dies ist die Repräsentation eine Nachricht in dem Message Board. Version: 1.0 Author: Stefan Zier Message(String, String, String, String, String, Date) Erstellt eine Message. addReply(Message) Fügt eine Antwort hinzu getBoard() Liefert das Board der Nachricht zurück getBody() Liefert den Text der Nachricht zurück getCreated() Liefert das Datum an dem die Nachricht gepostet wurde zurück. getId() Liefert die ID der Nachricht zurück. getReplies() Liefert die Liste der Antworten auf diese Nachricht zurück. getSender() Liefert den Absender der Nachricht zurück. getSubject() Liefert das Subject der Nachricht zurück. hasReplies() Liefert true zurück wenn die Nachricht Antworten hat. Message public Message(java.lang.String id, java.lang.String board, java.lang.String sender, java.lang.String subject, java.lang.String body, java.util.Date created) Erstellt eine Message. Parameters: id - die ID der Message board - das Board der Message sender - der Versender der Nachricht subject - das Subject der Nachricht body - der Text der Nachricht created - das Datum an dem die Nachricht erstellt wurde getId public java.lang.String getId() Liefert die ID der Nachricht zurück. Returns: die ID der Nachricht getBoard public java.lang.String getBoard() Liefert das Board der Nachricht zurück Returns: das Board der Nachricht. getSender public java.lang.String getSender() Liefert den Absender der Nachricht zurück. Returns: den Absender der Nachricht. getSubject public java.lang.String getSubject() Liefert das Subject der Nachricht zurück. Returns: das Subject der Nachricht getBody public java.lang.String getBody() Liefert den Text der Nachricht zurück Returns: den Text der Nachricht. getCreated public java.util.Date getCreated() Liefert das Datum an dem die Nachricht gepostet wurde zurück. Returns: das Datum an dem die Nachricht gepostet wurde getReplies public java.util.Vector getReplies() Liefert die Liste der Antworten auf diese Nachricht zurück. Returns: einen Vector mit Message-Objekten hasReplies public boolean hasReplies() Liefert true zurück wenn die Nachricht Antworten hat. Returns: true wenn die Nachricht antworten hat addReply public void addReply(Message reply) Fügt eine Antwort hinzu Parameters: message - die Antwort All Packages Class Hierarchy This Package Previous Next Index package mm.services.msgboard; import java.util.Date; import java.util.Vector; import java.io.Serializable; /** * Dies ist die Repräsentation eine Nachricht in dem Message Board . * * @author Stefan Zier Zie * @version 1.0 1. */ public class Message implements Serializable { private String id = null; private String board = null; private String sender = null; private String subject = null; private String body = null; private Date created = null; private Vector replies = null; /** * Erstellt eine Message. * * @param id die ID der Message Messag * @param board das Board der Messag e * @param sender der Versender der Nachrich t * @param subject das Subject der Nachrich t * @param body der Text der Nachrich t * @param created das Datum an dem die Nachricht erstellt wurd e */ public Message(String id, String board, String sender, String subject, String body, Date created) { this.id = id; this this.board = board; this this.sender = sender; this this.subject = subject; this this.body = body; this this.created = created; this replies = new Vector(); } /** * Liefert die ID der Nachricht zurück . * * @return die ID der Nachricht Nachrich */ public String getId() { return id; } /** * Liefert das Board der Nachricht zurüc k * * @return das Board der Nachricht. Nachricht */ public String getBoard() { return board; } /** * Liefert den Absender der Nachricht zurück . * * @return den Absender der Nachricht. Nachricht */ public String getSender() { return sender; } /** * Liefert das Subject der Nachricht zurück . * * @return das Subject der Nachricht Nachrich */ public String getSubject() { return subject; } /** * Liefert den Text der Nachricht zurüc k * * @return den Text der Nachricht. Nachricht */ public String getBody() { return body; } /** * Liefert das Datum an dem die Nachricht gepostet wurde zurück . * * @return das Datum an dem die Nachricht gepostet wurd e */ public Date getCreated() { return created; } /** * Liefert die Liste der Antworten auf diese Nachricht zurück . * * @return einen Vector mit Message-Objekten Message-Objekte */ public Vector getReplies() { return replies; } /** * Liefert true zurück wenn die Nachricht Antworten hat . * * @return true wenn die Nachricht antworten ha t */ public boolean hasReplies() { return (replies.size() > 0); } /** * Fügt eine Antwort hinzu hinz * * @param message die Antwort Antwor */ public void addReply(Message reply) { replies.addElement(reply); replies } } All Packages Class Hierarchy This Package Previous Next Index Interface mm.services.msgboard.MessageBoardServer public interface MessageBoardServer extends java.rmi.Remote, AliveCheckable Dies ist das Interface zum Messageboard-Server. Jedes Messageboard wird durch einen eindeutigen String identifiziert. Version: 1.0 Author: Stefan Zier createMessage(String, String, String, String, String) Erstellt eine neue Nachricht in dem angegebenen Messageboard. deleteMessage(String, String) Löscht eine Message mit einer bestimmten ID in einem Messageboard. deleteMessageBoard(String) Löscht ein Messageboard samt allen Messages. getMessages(String) Listet alle Nachrichten in einem Board auf. createMessage public void createMessage(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String board, sender, reply, subject, body) throws java.rmi.RemoteException Erstellt eine neue Nachricht in dem angegebenen Messageboard. Parameters: Board - das MessageBoard in das der spieler die Nachricht schreibt. Existiert das MessageBoard noch nicht so wird es erstellt. sender - der Name des Spielers, der die Nachricht sendet - id der Message auf die der Spieler antwortet oder null wenn er einen neuen Thread beginnt. subject - das Subject der Nachricht body - der Inhalt der Nachricht reply getMessages public java.util.Vector getMessages(java.lang.String board) throws java.rmi.RemoteExceptio Listet alle Nachrichten in einem Board auf. Dabei werden nur die Nachrichten übertragen, die nicht eine Antwort sind. Parameters: board - das Messageboard Returns: einen Vector mit Message Objekten deleteMessage public void deleteMessage(java.lang.String board, java.lang.String id) throws java.rmi.RemoteException Löscht eine Message mit einer bestimmten ID in einem Messageboard. Parameters: board - das Messageboard id - die ID der Message die gelöscht werden soll. deleteMessageBoard public void deleteMessageBoard(java.lang.String board) throws java.rmi.RemoteException Löscht ein Messageboard samt allen Messages. Parameters: board - das Messageboard All Packages Class Hierarchy This Package Previous Next Index package mm.services.msgboard; // Java core imports import java.rmi.*; // Jini extension imports import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; import net.jini.lookup.entry.Name; import net.jini.lookup.entry.ServiceInfo; import net.jini.core.entry.Entry; /** * Diese Klasse instantiiert den File server und registriert ihn bei m Lookup Service. Service * * @author Stefan Zier Zie * @version 1.0 1. */ public class RegisterMessageBoardServer { static JoinManager jm; static MessageBoardServerImpl msgboard_instance; /** * Main methode. */ public static void main(String[] argsv) { System.out.println("RegisterMessageBoardServer (c) 1999 MM System Jahresprojekt"); try { // install security manager so that we'll be able to talk to server System.out.println(">> installiere RMI Security manager..."); System System.setSecurityManager(new RMISecurityManager()); System // create service System.out.println(">> instantiiere MessageBoard Server ..."); System msgboard_instance = new MessageBoardServerImpl(); // this name will be seen to users that browse services Name nameEntry = new Name("MM MessageBoard Server"); // some addtl. serivice information such as manufacterer etc. ServiceInfo serviceInfo = new ServiceInfo("MM MessageBoard Server", "CE5 WS 1999/2000", "CE5 WS 1999/2000", "1.0", "", ""); // gather all information that we're going to pass on to the lookup service Entry entries[] = new Entry[] {nameEntry, serviceInfo}; System.out.println(">> registriere den MessageBoard Server beim System Lookup Service..."); // create a new join manager and have it register the jts_instance. jm = new JoinManager(msgboard_instance, entries, (ServiceIDListener) msgboard_instance, new LeaseRenewalManager()); System.out.println(">> MessageBoard Server bereit"); System } catch(Exception e) catch { System.out.println("##### mm: exception caught: "); System e.printStackTrace(); } } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.msgboard.MessageBoardServerImpl mm.services.msgboard.MessageBoardServerImpl public class MessageBoardServerImpl implements MessageBoardServer Implementation des MessageBoard Servers. Version: 1.0 Author: Stefan Zier MessageBoardServerImpl() Konstruktor. createMessage(String, String, String, String, String) Erstellt eine neue Nachricht in dem angegebenen Messageboard. deleteMessage(String, String) Löscht eine Message mit einer bestimmten ID in einem Messageboard. deleteMessageBoard(String) Löscht ein Messageboard samt allen Messages. getMessages(String) Listet alle Nachrichten in einem Board auf. isAlive() Gibt true zurück wenn alles mit dem FileServer in Ordnung ist. serviceIDNotify(ServiceID) Diese Methode wird vom Join Manager aufgerufen um dem Service seine Service ID mitzuteilen so bald der Service registriert ist. MessageBoardServerImpl public MessageBoardServerImpl() throws java.rmi.RemoteException, java.sql.SQLException, ja Konstruktor. Liest die Konfigurationsdatei und baut eine Verbindung zur Datenbank auf. createMessage public void createMessage(java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String board, sender, reply, subject, body) throws java.rmi.RemoteException Erstellt eine neue Nachricht in dem angegebenen Messageboard. Parameters: Board - das MessageBoard in das der spieler die Nachricht schreibt. Existiert das MessageBoard noch nicht so wird es erstellt. sender - der Name des Spielers, der die Nachricht sendet reply - id der Message auf die der Spieler antwortet oder null wenn er einen neuen Thread beginnt. subject - das Subject der Nachricht body - der Inhalt der Nachricht getMessages public java.util.Vector getMessages(java.lang.String board) throws java.rmi.RemoteExceptio Listet alle Nachrichten in einem Board auf. Dabei werden nur die Nachrichten übertragen, die nicht eine Antwort sind. Parameters: board - das Messageboard Returns: einen Vector mit Message Objekten deleteMessage public void deleteMessage(java.lang.String board, java.lang.String id) throws java.rmi.RemoteException Löscht eine Message mit einer bestimmten ID in einem Messageboard. Parameters: board - das Messageboard id - die ID der Message die gelöscht werden soll. deleteMessageBoard public void deleteMessageBoard(java.lang.String board) throws java.rmi.RemoteException package mm.services.msgboard; // Java core imports import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.io.IOException; import java.util.Vector; import java.util.Hashtable; import java.util.Date; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; // Jini extension imports import com.sun.jini.lookup.ServiceIDListener; import net.jini.core.lookup.ServiceID; // Database connection imports import com.bitmechanic.sql.ConnectionPoolManager; import com.bitmechanic.sql.ConnectionPool; // MM Project imports import mm.tools.ConfigFile; import mm.tools.Static; /** * Implementation des MessageBoard Servers. * * @author Stefan Zier Zie * @version 1.0 1. */ public class MessageBoardServerImpl extends UnicastRemoteObject implements MessageBoardServer, ServiceIDListener { // database connections private ConnectionPoolManager cpm = null; private ConnectionPool pool = null; ; /** * Konstruktor. Liest die Konfigurationsdatei und baut eine Verbindun g zur Datenbank auf. auf */ public MessageBoardServerImpl() throws RemoteException, SQLException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException InstantiationException { super(); super ConfigFile configfile = new ConfigFile("f:\\mm\\services\\msgboard\\msgboardserver.properties"); // create a connection pool manager cpm = new ConnectionPoolManager(120); // get String String String url, username and password from the configuration file url = configfile.get("msgboard.database.URL"); username = configfile.get("msgboard.database.username"); password = configfile.get("msgboard.database.password"); if if(url == null || url.equals("")) { throw new IllegalArgumentException("mm: no database URL given"); } if(username == null || username.equals("")) if { throw new IllegalArgumentException("mm: no database login given"); } if(password == null || password.equals("")) if { throw new IllegalArgumentException("mm: no database password given"); } // add the alias for "our" database cpm.addAlias("msgboard", "org.gjt.mm.mysql.Driver", url, username, cpm password, 120, 300, 120); pool = cpm.getPool("msgboard"); } /** * Erstellt eine neue Nachricht in dem angegebenen Messageboard. * * @param Board das MessageBoard in das der spieler die Nachrich t schreibt. schreibt * Existiert das MessageBoard noch nicht so wird es erstellt. * @param sender der Name des Spielers, der die Nachricht sende t * @param reply id der Message auf die der Spieler antwortet oder nul l wenn wen * er einen neuen Thread beginnt. * @param subject das Subject der Nachrich t * @param body der Inhalt der Nachrich t */ public void createMessage(String board, String sender, String reply, String subject, String body) throws RemoteException { // Attribute überprüfen if(Static.empty(board)) if throw new IllegalArgumentException("mm: board may not be empty"); if(Static.empty(sender)) if throw new IllegalArgumentException("mm: sender may not be empty"); try { // Datenbankverbindung aus dem Pool holen Connection conn = pool.getConnection(); // maximale ID herausfinden String query = "SELECT MAX(id) AS maxid FROM msgboard"; Statement sGetMaxId = conn.createStatement(); ResultSet rsGetMaxId = sGetMaxId.executeQuery(query); int maxid = 1; ; if(rsGetMaxId.next()) if { maxid = rsGetMaxId.getInt("maxid") + 1; } sGetMaxId.close(); sGetMaxId // query zusammenbauen query = "INSERT INTO msgboard (id, board, sender, subject, body, reply, created) values("; query += maxid; query += ", "; query += Static.sqlString(board); query += ", "; query += Static.sqlString(sender); query += ", "; query += Static.sqlString(subject); query += ", "; query += Static.sqlString(body); query += ", "; query += reply; query += ", "; query += Static.mysqlDate(new Date()); query += ")"; // query ausführen Statement sInsertMessage = conn.createStatement(); sInsertMessage.execute(query); sInsertMessage sInsertMessage.close(); sInsertMessage // Datenbankverbindung in den Pool zurückgeben conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: error using database", sql); } } /** * Listet alle Nachrichten in einem Board auf. Dabei werden nur di e Nachrichte Nachrichten * übertragen, die nicht eine Antwort sind. * * @param board das Messageboard Messageboar * @return einen Vector mit Message Objekte n */ public Vector getMessages(String board) throws RemoteException { try { // conn holen Connection conn = pool.getConnection(); // erst mal alle Nachrichten aus der Datenbank reinziehen Hashtable messages = new Hashtable(); Vector tmp = new Vector(); // query bauen String query = "SELECT id, body, subject, sender, created FROM msgboard WHERE board=" + Static.sqlString(board) + " ORDER BY created DESC"; // query machen Statement sGetMessages = conn.createStatement(); ResultSet rsGetMessages = sGetMessages.executeQuery(query); // messages in den Vector schreiben while(rsGetMessages.next()) while { // record auslesen String id = rsGetMessages.getString("id"); String body = rsGetMessages.getString("body"); String subject = rsGetMessages.getString("subject"); String sender = rsGetMessages.getString("sender"); Date created = rsGetMessages.getDate("created"); // message objekt erzeugen Message message = new Message(id, board, sender, subject, body, created); // message objekt in die hashtable schreiben messages.put(id, message); messages // message objekt in den vektor schreiben tmp.addElement(message); tmp } sGetMessages.close(); sGetMessages // so, nun bauen wir die baumstruktur. dazu lesen wir alle nachrichten aus, die was im reply // feld stehen haben. wir verlinken die nachricht jeweils mit der übernachricht und löschen // sie aus dem vektor, wir verschieben sie also von der obersten ebene des baums an den platz // an den sie gehört. // query II bauen query = "SELECT id, reply FROM msgboard WHERE board=" + Static.sqlString(board) + " AND reply NOT NULL ORDER BY created DESC" ; // query machen Statement sGetReplies = conn.createStatement(); ResultSet rsGetReplies = sGetReplies.executeQuery(query); while while(rsGetReplies.next()) { // record lesen String id = rsGetReplies.getString("id"); String reply = rsGetReplies.getString("reply"); // message suchen Message to_move = (Message) messages.get(id); // message aus Vector löschen tmp.removeElement(to_move); tmp // übermessage suchen Message top_msg = (Message) messages.get(reply); // message anhängen top_msg.addReply(to_move); top_msg } sGetReplies.close(); sGetReplies // conn zurück conn.close(); conn // Vector zurückgeben return tmp; } catch(SQLException sql) catch { throw new RemoteException("mm: could not get the messages from the database due to a database error", sql); } } /** * Löscht eine Message mit einer bestimmten ID in einem Messageboard . * * @param board das Messageboard Messageboar * @param id die ID der Message die gelöscht werden soll . */ public void deleteMessage(String board, String id) throws RemoteException { try { // Connection aus dem Pool holen Connection conn = pool.getConnection(); // query basteln String query = "DELETE FROM msgboard WHERE board=" + Static.sqlString(board) + " AND id=" + id; // query ausführen Statement sDeleteMessage = conn.createStatement(); sDeleteMessage.execute(query); sDeleteMessage sDeleteMessage.close(); sDeleteMessage // Connection zurück in den Pool conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: could not execute delete in database", sql); } } /** * Löscht ein Messageboard samt allen Messages . * * @param board das Messageboard Messageboar */ public void deleteMessageBoard(String board) throws RemoteException { try { // Connection aus dem Pool holen Connection conn = pool.getConnection(); // query basteln String query = "DELETE FROM msgboard WHERE board=" + Static.sqlString(board); // query ausführen Statement sDeleteMessage = conn.createStatement(); sDeleteMessage.execute(query); sDeleteMessage sDeleteMessage.close(); sDeleteMessage // Connection zurück in den Pool conn.close(); conn } catch(SQLException sql) catch { throw new RemoteException("mm: could not execute delete in database", sql); } } /** * Diese Methode wird vom Join Manager aufgerufen um dem Service sein e Service ID mitzuteilen mitzuteile * so bald der Service registriert ist . * * @param service_id Service ID I */ public void serviceIDNotify(ServiceID service_id) { System.out.print("Service registered, ID: "); System System.out.println(service_id); System } /** * Gibt true zurück wenn alles mit dem FileServer in Ordnung ist . * * @return true wenn alles mit dem FileServer in Ordnung ist . */ public boolean isAlive() throws RemoteException { // check the database connection try { // kleine Testquery Connection conn = pool.getConnection(); Statement s = conn.createStatement(); ResultSet rs = s.executeQuery("SELECT checkvalue FROM checktable"); if(rs.next()) if { String weltherrschaft = rs.getString("checkvalue"); if(weltherrschaft == null || if !weltherrschaft.equalsIgnoreCase("WELTHERRSCHAFT")) return false; } else return false; s.close(); conn.close(); conn } catch(SQLException sql) catch { return false; } return true; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.services.msgboard.RegisterMessageBoardServer java.lang.Object | +----mm.services.msgboard.RegisterMessageBoardServer public class RegisterMessageBoardServer extends java.lang.Object Diese Klasse instantiiert den File server und registriert ihn beim Lookup Service. Version: 1.0 Author: Stefan Zier RegisterMessageBoardServer() main(String[]) Main methode. RegisterMessageBoardServer public RegisterMessageBoardServer() main public static void main(java.lang.String argsv[]) Main methode. All Packages Class Hierarchy This Package Previous Next Index package mm.services.msgboard; // Java core imports import java.rmi.*; // Jini extension imports import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; import net.jini.lookup.entry.Name; import net.jini.lookup.entry.ServiceInfo; import net.jini.core.entry.Entry; /** * Diese Klasse instantiiert den File server und registriert ihn bei m Lookup Service. Service * * @author Stefan Zier Zie * @version 1.0 1. */ public class RegisterMessageBoardServer { static JoinManager jm; static MessageBoardServerImpl msgboard_instance; /** * Main methode. */ public static void main(String[] argsv) { System.out.println("RegisterMessageBoardServer (c) 1999 MM System Jahresprojekt"); try { // install security manager so that we'll be able to talk to server System.out.println(">> installiere RMI Security manager..."); System System.setSecurityManager(new RMISecurityManager()); System // create service System.out.println(">> instantiiere MessageBoard Server ..."); System msgboard_instance = new MessageBoardServerImpl(); // this name will be seen to users that browse services Name nameEntry = new Name("MM MessageBoard Server"); // some addtl. serivice information such as manufacterer etc. ServiceInfo serviceInfo = new ServiceInfo("MM MessageBoard Server", "CE5 WS 1999/2000", "CE5 WS 1999/2000", "1.0", "", ""); // gather all information that we're going to pass on to the lookup service Entry entries[] = new Entry[] {nameEntry, serviceInfo}; System.out.println(">> registriere den MessageBoard Server beim System Lookup Service..."); // create a new join manager and have it register the jts_instance. jm = new JoinManager(msgboard_instance, entries, (ServiceIDListener) msgboard_instance, new LeaseRenewalManager()); System.out.println(">> MessageBoard Server bereit"); System } catch(Exception e) catch { System.out.println("##### mm: exception caught: "); System e.printStackTrace(); } } } g.) mm.tools Dieses Package enthält einige Hilfsklassen. Klasse AliveCheckable Backup BreakPoint ConfigFile Static TextFiles Bedeutung Interface für Klassen, um zu prüfen ob Dienste noch laufen. Minimales Backup-Programm Debugging-Hilfe Klasse zum laden und parsen eines Konfigurationsfiles Sammlung hilfreicher statischer Methoden Vereinfachtes Handling von Textdateien All Packages Class Hierarchy This Package Previous Next Index Interface mm.tools.AliveCheckable public interface AliveCheckable extends java.rmi.Remote Wird von Jini Services implementiert und kann von Clients verwendet werden um festzustellen, ob der Server noch voll funktionstüchtig ist. Version: 1.0 Author: Stefan Zier isAlive() Gibt true zurück, wenn der geprüfte Service 100%ig einsatzfähig ist, d.h. isAlive public boolean isAlive() throws java.rmi.RemoteException Gibt true zurück, wenn der geprüfte Service 100%ig einsatzfähig ist, d.h. beispielsweise alle Datenbankverbindungen funktionieren etc. Returns: true, wenn alles in Ordnung ist Throws: java.rmi.RemoteException - wenn schon die Kommunikation mit dem Service nicht funktioniert. All Packages Class Hierarchy This Package Previous Next Index package mm.tools; import java.rmi.Remote; import java.rmi.RemoteException; /** * Wird von Jini Services implementiert und kann von Clients verwende t werden um festzustellen, * ob der Server noch voll funktionstüchtig ist. * * @author Stefan Zier Zie * @version 1.0 1. */ public interface AliveCheckable extends Remote { /** * Gibt true zurück, wenn der geprüfte Service 100%ig einsatzfähig ist , d.h. beispielsweise beispielsweis * alle Datenbankverbindungen funktionieren etc. etc * * @return true, wenn alles in Ordnung is t * @exception RemoteException wenn schon die Kommunikation mit de m Service nicht funktioniert. funktioniert */ public boolean isAlive() throws RemoteException; } All Packages Class Hierarchy This Package Previous Next Index Class mm.tools.Backup java.lang.Object | +----mm.tools.Backup public class Backup extends java.lang.Object Diese kleine Toolklasse erstellt ein Backup von allen Dateien des Pojektes. Dafür erstellt sie Version: 1.0 Author: Stefan Zier destination out prefix source Backup() backup(File) fit(String, int, char, boolean) this adds characters to a string until it reaches a certain length fitNumber(int, int) this fits a number to a certain length fitNumber(String, int) this fits a number to a certain length main(String[]) source public static java.lang.String source destination public static java.lang.String destination prefix public static java.lang.String prefix out public static java.util.zip.ZipOutputStream out Backup public Backup() backup public static void backup(java.io.File file) throws java.io.IOException main public static void main(java.lang.String args[]) fit public static java.lang.String fit(java.lang.String what, int howlong, char filler, boolean left) this adds characters to a string until it reaches a certain length Parameters: what - the source string howlong - the final length of the string filler - the char to fill the strinp up with left - true if the character should be added otherwiese false Returns: the result string before the string, fitNumber public static java.lang.String fitNumber(java.lang.String number, int howlong) this fits a number to a certain length Parameters: number - the number howlong - the final length Returns: the sized number string fitNumber public static java.lang.String fitNumber(int number, int howlong) this fits a number to a certain length Parameters: number - the number howlong - the final length Returns: the sized number string All Packages Class Hierarchy This Package Previous Next Index package mm.tools; import import import import import import import import java.util.zip.ZipOutputStream; java.util.zip.ZipEntry; java.util.Date; java.util.GregorianCalendar; java.io.FileOutputStream; java.io.FileInputStream; java.io.File; java.io.IOException; /** * Diese kleine Toolklasse erstellt ein Backup von allen Dateien de s Pojektes. Dafür erstellt * sie * * @author Stefan Zier Zie * @version 1.0 1. */ public class Backup { public static String source="F:\\mm"; public static String destination="D:\\Backups"; public static String prefix="MM Backup "; public static ZipOutputStream out = null; public static void backup(File file) throws IOException { if(file.isDirectory()) if { System.out.println(">> d: " + file.getPath()); System String[] subfiles = file.list(); String for for(int i=subfiles.length-1; i>=0; i--) backup(new File(file.getPath() + "/" + subfiles[i])); backup } else { System.out.println(">> f: " + file.getPath()); System // calculate the relative path String rel_path = Static.replace(file.getPath(), source, ""); // create a new ZIP-Entry ZipEntry ze = new ZipEntry(rel_path); // put the new ZIP-Entry out.putNextEntry(ze); out // open the source file FileInputStream in = new FileInputStream(file); // the buffer byte[] buf = new byte[10000]; byte while while(in.available() > 0) { int result = in.read(buf); out.write(buf, 0, result-1); out } in.close(); in } } public static void main(String[] args) { System.out.println("Backup started"); System // get the current date Date date = new Date(); // set calendar to date GregorianCalendar cal = new GregorianCalendar(); cal.setTime(date); cal StringBuffer date_str = new StringBuffer(); date_str date_str.append(destination); date_str.append("/"); date_str date_str.append(prefix); date_str int tmp = cal.get(cal.YEAR); date_str date_str.append(String.valueOf(tmp)); tmp = cal.get(cal.MONTH); date_str.append(fitNumber(tmp, 2)); date_str tmp = cal.get(cal.DATE); date_str.append(fitNumber(tmp, 2)); date_str tmp = cal.get(cal.HOUR); date_str.append(fitNumber(tmp, 2)); date_str tmp = cal.get(cal.MINUTE); date_str.append(fitNumber(tmp, 2)); date_str tmp = cal.get(cal.SECOND); date_str.append(fitNumber(tmp, 2)); date_str date_str.append(".zip"); date_str System.out.println(">> backing up to \"" + date_str.toString() + System "\""); try { // create the file FileOutputStream fos = new FileOutputStream(date_str.toString()); out = new ZipOutputStream(fos); backup(new File(source)); backup out out.close(); fos.close(); fos } catch(Exception e) catch { e.printStackTrace(); } } /** * this adds characters to a string until it reaches a certain lengt h * * @param what the source string strin * @param howlong the final length of the strin g * @param filler the char to fill the strinp up wit h * @param left true if the character should be added before the string , otherwiese false fals * @return the result string */ public static String fit(String what, int howlong, char filler, boolean left) { String tmp = what; if(tmp == null) if tmp = new String(); while while(tmp.length() < howlong) { if(left) if tmp = String.valueOf(filler) + tmp; else tmp = tmp + String.valueOf(filler); } return tmp; } /** * this fits a number to a certain lengt h * * @param number the number numbe * @param howlong the final length lengt * @return the sized number string strin */ public static String fitNumber(String number, int howlong) { return fit(number, howlong, "0".charAt(0), true); } /** * this fits a number to a certain lengt h * * @param number the number numbe * @param howlong the final length lengt * @return the sized number string strin */ public static String fitNumber(int number, int howlong) { return fit(String.valueOf(number), howlong, "0".charAt(0), true); } } All Packages Class Hierarchy This Package Previous Next Index Class mm.tools.BreakPoint java.lang.Object | +----mm.tools.BreakPoint public class BreakPoint extends java.lang.Object this is a little but nice tool for server side java development. Every call to the now() method outputs a string on the screen. Version: 1.0 - 08/12/1999 Author: Stefan Zier BreakPoint() now(Object, String) this method is called whenever a breakpoint is reached now(String) reset() reset the counter to zero BreakPoint public BreakPoint() now public static void now(java.lang.Object where, java.lang.String info) this method is called whenever a breakpoint is reached Parameters: where - the object in which the breakpoint occurred info - additional information now public static void now(java.lang.String info) reset public static void reset() reset the counter to zero All Packages Class Hierarchy This Package Previous Next Index package mm.tools; /** * this is a little but nice tool for server side java development. Ever y call to * the now() method outputs a string on the screen . * * @author Stefan Zier Zie * @version 1.0 - 08/12/1999 08/12/199 */ public class BreakPoint { private static int counter = 0; /** * this method is called whenever a breakpoint is reache d * * @param where the object in which the breakpoint occurre d * @param info additional information informatio */ public static void now(Object where, String info) { counter++; counter System.out.print("****[" + counter + "]**"); System if(where != null) if { System.out.print("[" + where.getClass().getName() + "]"); System } if(info != null) if { System.out.print(info); System } System.out.println(); System } public static void now(String info) { now(null, info); now } /** * reset the counter to zero zer */ public static void reset() { counter = 0; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.tools.ConfigFile java.lang.Object | +----mm.tools.ConfigFile public class ConfigFile extends java.lang.Object This class loads a read-only config-file from the disk Version: 1.0 - 07/08/1999 Author: Stefan Zier ConfigFile(String) Initializes the table get(String) Looks a variable up in the table keys() this returns a list of all keys ConfigFile public ConfigFile(java.lang.String filename) throws java.lang.IllegalArgumentException, ja Initializes the table get public java.lang.String get(java.lang.String varname) Looks a variable up in the table keys public java.util.Enumeration keys() this returns a list of all keys All Packages Class Hierarchy This Package Previous Next Index package mm.tools; import import import import import java.util.Properties; java.util.Enumeration; java.io.FileNotFoundException; java.io.IOException; java.io.FileInputStream; /** * This class loads a read-only config-file from the dis k * * @author Stefan Zier Zie * @version 1.0 - 07/08/1999 07/08/199 */ public class ConfigFile { protected Properties config; /** * Initializes the table tabl */ public ConfigFile(String filename) throws IllegalArgumentException, FileNotFoundException, IOException { // check if the filename is null if(filename == null) if throw new IllegalArgumentException("o Trying to load null config file !"); // open the file FileInputStream fis = new FileInputStream(filename); // load the file config = new Properties(); config.load(fis); config fis.close(); fis } /** * Looks a variable up in the tabl e */ public String get(String varname) { return config.getProperty(varname); } /** * this returns a list of all key s */ public Enumeration keys() { return config.keys(); } } All Packages Class Hierarchy This Package Previous Next Index Class mm.tools.Static java.lang.Object | +----mm.tools.Static public class Static extends java.lang.Object this class offers some static methods (e.g. date conversion) Version: 1.0 - created 07/08/1999 Author: Stefan Zier EMAIL_CONTAINS_WS EMAIL_EMPTY EMAIL_INVALID_CHARS EMAIL_INVALID_FORMAT EMAIL_INVALID_TLD EMAIL_OK MAX_EXCEEDED MIN_EXCEEDED random SIZE_OK Static() beginsUppercase(String) this simple function finds out whether the word starts with an uppercase Character or not can handle Umlauts like &Ouml; booleanToString(boolean) this little method converts boolean values to strings. checkSize(String, int, int) This will check if the size of a String is correct. containsWhitespaces(String) this checks whether a string contains any whitespaces cutString(String, int) This cuts Strings after a certain length and attaches "..." dayString(int) Returns a String containing the name of the day. emailCheck(String) Checks whether an email address is syntactically correct or not and returns the result as an int constant. emailValid(String) Checks whether an email address is syntactically correct. emailVerbose(String) Checks whether an email address is syntactically correct or not and returns the result in verbal form. empty(String) this one checks wether a string is null, empty or contains only whitespaces fit(String, int, char, boolean) this adds characters to a string until it reaches a certain length fitNumber(int, int) this fits a number to a certain length fitNumber(String, int) this fits a number to a certain length formatDate(long) This method will convert a date that is stored in a long into a US formatted date in the form MM/DD/YYYY HH:MM. formatFileSize(long) This method will format a long value that symbolizes the size of a file in bytes. getQueryParam(String, Hashtable) This will extract a usable String from a querystring hashtable that is never null. howManyDays(int, int) this returns how many days a month has httpGetString(String) this converts a string into one that is compatible for being used in a link with get-parameters insert (String, String, int) this method is used to insert a string into another javascriptString(String) This method will treat a String so that it can be output to javascript without problems. mysqlDate(Date) this converts a date into a MySQL-compliant string including the ' 's nonNull(String) this is used to make sure a string is not null (returns a "" string instead) oracleDate(Date) this converts a date into a Oracle-compliant string including the ' 's randomNumber(int) This method will return a String containing a random number in the length given. recursiveDelete(File) This method will remove this folder and all its subfolders. remove(String, String) this is used to do find and remove operations within strings removeSurroundingWhitespaces(String) this is used to remove whitespaces before and after a string only. removeWhitespaces(String) this is used to remove whitespaces such as spaces, tabs and cariage returns from strings. replace(String, String, String) this is used to do find and replace operations within strings sortStrings(String[]) This method sorts an array of Strings. sortStrings(String[], int, int) This method sorts an array of Strings. sqlLikeString(String) this is used to make sure a string is SQL compliant for like sqlString(String) this is used to make sure a string is SQL compliant stringToBoolean(String) this converts a string containing either "0" or "1" into the corresponding boolean value. whitespaceList(String) this creates a Properties object from a whitespace/comma/semicolon separated word list in a string. SIZE_OK public static final int SIZE_OK MAX_EXCEEDED public static final int MAX_EXCEEDED MIN_EXCEEDED public static final int MIN_EXCEEDED random public static java.util.Random random EMAIL_OK public static final int EMAIL_OK EMAIL_EMPTY public static final int EMAIL_EMPTY EMAIL_CONTAINS_WS public static final int EMAIL_CONTAINS_WS EMAIL_INVALID_FORMAT public static final int EMAIL_INVALID_FORMAT EMAIL_INVALID_CHARS public static final int EMAIL_INVALID_CHARS EMAIL_INVALID_TLD public static final int EMAIL_INVALID_TLD Static public Static() insert public static java.lang.String insert(java.lang.String in, java.lang.String fill_in, int pos) this method is used to insert a string into another Parameters: in - the string that the other string is to be inserted in fill_in - the string that is going to be inserted pos - the position in which to fill in the string Returns: the resulting string replace public static java.lang.String replace(java.lang.String inp, java.lang.String find, java.lang.String what) this is used to do find and replace operations within strings Parameters: in - the source string find - what to find in the source string what - what to write instead of Returns: replaced string remove public static java.lang.String remove(java.lang.String inp, java.lang.String find) this is used to do find and remove operations within strings Parameters: in - the source string find - what to find in the source string Returns: replaced string removeWhitespaces public static java.lang.String removeWhitespaces(java.lang.String in) this is used to remove whitespaces such as spaces, tabs and cariage returns from strings. Parameters: in - the source string Returns: the string without whitespaces removeSurroundingWhitespaces public static java.lang.String removeSurroundingWhitespaces(java.lang.String in) this is used to remove whitespaces before and after a string only. It leaves whitespaces within the string untouched Parameters: in - the string to be processed Returns: the string without whitespaces before and after it containsWhitespaces public static boolean containsWhitespaces(java.lang.String in) this checks whether a string contains any whitespaces Parameters: in - the string to check for whitespaces Returns: true, if it contains whitespaces beginsUppercase public static boolean beginsUppercase(java.lang.String word) this simple function finds out whether the word starts with an uppercase Character or not can handle Umlauts like &Ouml; Parameters: word - the word Returns: true if the first character is uppercase nonNull public static java.lang.String nonNull(java.lang.String in) this is used to make sure a string is not null (returns a "" string instead) Parameters: in - the source string Returns: the non-null string empty public static boolean empty(java.lang.String in) this one checks wether a string is null, empty or contains only whitespaces Parameters: in - the string to check Returns: false if the string contains characters fit public static java.lang.String fit(java.lang.String what, int howlong, char filler, boolean left) this adds characters to a string until it reaches a certain length Parameters: what - the source string howlong - the final length of the string filler - the char to fill the strinp up with left - true if the character should be added before the string, otherwiese false Returns: the result string fitNumber public static java.lang.String fitNumber(java.lang.String number, int howlong) this fits a number to a certain length Parameters: number - the number howlong - the final length Returns: the sized number string fitNumber public static java.lang.String fitNumber(int number, int howlong) this fits a number to a certain length Parameters: number - the number howlong - the final length Returns: the sized number string formatFileSize public static java.lang.String formatFileSize(long length) This method will format a long value that symbolizes the size of a file in bytes. Parameters: length - the length of the file Returns: the formatted length httpGetString public static java.lang.String httpGetString(java.lang.String input) this converts a string into one that is compatible for being used in a link with get-parameters Parameters: input - the inputstring Returns: the correct string oracleDate public static java.lang.String oracleDate(java.util.Date date) this converts a date into a Oracle-compliant string including the ' 's Parameters: date - a date Returns: the string mysqlDate public static java.lang.String mysqlDate(java.util.Date date) this converts a date into a MySQL-compliant string including the ' 's Parameters: date - a date Returns: the string sqlString public static java.lang.String sqlString(java.lang.String in) this is used to make sure a string is SQL compliant Parameters: in - the source string Returns: SQL compliant string sqlLikeString public static java.lang.String sqlLikeString(java.lang.String in) this is used to make sure a string is SQL compliant for like Parameters: in - the source string Returns: SQL compliant string booleanToString public static java.lang.String booleanToString(boolean in) this little method converts boolean values to strings. Unlike the String.toString(boolean) function it doesn't convert to "true" or "false" but to: true -> "1" false --> "0" Parameters: in - the boolean value Returns: the String stringToBoolean public static boolean stringToBoolean(java.lang.String in) this converts a string containing either "0" or "1" into the corresponding boolean value. All other values of the string return false. Parameters: in - the String Returns: the boolean equivalent of the String whitespaceList public static java.util.Properties whitespaceList(java.lang.String wordlist) this creates a Properties object from a whitespace/comma/semicolon separated word list in a string. The values of the words are set to null Parameters: wordlist - the word list Returns: the Properties object getQueryParam public static java.lang.String getQueryParam(java.lang.String param_name, java.util.Hashtable querystring) This will extract a usable String from a querystring hashtable that is never null. Parameters: param_name - the name of the parameter to get q Returns: a String checkSize public static int checkSize(java.lang.String input, int min, int max) This will check if the size of a String is correct. Parameters: input - the input String min - the minimum length max - the maximum length Returns: SIZE_OK, if the size is ok, MAX_EXCEEDED or MIN_EXCEEDED if not. randomNumber public static java.lang.String randomNumber(int length) This method will return a String containing a random number in the length given. Parameters: length Returns: a random number in the requested length howManyDays public static int howManyDays(int month, int year) this returns how many days a month has Parameters: month - the month to look for year - the year to look for Returns: the number of days dayString public static java.lang.String dayString(int day) Returns a String containing the name of the day. Returns: a String containing the name of the day. formatDate public static java.lang.String formatDate(long theTime) This method will convert a date that is stored in a long into a US formatted date in the form MM/DD/YYYY HH:MM. Parameters: theTime - long the date Returns: the String containing the formatted date, null if the date is undefined sortStrings public static void sortStrings(java.lang.String strings[]) This method sorts an array of Strings. The algorithm used is a mergesort, a modified version of quicksort that works better on arrays with fixed size. Parameters: strings - the array to sort sortStrings public static void sortStrings(java.lang.String strings[], int lo, int hi) This method sorts an array of Strings. The algorithm used is a mergesort, a modified version of quicksort that works better on arrays with fixed size. Parameters: strings - the array to sort start - the index to start at end - the index to end at javascriptString public static java.lang.String javascriptString(java.lang.String input) This method will treat a String so that it can be output to javascript without problems. Parameters: input - the input String Returns: the java script compliant String cutString public static java.lang.String cutString(java.lang.String input, int length) This cuts Strings after a certain length and attaches "..." Parameters: input - the source String length - the length the output String is suppoed to have Returns: the cut String emailVerbose public static java.lang.String emailVerbose(java.lang.String email) Checks whether an email address is syntactically correct or not and returns the result in verbal form. Parameters: email - The address to check. Returns: The verbose result of the check. emailValid public static boolean emailValid(java.lang.String email) Checks whether an email address is syntactically correct. Parameters: email - The address to check. Returns: True if the email is valid. emailCheck public static int emailCheck(java.lang.String email) Checks whether an email address is syntactically correct or not and returns the result as an int constant. Parameters: email - The address to check. Returns: The int result of the check. recursiveDelete public static boolean recursiveDelete(java.io.File rotten) This method will remove this folder and all its subfolders. Returns: true if the action completed successfully All Packages Class Hierarchy This Package Previous Next Index package mm.tools; import import import import import import import import java.util.Properties; java.util.StringTokenizer; java.util.Random; java.io.File; java.util.Date; java.util.Calendar; java.util.GregorianCalendar; java.util.Hashtable; /** * this class offers some static method s * (e.g. date conversion) conversion * * @author Stefan Zier Zie * @version 1.0 - created 07/08/1999 07/08/199 */ public class Static { // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// M E T H O D S T H A T java.lang.String S H O U L D H A V E // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------/** * this method is used to insert a string into anothe r * * @param in the string that the other string is to be inserted i n * @param fill_in the string that is going to be inserte d * @param pos the position in which to fill in the strin g * @return the resulting string strin */ public synchronized static String insert(String in, String fill_in, int pos) { // is the string ok ? if(in == null) if return null; // is the pos ok ? if(pos < 0 || pos > in.length() - 1) if return in; // do we need to do anything ? if(fill_in == null) if return in; if(fill_in.equals("")) if return in; if(in.equals("")) if return fill_in; StringBuffer tmp = new StringBuffer(512); tmp tmp.append(in.substring(0, pos)); tmp.append(fill_in); tmp tmp.append(in.substring(pos)); tmp return tmp.toString(); } /** * this is used to do find and replace operations within string s * * @param in the source string * @param find what to find in the source strin g * @param what what to write instead o f * @return replaced string strin */ public static String replace(String inp, String find, String what) { // check if we need to search if(inp == null || find == null || what == null) if return inp; if(inp.equals("") || find.equals("")) if return inp; // do the replacements StringBuffer tmp = new StringBuffer(1024); String in = new String(inp); int length = find.length(); while while(in.indexOf(find) != -1) { // copy everythng before the first match to tmp tmp.append(in.substring(0, in.indexOf(find))); tmp ))); // append what to tmp tmp.append(what); tmp // cut until the end of the first match from tmp in = in.substring(in.indexOf(find) + length); } tmp.append(in); tmp return tmp.toString(); } /** * this is used to do find and remove operations within string s * * @param in the source string * @param find what to find in the source strin g * @return replaced string strin */ public static String remove(String inp, String find) { // check if we need to search if(inp == null || find == null) if return inp; if(inp.equals("") || find.equals("")) if return inp; // do the replacements StringBuffer tmp = new StringBuffer(1024); String in = new String(inp); while while(in.indexOf(find) != -1) { // copy evrthng before the first match to tmp tmp.append(in.substring(0, in.indexOf(find))); tmp ))); // cut until the end of the first match from tmp in = in.substring(in.indexOf(find) + find.length()); } tmp.append(in); tmp return tmp.toString(); } /** * this is used to remove whitespaces such as spaces, tabs and cariag e * returns from strings. * * @param in the source string strin * @return the string without whitespaces whitespace */ public static String removeWhitespaces(String in) { // create a copy of the string String tmp = new String(in); // space tmp = replace(tmp, " ", ""); // newline tmp = replace(tmp, // formfeed tmp = replace(tmp, // cariage return tmp = replace(tmp, // tab tmp = replace(tmp, // backspace tmp = replace(tmp, return tmp; "\n", ""); "\f", ""); "\r", ""); "\t", ""); "\b", ""); } /** * this is used to remove whitespaces before and after a string only. I t leaves leave * whitespaces within the string untouched untouche * * @param in the string to be processe d * @return the string without whitespaces before and after i t */ public static String removeSurroundingWhitespaces(String in) { // check if we got a faules Ei if(in == null) if return null; // maybe we should try the built-in String function return in.trim(); } /** * this checks whether a string contains any whitespace s * * @param in the string to check for whitespace s * @return true, if it contains whitespace s */ public static boolean containsWhitespaces(String in) { return (in.indexOf(" ") != -1 ||in.indexOf("\n") != -1 || in.indexOf("\f") != -1 || in.indexOf("\r") in != -1 || in.indexOf("\t") != -1 || in.indexOf("\b") != -1); } /** * this simple function finds out whether the word starts with a n uppercase Character or not no * can handle Umlauts like &amp;Ouml; * * @param word the word wor * @return true if the first character is uppercas e */ public static boolean beginsUppercase(String word) { if(word.substring(0,1).equals("&")) if { // ok, so this is a &amp;Ouml; thing, let`s take care of the second char return word.substring(1,1).equals(word.substring(1,1).toUpperCase()); } // this is the normal case, let's stick with the first character. return word.substring(0,1).equals(word.substring(0,1).toUpperCase()); } /** * this is used to make sure a string is not null (returns a "" strin g instead) instead * * @param in the source string strin * @return the non-null string strin */ public static String nonNull(String in) { if(in == null) if return ""; else return in; } /** * this one checks wether a string is null, empty or contains onl y whitespaces whitespace * * @param in the string to check chec * @return false if the string contains character s */ public static boolean empty(String in) { if(in == null) if return true; if(removeWhitespaces(in).equals("")) if return true; return false; } // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// F O R M A T T I N G M E T H O D S // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------/** * this adds characters to a string until it reaches a certain lengt h * * @param what the source string strin * @param howlong the final length of the strin g * @param filler the char to fill the strinp up wit h * @param left true if the character should be added before the string , otherwiese false fals * @return the result string */ public static String fit(String what, int howlong, char filler, boolean left) { String tmp = nonNull(what); while while(tmp.length() < howlong) { if(left) if tmp = String.valueOf(filler) + tmp; else tmp = tmp + String.valueOf(filler); } return tmp; } /** * this fits a number to a certain lengt h * * @param number the number numbe * @param howlong the final length lengt * @return the sized number string strin */ public static String fitNumber(String number, int howlong) { return fit(number, howlong, "0".charAt(0), true); } /** * this fits a number to a certain lengt h * * @param number the number numbe * @param howlong the final length lengt * @return the sized number string strin */ public static String fitNumber(int number, int howlong) { return fit(String.valueOf(number), howlong, "0".charAt(0), true); } // -- the chars (byte, kilobyte, Megabyte, Gigabyte, Terrabyte) protected static String[] measure_chars = {" b", " k", " M", " G", " T"}; /** * This method will format a long value that symbolizes the size of file in i * bytes. * * @param length the length of the fil e * @return the formatted length lengt */ public static String formatFileSize(long length) { double display = length; a StringBuffer result = new StringBuffer(); int measure = 0; // make sure we have a small number while(display > 2000) while { display /= 1024; measure++; measure } // now there are two cases // a) the number has two, three or four characters - we won't display the digits after the comma // b) the number has one character. This means we will have to print a comma and the next number after // the digit int number = (int) display; result result.append(number); result.append(measure_chars[measure]); result return result.toString(); } // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// W E B - R E L A T E D M E T H O D S // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------/** * this converts a string into one that is compatible for being used i n a link lin * with get-parameters get-parameter * * @param input the inputstring inputstrin * @return the correct string strin */ public synchronized static String httpGetString(String input) { input = replace(input, "%", "%25"); input = replace(input, " ", "%20"); return input; } // ------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// D A T A B A S E M E T H O D S // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------/** * this converts a date into a Oracle-compliant string including the ' 's * * @param date a date dat * @return the string strin */ public synchronized static String oracleDate(Date date) { if(date != null) if { StringBuffer tmp = new StringBuffer(); tmp.append("TO_DATE('"); tmp tmp.append(fitNumber(String.valueOf(date.getDate()), 2)); tmp switch(date.getMonth()) switch { case 0: tmp.append("-JAN-"); tmp break; break case 1: tmp.append("-FEB-"); tmp break; break case 2: tmp.append("-MAR-"); tmp break; break case 3: tmp.append("-APR-"); tmp break; break case 4: tmp.append("-MAY-"); tmp break; break case 5: tmp.append("-JUN-"); tmp break; break case 6: tmp.append("-JUL-"); tmp break; break case 7: tmp.append("-AUG-"); tmp break; break case 8: tmp.append("-SEP-"); tmp break; break case 9: tmp.append("-OCT-"); tmp break; break case 10: tmp.append("-NOV-"); tmp break; break case 11: tmp.append("-DEC-"); tmp break; break } tmp.append(String.valueOf(date.getYear() + 1900)); tmp tmp tmp.append(" "); tmp.append(fitNumber(String.valueOf(date.getHours()), 2)); tmp tmp.append(":"); tmp tmp.append(fitNumber(String.valueOf(date.getMinutes()), 2)); tmp tmp.append(":"); tmp tmp.append(fitNumber(String.valueOf(date.getSeconds()), 2)); tmp tmp tmp.append("','DD-MON-YYYY HH24:MI:SS')"); return tmp.toString(); } return null; } /** * this converts a date into a MySQL-compliant string including the ' ' s * * @param date a date dat * @return the string strin */ public synchronized static String mysqlDate(Date date) { if(date != null) if { StringBuffer tmp = new StringBuffer(); tmp.append("'"); tmp tmp.append(String.valueOf(date.getYear() + 1900)); tmp tmp.append("-"); tmp tmp.append(fitNumber(String.valueOf(date.getMonth()), 2)); tmp tmp.append("-"); tmp tmp.append(fitNumber(String.valueOf(date.getDate()), 2)); tmp tmp.append(" "); tmp tmp tmp.append(fitNumber(String.valueOf(date.getHours()), 2)); tmp.append(":"); tmp tmp.append(fitNumber(String.valueOf(date.getMinutes()), 2)); tmp tmp.append(":"); tmp tmp.append(fitNumber(String.valueOf(date.getSeconds()), 2)); tmp tmp.append("'"); tmp return tmp.toString(); } return null; } /** * this is used to make sure a string is SQL complian t * * @param in the source string strin * @return SQL compliant string strin */ public static String sqlString(String in) { String tmp = in; if(tmp == null) if return "NULL"; tmp = replace(tmp, "'", "''"); tmp = replace(tmp, "´", "´´"); tmp = "'" + tmp + "'"; return tmp; } /** * this is used to make sure a string is SQL compliant for lik e * * @param in the source string strin * @return SQL compliant string strin */ public static String sqlLikeString(String in) { String tmp = nonNull(in); tmp = replace(tmp, "'", "''"); tmp = replace(tmp, "´", "´´"); tmp = "'%" + tmp + "%'"; return tmp; } // --------------------------------------------------------------------------- -----------------------// -------------------------------------------------------------------------------------------------// S P E C I A L T Y P E C O N V E R S I O N M E T H O D S // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------/** * this little method converts boolean values to strings. Unlike th e String.toString(boolean) String.toString(boolean * function it doesn't convert to "true" or "false" but to:<br > * true -> "1"<br> "1"<br * false --> "0" "0 * * @param in the boolean value valu * @return the String Strin */ public static String booleanToString(boolean in) { if(in) if return "1"; else return "0"; } /** * this converts a string containing either "0" or "1" into th e corresponding correspondin * boolean value. All other values of the string return false . * * @param in the String Strin * @return the boolean equivalent of the Strin g */ public static boolean stringToBoolean(String in) { if(removeSurroundingWhitespaces(in).equals("1")) if return true; return false; } /** * this creates a <code>Properties</code> object from a whitespace/comma/semicolon whitespace/comma/semicolo * separated word list in a string. The values of the words are set t o null nul * * @param wordlist the word list lis * @return the Properties object objec */ public synchronized static Properties whitespaceList(String wordlist) { // remove surrounding whitespaces wordlist = removeWhitespaces(wordlist); Properties list = new Properties(); if(empty(wordlist)) if return null; StringTokenizer st = new StringTokenizer(wordlist, " ;\n", false); while(st.hasMoreTokens()) while { String token = st.nextToken(); list.put(token, token); list } return list; } // -------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------// S U P P L E M E N T A R Y M E T H O D S // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------- /** * This will extract a usable String from a querystring hashtable tha t is never null. null * * @param param_name the name of the parameter to ge t * @param q * @return a String Strin */ public static String getQueryParam(String param_name, Hashtable querystring) { if(querystring == null || param_name == null) if return new String(); String[] many = (String[])querystring.get(param_name); String if(many == null) if return new String(); String single = many[0]; if(single == null) if return new String(); return single.trim(); } public static final int SIZE_OK = 0; public static final int MAX_EXCEEDED = 1; public static final int MIN_EXCEEDED = 2; /** * This will check if the size of a String is correct. * * @param input the input String Strin * @param min the minimum length lengt * @param max the maximum length lengt * @return SIZE_OK, if the size is ok, MAX_EXCEEDED or MIN_EXCEEDED i f not. not */ public static int checkSize(String input, int min, int max) { if(input == null && min > 0) if return MIN_EXCEEDED; if(input.length() < min) if return MIN_EXCEEDED; if(input.length() > max) if return MAX_EXCEEDED; return SIZE_OK; } public static Random random = new Random(); /** * This method will return a String containing a random number in th e length given. given * * @param length * @return a random number in the requested lengt h */ public static String randomNumber(int length) { StringBuffer result = new StringBuffer(); byte[] numbers = new byte[length]; byte random.nextBytes(numbers); random while while(length > 0) { length--; length // append the char representation of this number to the buffer String number = (new Integer(numbers[length])).toString(); result.append(number.substring(number.length() - 1)); result } return result.toString(); } /** * returns the minimum of two integer s * * @param i1 first number numbe * @param i2 second number numbe * @return the minimum minimu */ private synchronized static int min(int i1, int i2) { if(i1 < i2) return i1; if return i2; } /** * this returns how many days a month ha s * * @param month the month to look fo r * @param year the year to look fo r * @return the number of days day */ public static int howManyDays(int month, int year) { if(month == 1 || month == 3 || month == 5 || month == 8 || month == if 10 || month == 12) return 31; if(month == 2) if { if(year < 90) if year += 2000; if(year < 100) if year += 1900; if(year == 2000) if return 28; if(year % 4 == 0) if return 29; return 28; } return 30; } /** * Returns a String containing the name of the day . * * @return a String containing the name of the day . */ public static String dayString(int day) { switch(day) switch { case GregorianCalendar.MONDAY: return "Monday"; case GregorianCalendar.TUESDAY: return "Tuesday"; case GregorianCalendar.WEDNESDAY: return "Wednesday"; case GregorianCalendar.THURSDAY: return "Thursday"; case GregorianCalendar.FRIDAY: return "Friday"; case GregorianCalendar.SATURDAY: return "Saturday"; case GregorianCalendar.SUNDAY: return "Sunday"; } return null; } protected static java.util.Date format_date = new java.util.Date(); protected static GregorianCalendar format_cal = new GregorianCalendar(); /** * This method will convert a date that is stored in a long into a U S formatted formatte * date in the form MM/DD/YYYY HH:MM. * * @param theTime long the date dat * @return the String containing the formatted date, null if the date i s undefined undefine */ public synchronized static String formatDate(long theTime) { format_date.setTime(theTime); format_date format_cal.setTime(format_date); format_cal StringBuffer result = new StringBuffer(); int month = format_cal.get(Calendar.MONTH) + 1; if(month < 10) if result.append("0"); result result.append(month); result result.append("/"); result int date = format_cal.get(Calendar.DATE); if if(date < 10) result.append("0"); result result.append(date); result result.append("/"); result int year = format_cal.get(Calendar.YEAR); result result.append(year); result.append(" "); result int hour = format_cal.get(Calendar.HOUR); if if(hour < 10) result.append("0"); result result.append(hour); result result.append(":"); result int minute = format_cal.get(Calendar.MINUTE); if if(minute < 10) result.append("0"); result result.append(minute); result return result.toString(); } /** * This method sorts an array of Strings. The algorithm used is a mergesort, a * modified version of quicksort that works better on arrays with fixe d size. size * * @param strings the array to sor t */ public static void sortStrings(String[] strings) { if(strings == null) if return; return sortStrings(strings, 0, strings.length-1); sortStrings } /** * This method sorts an array of Strings. The algorithm used is a mergesort, a * modified version of quicksort that works better on arrays with fixe d size. size * * @param strings the array to sor t * @param start the index to start a t * @param end the index to end a t */ public static void sortStrings(String[] strings, int lo, int hi) { // do not sort one item (it's already sorted ;-)) if(lo == hi) if return; return // the length of the buffer array to sort in int length = hi-lo+1; // set the pivot to the middle of the array int pivot = (lo+hi)/2; // sort both parts left and right of the array sortStrings(strings, lo, pivot); sortStrings sortStrings(strings, pivot+1, hi); sortStrings // now that they're sorted copy all of them into a working array String[] working = new String[length]; String for(int i = 0; i < length; i++) for working[i] = strings[lo+i]; working // merge the two parts that have been sorted int m1 = 0; int m2 = pivot-lo+1; for(int i=0; i<length; i++) for { if(m2 <= hi-lo) if { if(m1 <= pivot-lo) if { if(working[m1].toLowerCase().compareTo(working[m2].toLowerCase()) > 0) strings[i+lo] = working[m2++]; strings else strings[i+lo] = working[m1++]; strings } else strings[i+lo] = working[m2++]; strings } else strings[i+lo] = working[m1++]; strings } } /** * This method will treat a String so that it can be output t o javascript without problems. problems * * @param input the input String Strin * @return the java script compliant Strin g */ public static String javascriptString(String input) { return replace(input, "'", "\'"); } /** * This cuts Strings after a certain length and attaches "... " * * @param input the source String Strin * @param length the length the output String is suppoed to hav e * @return the cut String Strin */ public static String cutString(String input, int length) { if(input == null) if return null; if(input.length() <= length) if return input; return input.substring(0, length) + "..."; } // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// E M A I L C H E C K E R // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// -- constants for public static final public static final public static final public static final public static final public static final the int int int int int int email checker EMAIL_OK = 0; EMAIL_EMPTY = 1; EMAIL_CONTAINS_WS = 2; EMAIL_INVALID_FORMAT = 3; EMAIL_INVALID_CHARS = 4; EMAIL_INVALID_TLD = 5; /** * Checks whether an email address is syntactically correct or not an d returns * the result in verbal form. form * * @param email The address to check . * @return The verbose result of the check . */ public synchronized static String emailVerbose(String email) { switch(emailCheck(email)) switch { case EMAIL_EMPTY: return "No email-address was given."; case EMAIL_CONTAINS_WS: return "The email-address contains whitespaces."; case EMAIL_INVALID_FORMAT: return "The format of the email-address is invalid."; case EMAIL_INVALID_CHARS: return "The email-address contains invalid characters."; case EMAIL_INVALID_TLD: return "The top level domain of the email-address is not valid."; default: default return "The email-address is ok."; } } /** * Checks whether an email address is syntactically correct . * * @param email The address to check . * @return True if the email is valid . */ public synchronized static boolean emailValid(String email) { int ok = emailCheck(email); return ok == EMAIL_OK; } /** * Checks whether an email address is syntactically correct or not an d returns * the result as an int constant. constant * * @param email The address to check . * @return The int result of the check . */ public synchronized static int emailCheck(String email) { // check if the string is null if(email == null) if return EMAIL_EMPTY; // make a copy of the string String tmp = new String(email); // remove whitespaces before and after the address tmp = removeSurroundingWhitespaces(tmp); // check if the string is empty now if(tmp.length() == 0) if return EMAIL_EMPTY; // check if the string contains any whitespaces if(containsWhitespaces(tmp)) if return EMAIL_CONTAINS_WS; // the minimum length of all email addresses is [email protected] = 6 characters if(tmp.length() < 6) if return EMAIL_INVALID_FORMAT; // check for a @ and for characters before the @ if(tmp.indexOf("@") < 1) if return EMAIL_INVALID_FORMAT; // does the string contain more than one @ ? if(tmp.indexOf("@") != tmp.lastIndexOf("@")) if return EMAIL_INVALID_FORMAT; // check whether there is at least one dot after the @ if(tmp.indexOf("@") > tmp.lastIndexOf(".")) if return EMAIL_INVALID_FORMAT; // are there characters between the @ and the first dot after it? if(tmp.substring(tmp.indexOf("@")).indexOf(".") < 1) if return EMAIL_INVALID_FORMAT; // are there characters between the @ and the first dot before it? if(tmp.substring(0, tmp.indexOf("@") - 1).lastIndexOf(".") == if tmp.substring(0, tmp.indexOf("@") - 1).length()) return EMAIL_INVALID_FORMAT; // check whether the first character is a dot if(tmp.indexOf(".") == 0) if return EMAIL_INVALID_FORMAT; // check if we have at least four characters after the @ (eg [email protected]) if(tmp.substring(tmp.indexOf("@")+1).length() < 4) if return EMAIL_INVALID_FORMAT; // commata are only allowed before the @ if(tmp.indexOf("@") < tmp.lastIndexOf(",")) if return EMAIL_INVALID_CHARS; // check for invalid characters String invalidchars = "/\\?!'\"$&=`*+:;<>|[]"; for(int i = 0; i < tmp.length(); i++) for if(invalidchars.indexOf(tmp.substring(i, i+1)) != -1 if || tmp.charAt(i) tmp > 0x007F) return EMAIL_INVALID_CHARS; // last but not least check if the tld of ok String valid_tld = "com;edu;gov;int;mil;net;org;"; valid_tld += "ad;ae;af;ag;ai;al;am;an;ao;aq;ar;as;at;au;aw;az;ba;bb;bd;be;bf;bg;bh;bi;bj ;bm;bn;bo;br;bs;bt;bv;bw;by;bz;ca;cc;cd;cf;cg;ch;ci;ck;cl;cm;cn;co;cr;cu;cv ;cx;cy;cz;de;dj;dk;dm;do;dz;ec;ee;eg;eh;er;es;es;et;fi;fj;fk;fm;fo;fr;fx;ga ;gb;gd;ge;gf;gg;gh;gi;gl;gm;gn;gp;gq;gr;gt;gu;gw;gy;hk;hm;hn;hr;ht;hu;id;ie ;il;im;in;io;iq;ir;is;it;je;jm;jo;jp;ke;kg;kh;ki;km;kn;kp;kr;kw;ky;kz;la;lb ;lc;li;lk;lr;ls;lt;lu;lv;ly;ma;mc;md;mg;mh;mk;ml;mm;mn;mo;mp;mq;mr;ms;mt;mu ;mv;mw;mx;my;mz;na;nc;ne;nf;ng;ni;nl;no;np;nr;nu;nz;om;pa;pe;pf;pg;ph;pk;pl ;pm;pn;pr;pt;pw;py;qa;re;ro;ru;rw;sa;sb;sc;sd;se;sg;sh;si;sj;sk;sl;sm;sn;so ;sr;st;su;sv;sy;sz;tc;td;tf;tg;th;tj;tk;tm;tn;to;tp;tr;tt;tv;tw;tz;ua;uk;ug ;um;us;uy;uz;va;vc;ve;vg;vi;vn;vu;wf;ws;ye;yt;yu;za;zm;zr;zw"; // get the characters after the last dot String tld = tmp.substring(tmp.lastIndexOf(".") + 1); // check for the length of the tld if(tld.length() < 2) if return EMAIL_INVALID_TLD; // check if it's valid if(valid_tld.indexOf(tld.toLowerCase()) == -1) if return EMAIL_INVALID_TLD; return EMAIL_OK; } // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------// F I L E R E L A T E D // -------------------------------------------------------------------------------------------------// -------------------------------------------------------------------------------------------------- /** * This method will remove this folder and all its subfolders . * @parameter File directory to delet e * @return true if the action completed successfull y */ public static boolean recursiveDelete(File rotten) { // if this is a file just delete if(!rotten.isDirectory()) if return rotten.delete(); boolean success = true; ; String String[] nodes = rotten.list(); for(int i=0; i<nodes.length; i++) for { File file = new File(rotten.getPath() + "/" + nodes[i]); if(file != null) if success = success && recursiveDelete(file); if(!success) if break; break } success = success && rotten.delete(); return success; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.tools.TextFiles java.lang.Object | +----mm.tools.TextFiles public class TextFiles extends java.lang.Object this class offers some file operations Author: Stefan Zier TextFiles() get(String) this method reads a textfile from the given path. TextFiles public TextFiles() get public static java.lang.String get(java.lang.String path) this method reads a textfile from the given path. If there is a error it only returns null. Parameters: path - the path to read from Returns: the string All Packages Class Hierarchy This Package Previous Next Index package mm.tools; import import import import java.io.FileReader; java.io.FileNotFoundException; java.io.IOException; java.util.Hashtable; /** * this class offers some file operation s * * @author Stefan Zier Zie * @created 12/15/98 - last modified 12/15/9 8 */ public class TextFiles { protected static Hashtable textfiles = null; /** * this method reads a textfile from the given path. If there is a erro r it * only returns null. * * @param path the path to read fro m * @return the string strin */ public static String get(String path) { if(path == null) if return new String(); String tmp = new String(); try { FileReader fr = new FileReader(path); while(!fr.ready()) while {} int offset = 0; char char[] ch = new char[1024]; while(offset != -1) while { offset = fr.read(ch); if(offset != -1) if tmp += String.valueOf(ch, 0, offset); } fr.close(); fr } catch(IOException io) {System.out.println("! getTextFileNoCache threw catch Exception reading \"" + path + "\"");} return tmp; } } h.) mm.webserver.servlets Dieses Package enthält Servlets. Klasse RessourceServlet StatusCheck Bedeutung Das Servlet von dem alle anderen Servlets erben. Verwaltet die Ressourcen. Überprüft den Status der Jini-Dienste All Packages Class Hierarchy This Package Previous Next Index Class mm.webserver.servlets.RessourceServlet mm.webserver.servlets.RessourceServlet public abstract class RessourceServlet Von diesem Servlet sollten alle Servlets erben. Es verwaltet gemeinsame Ressourcen wie den Session Manager und die RMI Verbindungen zu den Jini Services. Version: 1.0 Author: Stefan Zier configfile_location RessourceServlet() _service(HttpServletRequest, HttpServletResponse) this method is defined in the JSP standard and is called by the servlets service() method to process a request. getServletConfig() this method is defined in the JSP standard and has to return the servlet configuration. init(ServletConfig) Dies ist die Hauptinitmethode des Webservers. loadConfigFile() lädt die Konfigurationsdatei loadSessionManager() lädt den Session Manager service(ServletRequest, ServletResponse) this method is the service method derived from the servlet API. configfile_location public static final java.lang.String configfile_location RessourceServlet public RessourceServlet() loadConfigFile public void loadConfigFile() throws javax.servlet.ServletException lädt die Konfigurationsdatei loadSessionManager public void loadSessionManager() throws javax.servlet.ServletException lädt den Session Manager init public void init(javax.servlet.ServletConfig config) throws javax.servlet.ServletException Dies ist die Hauptinitmethode des Webservers. Alle gemeinsam genutzten Ressourcen werden hier initialisiert. Damit sie pro Webserver nur ein mal vorhanden sein müssen werden sie über den Servlet Context anderen Instanzen zur Verfügung gestellt. service public void service(javax.servlet.ServletRequest req, javax.servlet.ServletResponse res) throws javax.servlet.ServletExcepti this method is the service method derived from the servlet API. it is called for evert request that the server needs to answer. It is _required_ to call the _jspService() method from this method. Subclasses but not the JSP page may override it. _service public abstract void _service(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java this method is defined in the JSP standard and is called by the servlets service() method to process a request. Parameters: request - the HttpServletRequest object response - the HttpServletResponse object getServletConfig public final javax.servlet.ServletConfig getServletConfig() this method is defined in the JSP standard and has to return the servlet configuration. Returns: the ServletConfig object All Packages Class Hierarchy This Package Previous Next Index package mm.webserver.servlets; // Java Core imports import java.util.Hashtable; import java.util.StringTokenizer; import java.sql.SQLException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.rmi.RemoteException; // Java Servlet imports import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpUtils; import javax.servlet.http.HttpServlet; import javax.servlet.ServletException; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; // Project imports import mm.webserver.sessions.Session; import mm.webserver.sessions.SessionManager; import mm.webserver.sessions.SessionManagerImpl; import mm.jini.ServiceController; // Project service imports import mm.services.files.FileServer; import mm.services.game.GameServer; import mm.services.mailer.MailServer; import mm.services.msgboard.MessageBoardServer; import mm.services.chat.Chat; // Project tool imports import mm.tools.ConfigFile; import mm.tools.Static; /** * Von diesem Servlet sollten alle Servlets erben. Es verwaltet gemeinsam e Ressourcen wie * den Session Manager und die RMI Verbindungen zu den Jini Services . * * @author Stefan Zier Zie * @version 1.0 1. */ public abstract class RessourceServlet extends HttpServlet { // -- gemeinsame Ressourcen // Die Konfigurationsdatei protected ConfigFile configfile = null; // Die Serlvet Configuration protected ServletConfig config = null; // Der session manager protected SessionManager session_manager = null; // Der Jini Service Controller protected ServiceController service_controller = null; // Die Basis-URL der Webseite protected String baseurl = null; // Der servlet context ServletContext context = null; // Pfad zum Konfigurationsfile public final static String configfile_location = "/export/home/source/mm/webserver/webserver.properties"; // Konstanten für Service-Klassennamen protected final String FILE_SERVER = "mm.services.files.FileServer"; protected final String MSGBOARD_SERVER = "mm.services.msgboard.MessageBoardServer"; protected final String MAIL_SERVER = "mm.services.mailer.MailServer"; protected final String GAME_SERVER = "mm.services.game.GameServer"; protected final String CHAT_SERVER = "mm.services.chat.Chat"; // Standard-Sprache protected final String DEFAULT_LANGUAGE = "de"; protected final boolean debug = true; /** * lädt die Konfigurationsdatei Konfigurationsdate */ public void loadConfigFile() throws ServletException { try { configfile = new ConfigFile(configfile_location); } catch(java.io.IOException io) catch { forwardThrowable("mm: error loading config file", io); forwardThrowable } } /** * lädt den Session Manager Manage */ public void loadSessionManager() throws ServletException { try { // Sesion Manager mit timeout 10 Minuten erstellen session_manager = new SessionManagerImpl(600); } catch(IllegalArgumentException iae) catch { forwardThrowable("mm: error instantiating session manager", iae); forwardThrowable } } /** * Dies ist die Hauptinitmethode des Webservers. Alle gemeinsa m genutzten Ressourcen werden werde * hier initialisiert. Damit sie pro Webserver nur ein mal vorhande n sein müssen werden sie * über den Servlet Context anderen Instanzen zur Verfügung gestellt. */ public void init(ServletConfig config) throws ServletException { // Servlet Config speichern this.config = config; this // servlet context holen context = config.getServletContext(); // Konfigurationsdatei laden configfile = (ConfigFile)context.getAttribute("webserver.properties"); if(configfile == null) if { loadConfigFile(); loadConfigFile // publizieren context.setAttribute("webserver.properties", configfile); context } // this is the session management class session_manager = (SessionManager)context.getAttribute("mm.webserver.sessions.SessionManager" ); if if(session_manager == null) { loadSessionManager(); loadSessionManager // publizieren context.setAttribute("mm.webserver.sessions.SessionManager", context session_manager); } // baseurl setzen baseurl = configfile.get("server.baseurl"); service_controller = (ServiceController)context.getAttribute("mm.webserver.jini.ServiceControlle r"); if(service_controller == null) if { try { service_controller = new ServiceController(); // wir suchen klassen x,y und y service_controller.addSearchClass(FILE_SERVER); service_controller service_controller.addSearchClass(CHAT_SERVER); service_controller service_controller.addSearchClass(MSGBOARD_SERVER); service_controller service_controller.addSearchClass(MAIL_SERVER); service_controller service_controller.addSearchClass(GAME_SERVER); service_controller } catch(IOException ioe) catch { throw new ServletException("mm: error while instantiation the service controller", ioe); } } } /** * this method should be used to forward exceptions to the JVM. It als o prints the * stacktrace of the exception onto the screen. All JSP pages should us e this mechanism * to handle exceptions. exceptions * * @param text the text to display on the screen and for the newl y generated ServletException ServletExceptio * @param throwable the Throwable object to forwar d */ protected void forwardThrowable(String text, Throwable throwable) throws ServletException { //write the exception on the screen if debug is on throwable throwable.printStackTrace(System.out); // write the exception to the log file context context.log(text, throwable); // forward the exception to the servlet engine throw new ServletException(text, throwable); } /** * this method is the service method derived from the servlet API. it i s * called for evert request that the server needs to answer. It is * _required_ to call the _jspService() method from this method. * Subclasses but not the JSP page may override it . */ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; try { // get the querystring String query_string = request.getQueryString(); Hashtable querystring = null; // parse it to get the session id if(!Static.empty(query_string)) if { querystring = HttpUtils.parseQueryString(query_string); // save the Hashtable for latter use if(querystring != null) if request.setAttribute("querystring", querystring); request } String sid = null; // look up the session id if(querystring != null) if { String[] sids = (String[]) querystring.get("sid"); String if(sids !=null) if sid = sids[0]; // get the session if(!Static.empty(sid)) if { Session session = session_manager.find(sid); if(session != null) if { request.setAttribute("sid", sid); request request.setAttribute("session", session); request } } } // finally do the JSP page processing... _service(request, response); _service } catch(Throwable se) catch { // Stacktrace auf die Fehlerkonsolre schreiben System.err.println("mm: " + this.getClass().getName() + System ".service() threw Exception:"); se.printStackTrace(System.err); se // ggf. Stacktrace auf den Webbrowser schreiben res.setContentType("text/html"); res PrintWriter out = res.getWriter(); if(debug) if { out.println("<html><head><title>MM ERROR</title></head><body out bgcolor=\"#FFFFFF\">"); out.println("<h3>JSP Page exception in class " + out this.getClass().getName() + "</h3><font size=\"-1\"><tt>"); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); se.printStackTrace(pw); se String se_html = Static.replace(sw.toString(), "\n", "<br>\n"); out out.println(se_html); out.println("</tt></font></body></html>"); out } else { out.println("M&uuml;ll"); out } } } /** * this method is defined in the JSP standard and is called by th e servlets servlet * service() method to process a request . * * @param request the HttpServletRequest objec t * @param response the HttpServletResponse objec t */ public abstract void _service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; /** * this method is defined in the JSP standard and has to return th e servlet servle * configuration. configuration * * @return the ServletConfig object objec */ final public ServletConfig getServletConfig() { return config; } } All Packages Class Hierarchy This Package Previous Next Index Class mm.webserver.servlets.StatusCheck mm.webserver.servlets.RessourceServlet | +----mm.webserver.servlets.StatusCheck public class StatusCheck extends RessourceServlet Diese Klasse initialisiert alle shared Ressources der JspSuperClass und zeigt den Status der Services an. Version: 1.0 Author: Stefan Zier StatusCheck() _service(HttpServletRequest, HttpServletResponse) Gibt den Status der Services aus. checkService(String, BlockList) Schreibt eine Zeile der Statustabelle StatusCheck public StatusCheck() _service public void _service(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse res) Gibt den Status der Services aus. Parameters: req - der Request res - die Response Overrides: _service in class RessourceServlet checkService public void checkService(java.lang.String servicename, com.zier.fasthtml.tools.BlockList blocklist) Schreibt eine Zeile der Statustabelle Parameters: servicename - der Service Name der gecheckt werden soll out - der PrintWriter in den das HTML geschrieben werden soll All Packages Class Hierarchy This Package Previous Next Index package mm.webserver.servlets; // Java core imports import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.rmi.RemoteException; // Servlet extension imports import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // Project imports import mm.tools.AliveCheckable; import mm.tools.Static; // FastHtml imports import com.zier.fasthtml.tools.BlockList; import com.zier.fasthtml.Page; /** * Diese Klasse initialisiert alle shared Ressources der JspSuperClass * und zeigt den Status der Services an. * * @author Stefan Zier Zie * @version 1.0 1. * @deprecated Das RessourceServlet ist aktuell aktuel */ public class StatusCheck extends RessourceServlet { /** * Gibt den Status der Services aus . * * @param req der Request Reques * @param res die Response Respons */ public void _service(HttpServletRequest req, HttpServletResponse res) { /* try { res.setContentType("text/html"); res.setContentType("text/html") checkService(MAIL_SERVER, out); out) checkService(FILE_SERVER, out); out) checkService(MSGBOARD_SERVER, out); out) checkService(CHAT_SERVER, out); out) checkService(GAME_SERVER, out); out) out.println("</table>"); out.println("</table>") } catch(IOException ioe) ioe { } */ } /** * Schreibt eine Zeile der Statustabelle Statustabell * * @param servicename der Service Name der gecheckt werden sol l * @param out der PrintWriter in den das HTML geschrieben werden sol l */ public void checkService(String servicename, BlockList blocklist) { /* blocklist.set("servicename", servicename); blocklist AliveCheckable ac = (AliveCheckable) (AliveCheckable service_controller.getService(servicename) service_controller.getService(servicename); if(ac == null) null out.println(font_red + "null"); "null") else els { try tr { if(ac.isAlive()) if(ac.isAlive() { blocklist.set("color", "#FFFFFF"); "#FFFFFF") blocklist.set("status", "OK"); "OK") } else els { blocklist.set("color", "#FF0000"); "#FF0000") blocklist.set("status", "down"); "down") } } catch(RemoteException re) re { blocklist.set("color", "#FF0000"); "#FF0000") blocklist.set("status", "RemoteException:<br><tt>"); "RemoteException:<br><tt>") // Exception Stacktrace in HTML umwandeln (CR/LFs) (CR/LFs StringWriter sw = new StringWriter(); StringWriter() PrintWriter pw = new PrintWriter(sw); PrintWriter(sw) re.printStackTrace(pw); re.printStackTrace(pw) out.println(Static.replace(sw.toString(), "\n", "<br>")); "<br>")) out.println("</tt>"); out.println("</tt>") } } out.println("</font></td></tr>"); */ } } i.) mm. webserver.sessions Dieses Package enthält Klassen, die für das Session Management zuständig sind. Wir entschieden uns, ein eigenes Session Management zu entwickeln, obwohl Servlets diese Funktionalität bereits bieten. So ist es möglich, einen verteilten Session-ManagementMechanismus über mehrere Webserver hinweg zu implementieren. Klasse Session SessionManager SessionManagerImpl Bedeutung Eine Sitzung Interface zum Session Manager Eine Implementation eines Session Managers für den Standalonebetrieb All Packages Class Hierarchy This Package Previous Next Index Class mm.webserver.sessions.Session java.lang.Object | +----mm.webserver.sessions.Session public class Session extends java.lang.Object Diese Klasse stellt eine Sitzung dar. Sie kann beliebige Objekte die mit der Sitzung in Verbindung stehen unter einem Schlüsselwert abspeichern. Version: 1.0 Author: Stefan Zier Session(String) Konstruktor. create() Creator Methode. getAttribute(String) Liest ein Attribut. getSessionId() Accessor für die Session ID outdated(int) Prüft, ob die Session veraltet ist. setAttribute(String, Object) Setzt ein Attribut. touch() Aktualisiert die Zeit an dem das Objekt zuletzt aktualisiert wurde. Session public Session(java.lang.String sid) Konstruktor. Dient dazu eine Session zu erzeugen. Parameters: sid - die Session ID create public static Session create() Creator Methode. Diese Methode generiert automatisch ein Session und erzeugt auch eine Session ID. Returns: ein neues Session Objekt setAttribute public void setAttribute(java.lang.String key, java.lang.Object value) Setzt ein Attribut. Parameters: key - der Name des Attributes value - das Objekt getAttribute public java.lang.Object getAttribute(java.lang.String key) Liest ein Attribut. Parameters: key - der Name des Attributes Returns: das Attribut getSessionId public java.lang.String getSessionId() Accessor für die Session ID Returns: die Session ID touch public void touch() Aktualisiert die Zeit an dem das Objekt zuletzt aktualisiert wurde. outdated public boolean outdated(int timeout) Prüft, ob die Session veraltet ist. Parameters: timeout - die Zeit nach der eine Session veraltet sein soll in sekunden Returns: true wenn die Session veraltet ist. All Packages Class Hierarchy This Package Previous Next Index package mm.webserver.sessions; import import import import java.util.Hashtable; java.util.Date; java.util.GregorianCalendar; java.util.Random; /** * Diese Klasse stellt eine Sitzung dar. Sie kann beliebige Objekte die mi t der Sitzung in * Verbindung stehen unter einem Schlüsselwert abspeichern . * * @author Stefan Zier Zie * @version 1.0 1. */ public class Session { private String sid = null; private Hashtable attributes = null; private Date last_touched = null; private static Random random = new Random(); /** * Konstruktor. Dient dazu eine Session zu erzeugen . * * @param sid die Session ID I */ public Session(String sid) { // Session ID Speichern this.sid = sid; this // Hashtable anlegen attributes = new Hashtable(); // last_touched initialisieren last_touched = new Date(); } /** * Creator Methode. Diese Methode generiert automatisch ein Session un d erzeugt auch eine ein * Session ID. ID * * @return ein neues Session Objekt Objek */ public static Session create() { // eine Session ID generieren StringBuffer sid = new StringBuffer(); byte[] numbers = new byte[13]; byte String sid_string = null; random.nextBytes(numbers); random int count = 0; while while(count < 13) { // append the char representation of this number to the buffer String number = (new Integer(numbers[count])).toString(); sid.append(number.substring(number.length() - 1)); sid count++; count // the dashes if(count == 3 || count == 8) if sid.append("-"); sid } sid_string = sid.toString(); return new Session(sid_string); } /** * Setzt ein Attribut. Attribut * * @param key der Name des Attribute s * @param value das Objekt Objek */ public void setAttribute(String key, Object value) { attributes.put(key, value); attributes } /** * Liest ein Attribut. Attribut * * @param key der Name des Attribute s * @return das Attribut Attribu */ public Object getAttribute(String key) { return attributes.get(key); } /** * Accessor für die Session ID I * * @return die Session ID I */ public String getSessionId() { return sid; } /** * Aktualisiert die Zeit an dem das Objekt zuletzt aktualisiert wurde . */ public void touch() { last_touched = new Date(); } /** * Prüft, ob die Session veraltet ist . * * @param timeout die Zeit nach der eine Session veraltet sein soll i n sekunden sekunde * @return true wenn die Session veraltet ist . */ public boolean outdated(int timeout) { // Kalender auf aktuelle Zeit setzen GregorianCalendar gc = new GregorianCalendar(); gc.setTime(new Date()); gc // die Zeit des Kalenders um den Timeout zurücksetzen gc.add(gc.SECOND, 0 - timeout); gc // anderen Kalender auf letzten Touch setzen GregorianCalendar last = new GregorianCalendar(); last.setTime(last_touched); last // vergleichen: ist jetzt-timeout > last touched ist die Session veraltet. if(gc.after(last)) if return true; return false; } } All Packages Class Hierarchy This Package Previous Next Index Interface mm.webserver.sessions.SessionManager public interface SessionManager Das Interface zu einem Session Manager. So ist es Möglich später ggf. statt des lokalen einen RMI-Session Manager einzusetzen, der mehrere Webserver ermöglicht. Version: 1.0 Author: Stefan Zier add(Session) fügt eine neue Session hinzu. find(String) Sucht nach eine Session. add public void add(Session session) fügt eine neue Session hinzu. Parameters: session - die neue Session find public Session find(java.lang.String sid) Sucht nach eine Session. Muss die Session auch touchen. Parameters: sid - die Session Id Returns: das Session Objekt oder null wenn die Session nicht mehr existiert. All Packages Class Hierarchy This Package Previous Next Index package mm.webserver.sessions; /** * Das Interface zu einem Session Manager. So ist es Möglich später ggf . statt des lokalen lokale * einen RMI-Session Manager einzusetzen, der mehrere Webserver ermöglicht . * * @author Stefan Zier Zie * @version 1.0 1. */ public interface SessionManager { /** * fügt eine neue Session hinzu. hinzu * * @param session die neue Session Sessio */ public void add(Session session); /** * Sucht nach eine Session. Muss die Session auch touchen. * * @param sid die Session Id I * @return das Session Objekt oder null wenn die Session nicht meh r existiert. existiert */ public Session find(String sid); } All Packages Class Hierarchy This Package Previous Next Index Class mm.webserver.sessions.SessionManagerImpl java.lang.Object | +----mm.webserver.sessions.SessionManagerImpl public class SessionManagerImpl extends java.lang.Object implements SessionManager Diese Klasse ist ein Session Manager. Sie verwaltet Sitzungen. Eine Sitzung wird durch das Session-Objekt repräsentiert. Nach einer gegebenen Zeitspanne ohne Seitenabruf ist eine Session hinfällig und wird gelöscht. Version: 1.0 Author: Stefan Zier SessionManagerImpl(int) Konstruktor. add(Session) fügt eine neue Session hinzu. find(String) Sucht nach eine Session. SessionManagerImpl public SessionManagerImpl(int timeout) Konstruktor. Parameters: timeout - die Zeit in Sekunden nach der eine Session gelöscht wird add public void add(Session session) fügt eine neue Session hinzu. Parameters: session - die neue Session find public Session find(java.lang.String sid) Sucht nach eine Session. Muss die Session auch touchen. Parameters: sid - die Session Id Returns: das Session Objekt oder null wenn die Session nicht mehr existiert. All Packages Class Hierarchy This Package Previous Next Index package mm.webserver.sessions; import java.util.Hashtable; /** * Diese Klasse ist ein Session Manager. Sie verwaltet Sitzungen. Ein e Sitzung wird durch durc * das Session-Objekt repräsentiert. Nach einer gegebenen Zeitspanne ohn e Seitenabruf ist is * eine Session hinfällig und wird gelöscht. * * @author Stefan Zier Zie * @version 1.0 1. */ public class SessionManagerImpl implements SessionManager { private CleanerThread cleaner = null; private Hashtable sessions = null; /** * Konstruktor. * * @param timeout die Zeit in Sekunden nach der eine Session gelösch t wird wir */ public SessionManagerImpl(int timeout) { // Sessionliste erzeugen sessions = new Hashtable(); // CleanerThread starten cleaner = new CleanerThread(timeout); cleaner.start(); cleaner } /** * Interne Klasse, die die Sessionliste nach veralteten Einträge n durchkämmt. durchkämmt * * @author Stefan Zier Zie * @version 1.0 1. */ private class CleanerThread extends Thread { private long timeout = -1; /** * Konstruktor. Konstruktor * * @param timeout die Zeit in Sekunden nach der eine Session gelösch t wird wir */ public CleanerThread(int timeout) { // Timeout speichern this.timeout = timeout; this // Korrektheit überprüfen if(timeout <= 0) if throw new IllegalArgumentException("mm: timeout must be bigger than 0 !"); } /** * Der Inhalt des Threads. */ public void run() { try { // Pro Minute einmal die Session Liste durchgehen sleep(60000); sleep } catch(InterruptedException e) catch {} } } /** * fügt eine neue Session hinzu. hinzu * * @param session die neue Session Sessio */ public void add(Session session) { if(session == null) if return; return sessions.put(session.getSessionId(), session); sessions } /** * Sucht nach eine Session. Muss die Session auch touchen. * * @param sid die Session Id I * @return das Session Objekt oder null wenn die Session nicht meh r existiert. existiert */ public Session find(String sid) { // Session lesen Session tmp = (Session)sessions.get(sid); // ggf. touchen if(tmp != null) if tmp.touch(); tmp return tmp; } }