![]() |
|
Um den praktischen Einsatz dieser Konzepte zu demonstrieren, wird nun ein Anwendungsbeispiel entwickelt, das die Aufgabe hat, die Kommunikation mit einer entfernten Datenbank zu realisieren. Um das Beispiel nicht zu überfrachten, wird hier lediglich die Kommunikationsfunktionalität realisiert, nicht aber der eigentliche Datenbankzugriff. Das vollständige Beispiel, inklusive der Datenbankanwendung, ist in Kapitel 14 dargestellt. In Erweiterung des Beispiels, das in Kapitel 11.4 vorgestellt wurde, wird die Anwendung nun unter Einbezug aktiver Objekte und des Callback-Mechanismus erweitert. Die Schritte, die bei der Ausführung der Anwendung durchlaufen werden, sind daher die folgenden:
Callback-Operationen Im Folgenden wird zunächst die Funktionalität realisiert, dass der Server auf die Anfrage eines Clients mit einem Callback antworten kann, dass er also vor dem Datenbankzugriff ein Passwort verlangt. Hierzu sind dieselben Schritte zu durchlaufen, wie bei der Entwicklung der bisher betrachteten RMI-Anwendungen. Diese Schritte werden im Folgenden detailliert betrachtet. Definition des Interfaces Zur Abfrage eines Passworts muss zuerst ein zweites Interface definiert werden, das eine Methode enthält, um das Passwort vom Client anzufordern. Dieses
package server2; //Anfordern des Passworts } package server2; //Aktualisierung des Spielstands } Nach der Definition der Interfaces müssen wiederum die Implementierungsklassen erzeugt werden. Implementierungsklassen Die erste Implementierungsklasse wird nun beim Server gespeichert und implementiert das oben angegebene Interface DatabaseUpdate2. Die Klasse DatabaseUpdateImpl2 implementiert das Interface DatabaseUpdate2, die Klasse PasswortImpl2 implementiert das Interface Passwort2.
package server2; public DatabaseUpdateImpl2 () throws RemoteException { } String clientPasswort=null; //Schlage Passwort in Datenbank nach System.out.println("Passwort OK"); //Hier wird noch keine Funktion realisiert } } }
package server2; private String obname, pwd; } String passwort = null; } } Es ist zu beachten, dass die Klasse DatabaseUpdateImpl2 auf die Klasse PasswortImpl2 zurückgreift, die aber beim Client ausgeführt wird. Die Erzeugung der Stubs und des Skeletons, die Implementierung und Übersetzung des Server-Programms und der Start der Registry bzw. des Servers sind identisch mit denen des vorangegangenen Beispiels. Anders verhält sich dies bei der Implementierung des Clients. Hier muss eine Instanz der Klasse PasswortImpl2 erzeugt werden, die an den Server übergeben wird. Mittels dieser Instanz kann der Server dann die Methode passwort auf dem Client aufrufen.
package server2; public static void main (String args[]) { DatabaseUpdate2 du = null; System.err.println("Eingabe: java server.Klient2 <Server> <Port>"); } //Neuer Security Manager try { //Binden der Objektinstanz an entfernte Registry } catch (Exception e) { System.err.println("Keine Verbindung"+e); } try { pwd = new PasswortImpl2(); //Schicke 1, also Spieler hat gewonnen } catch (Exception e) { System.err.println("Problem in Remote-Methode"); } } } Objektaktivierung Im Folgenden soll die Funktionalität der Objektaktivierung realisiert werden. Ein Objekt soll dann aktiv werden, wenn es benötigt wird. Im Beispiel wird daher die Passwortüberprüfung in zwei Teile aufgeteilt: Die erste Klasse erzeugt eine aktivierbare Instanz zur Eingabe von Passwörtern. Nach der Abarbeitung terminiert diese Klasse. Die zweite Klasse greift auf die aktivierbare Instanz zu und bewirkt beim Zugriff die tatsächliche Aktivierung. Ebenso wie das vorangegangene Beispiel ist dieses Beispiel darauf angelegt, eine beidseitige Kommunikation umzusetzen. Aus diesem Grund muss wiederum ein Client-Objekt an den Server übergeben werden, der mittels dieses Objekts Methoden auf dem Client ausführen kann. Zunächst wird wieder das Interface des aktivierbaren Objekts definiert. Es ist zu beachten, dass nun das Passwort3-Objekt Teil der Client-Klasse ist.
package server3; //Aktualisierung des Spielstands } Die Implementierung dieser Klasse unterscheidet sich nun von der vorangegangenen Implementierung. In diesem Fall wird nicht das Interface UnicastRemoteObject, sondern das Interface Activatable implementiert.
package server3; public DatabaseUpdateImpl2 (ActivationID id, MarshalledObject daten) throws RemoteException { //Registrierung bei rmid und Export an anonymen Port } String clientPasswort=null; //Schlage Passwort in Datenbank nach System.out.println("Passwort OK"); } } } Die Client-Seite dieses Beispiels ähnelt stark der Client-Seite des vorangegangenen Beispiels, da Client-Aufrufe eine entfernte Referenz erfordern und der Client daher nicht weiß, dass ein Objekt überhaupt aktivierbar ist. Die Server-Seite ist allerdings unterschiedlich, da der Server das aktivierbare Objekt registrieren muss, bevor er terminiert. Zunächst wird ein Security-Manager eingerichtet. Mittels der Methode put, die Teil der Klasse Properties ist, wird eine Policy-Datei assoziiert, die in diesem Fall der Anwendung alle Rechte einräumt. Es sei aber darauf hingewiesen, dass dieses Vorgehen nur im Testfall sinnvoll ist. Real sollte eine Anwendung niemals mit allen Rechten ausgestattet werden. Die Klasse, die zur Einrichtung des Objekts dient, ist komplexer als Klassen, die UnicastRemoteObject erweitern. Hierbei wird eine Textrepräsentation der URL verwendet, die den Speicherort der aktivierbaren Klasse DatabaseUpdateImpl3 angibt. Ein ActivationGroupID-Objekt wird an ein ActivationDesc-Objekt übergeben, das von rmid registriert wird. Jede neue VM, die mit rmid gestartet wird, wird dann nur Objekte einer einzelnen ActivationGroupID aktivieren. Läuft eine VM bereits, die mit der Klasse einer ActivationGroupID assoziiert ist, so wird das Objekt in der VM erzeugt, anstatt eine neue VM zu starten zu müssen. ActivationGroupID-Objekte ermöglichen daher eine genaue Kontrolle darüber, in welcher VM ein aktiviertes Objekt läuft. In diesem Zusammenhang sei auch auf die Verwendung der Klasse MarshalledObject hingewiesen. Wird ein UnicastRemoteObject-Objekt verwendet, so können Kommandozeilenargumente ohne Probleme an die Implementierungsklasse übergeben werden, da das Server-Programm, das diese Argumente entgegennimmt, während der Lebenszeit der Implementierung des entfernten Objekts immer läuft. Bei aktivierbaren Objekten hingegen kann die einrichtende Klasse nach der Registrierung des Aktivierungsdeskriptors beim RMI-Dämon und nach der Registrierung des Stubs bei der Registry sofort terminieren. Die Klasse MarshalledObject bietet einen flexiblen Mechanismus zur persistenten Übergabe von Daten bzw. zur Initialisierung der Daten über ein ActivationDesc-Objekt, das über rmid registriert ist, an, anstatt Werte hart in der Implementierungsklasse einer Datei zu verdrahten. Im weiteren Verlauf des Programms wird die Methode Activatable.register() aufgerufen, die das ActivationDesc-Objekt an rmid übergibt. Der Deskriptor der Aktivierungsgruppe beinhaltet alle Informationen, die rmid benötigt, um eine Instanz einer aktivierbaren Klasse zu erzeugen. Die Methode Activatable.register() liefert eine entfernte Referenz zurück, die zur Registrierung des aktivierbaren Objekts in der Registry verwendet wird.
package server3; public static void main (String args[]) { if (args.length != 3) { System.err.println("Eingabe: java server.Datenbank <Server> <Port><Pfad zu Klassendateien>"); } //Security Manager try { //Instanz des Objekts anlegen ActivationGroupID groupID= ActivationGroup.getSystem().registerGroup(n ew ActivationGroupDesc(env, null)); //Verwende verpacktes Objekt, um dem aktivierten Objekt //Registrierung System.out.println("rmid registriert"); //Binden Naming.rebind(url,du); }catch (Exception e) { e.printStackTrace(); } } } Auf der Client-Seite wird ein Passwort3-Objekt an ein DatabaseUpdate3-Objekt übergeben. Die Klasse Passwort3 ist sehr einfach.
package server3; private String pw; } String pwd = null; //Erfrage Passwort der Datenbank beim Client return pwd; } } Zum Abschluss des Beispiels muss die Klasse entwickelt werden, die das aktivierbare Objekt aufruft. Diese Klasse erzeugt eine Instanz eines Passwort3-Objekts und verlangt von der Registry eine Referenz auf ein DatabaseUpdate3-Objekt.
package server3; public static void main (String args[]) { DatabaseUpdate3 du = null; System.err.println("Eingabe: java server.Klient3 <Server> <Port>"); } //Neue Passwort3-Instanz //Neuer Security Manager try { //Binden der Objektinstanz an entfernte Registry du = (DatabaseUpdate3)Naming.lookup(url); } catch (Exception e) { System.err.println("Keine Verbindung"+e); } try { //Schicke 1, also Spieler hat gewonnen } catch (Exception e) { System.err.println("Problem in Remote-Methode"); } } } Es sollte nicht vergessen werden, die Dateien in der richtigen Reihenfolge aufzurufen. Zuerst müssen rmiregistry und rmid gestartet werden und anschließend ein Passwortobjekt mittels der Anweisung java server3.PasswortStart3 localhost <Port> <Verzeichnis> angelegt werden. Erst im Anschluss kann auf das entfernte Objekt zugegriffen werden. |
|
|