![]() |
|
Im Rahmen dieses Unterkapitels werden zwei Anwendungen entwickelt. Zunächst wird das Beispiel aus Kapitel 11.4 (einfache Kommunikation über RMI) in ein CORBA-Beispiel überführt. Anschließend wird erläutert, wie Callbacks in CORBA implementiert werden können. Kommunikation in CORBA Die grundlegenden Schritte bei der Entwicklung und Verwendung eines CORBA-Dienstes sehen wie folgt aus:
Der wichtigste Schritt ist hierbei der erste, da hier das grundlegende Design des Dienstes implementiert wird. Im Folgenden werden die einzelnen Schritte im Detail erläutert. Erzeugen einer IDL-Datei Die im Folgenden erläuterte Anwendung dient der Aktualisierung einer Datenbank. Hierzu ruft der Client einen CORBA-Dienst auf und übergibt einen Parameter, der angibt, ob er das Spiel gewonnen oder verloren hat. Die IDL-Definition sieht dann wie folgt aus:
module DatabaseUpdate { //Aktualisierung des Spielstands long updateDatenbank(in long spiel); }; }; Der Parameter wird hierbei als Call-by-Value übergeben. Übersetzen der IDL-Datei Will man die IDL-Datei, die in diesem Fall DatabaseUpdate.idl heißen könnte, übersetzen, so ist die folgende Anweisung anzugeben:
idltojava -fno-cpp DatabaseUpdate.idl Die Option -fno-cpp muss verwendet werden, um den C/C++-Präprozessor auszuschalten. Der IDL-Compiler überprüft die IDL-Datei und erzeugt die entsprechenden Verzeichnisse und Java-Dateien. Hierbei wird der Bezeichner module zur Angabe eines Packages verwendet, der Bezeichner interface zur Angabe der Java-Interfaces. Übersetzen der generierten Java-Klassen Anschließend müssen die erzeugten Java-Klassen übersetzt werden. Hierbei können keine Syntaxfehler auftreten, da der Code automatisch erzeugt wurde. Zur Übersetzung der Dateien muss die folgende Anweisung verwendet werden:
javac DatabaseUpdate\*.java Erzeugen der Implementierungsklasse(n) Im nächsten Schritt muss eine Java-Klasse entwickelt werden, die das Interface implementiert, das vom IDL-Compiler erzeugt wurde. Hierbei empfiehlt es sich, zur Speicherung der neu entwickelten Dateien ein anderes Verzeichnis zu wählen als das der automatisch erzeugten Dateien. Die Implementierung muss nun erfolgen, indem die Datei _FunctionsImplBase.java verwendet wird, die der IDL-Compiler angelegt hat. Diese Klasse ist abstrakt und erweitert die Klasse org.omg.CORBA.portable.ObjectImpl. Indem diese Klasse weiterentwickelt wird, können die geeigneten Skeleton-Methoden eingefügt werden, die für ORB-Aufrufe an die Implementierungsmethoden notwendig sind. Die Implementierungsklasse muss einen Rumpf für jede Methode zur Verfügung stellen, die im Interface beschrieben ist:
/* package DatabaseUpdate; int updateDatenbank(int spiel); } Die Implementierungsklasse muss daher die Methode updateDatenbank enthalten. Per Konvention fügt die Implementierungsklasse das Suffix Impl an den Namen des Interfaces an. Die Realisierung dieser Klasse ist im Folgenden angegeben.
//Implementierung des Interfaces Functions.java public FunctionsImpl() { } public int updateDatenbank(int spiel){ //Hier muss der Datenbankzugriff erfolgen } } Erzeugen des Implementierungs-Servers Im nächsten Schritt muss die Server-Klasse erzeugt werden, die das implementierte Objekt beim ORB und beim Naming Service registriert und die die Verbindung zur Implementierungsklasse herstellt. Wie schon die Implementierungsklasse wird auch diese Klasse nicht vom Compiler idltojava erzeugt, sondern muss selbst implementiert werden.
//Server-Klasse public static void main (String args[]){ try { //Erzeugen des Server-ORB //Implementierungsobjekt erstellen //Handle fuer Name Server erzeugen NamingContext nc = NamingContextHelper.narrow(oref); //Binden der Objektreferenz //Aufrufe des Clients erwarten sync.wait(); } }catch (Exception e) { System.err.println("Fehler: "+e); } } } Dieser Server ist ein Beispiel für einen transienten Objekt-Server, da die Objektreferenz und der ORB erfordern, dass die Server-Anwendung lauffähig bleibt, nachdem sie einmal gestartet wurde. Erzeugen der Client-Anwendung Die Client-Anwendung lokalisiert eine Referenz auf das Functions-Objekt, indem der Naming Service verwendet wird. Die zurückgegebene Objektreferenz ist eine CORBA-Referenz, die auf den richtigen Referenztyp eingeschränkt (Narrowing) werden muss. Der Server hat diesen Namen als DB veröffentlicht, so dass der Client in der Folge genau diesen Namen anfordern muss.
//Client-Klasse public static void main (String args[]){ try { //Erzeugen des Client-ORB //Handle fuer Name Server erzeugen NamingContext nc = NamingContextHelper.narrow(oref); //Finden der Objektreferenz //Helper-Klasse verwenden, um Casting vorzunehmen. //Aufrufe ausfuehren, 0 heisst Spieler hat verloren }catch (Exception e) { System.err.println("Fehler: "+e); } } } Der Aufruf des Naming Services liefert eine generelle CORBA-Referenz zurück, die in den geeigneten Typ umgewandelt werden muss, bevor Methodenaufrufe erfolgen können. Zusätzlich ruft der Client Methoden eines Stubs auf, der die Objektreferenz repräsentiert. Die automatisch erzeugte Helper-Klasse erleichtert diese Aufgabe, da die narrow-Methode dazu verwendet werden kann, eine Referenz auf den Functions-Stub zu erhalten. Mit dieser Referenz kann die Methode updateDatenbank aufgerufen werden. Übersetzen der Java-Klassen Nachdem alle Implementierungen erfolgt sind, müssen die Quelldateien übersetzt werden. Hierzu kann die folgende Anweisung verwendet werden:
javac -d . FunctionsImpl.java Server.java Client.java Aufruf des Naming Services tnameserv Der Naming Service tnameserv ist Teil von Java 1.2. Dieser Dienst hört einen Port ab (standardmäßig den Port 900), um Anfragen nach Namen und nach Objektbindungen zu empfangen. Indem ein Argument übergeben wird, kann die Port-Nummer verändert werden.
tnameserv -ORBInitialPort 2000 Nach dem Aufruf gibt der Naming Service die sog. Interoperable Object Reference (IOR) und die Port-Nummer zurück, die der Naming Service überwacht. Der IOR-String stellt eine weitere Möglichkeit zur Verfügung, CORBA-Objektreferenzen zu lokalisieren, da der String Informationen über den Speicherort eines Objekts enthält, unter anderem den Host-Namen und die IP-Adresse bzw. welche Dienste ein Objekt anbietet. Ein IOR kann daher bspw. dazu eingesetzt werden, um Objektreferenzen zwischen zwei ORBs zu übergeben, ohne den Naming Service zur Lokalisierung einer Objektreferenz einzusetzen. Hierzu veröffentlicht der Server eine sog. Stringified-Objektreferenz, also eine String-Repräsentation einer CORBA-Objektreferenz, indem die Objektreferenz in einen String umgewandelt wird. Aufruf des Servers Anschließend muss der Server mit der Port-Nummer aufgerufen werden, unter der der Naming Service erreichbar ist.
java Server -ORBInitialPort 2000 Aufruf des Clients Auch der Client muss mit der Port-Nummer des Naming Service aufgerufen werden. Nach dem Start des Clients ruft dieser die Aktualisierungsfunktion der Datenbank beim Server auf und wird anschließend beendet. Implementiert man daher bspw. diese Funktionalität beim Server, so kann nach einem gewonnenen oder verlorenen Spiel durch Verwendung von CORBA-Objekten vermerkt werden, wie oft ein Spieler gewonnen oder verloren hat. Zum Aufruf des Clients ist die folgende Anweisung zu verwenden:
java Client -ORBInitialPort 2000 Callbacks in CORBA Nachdem die einfache Kommunikation über CORBA erläutert wurde, soll im Folgenden betrachtet werden, wie Callbacks in CORBA erfolgen. Auch hier wird das Passwort-Beispiel eingesetzt. Das vorangegangene Beispiel wird daher so erweitert, dass der Server vor dem Eintrag in die Datenbank eine Autorisierung verlangt. Hierzu verwendet er einen Callback. Auch diese Implementierung läuft in den bereits beschriebenen Schritten ab. Erzeugen einer IDL-Datei Die Erzeugung der IDL-Datei ähnelt dem ersten Beispiel. Wie auch schon in RMI muss nun aber die Menge der Eingabeparameter der Funktion updateDatenbank verändert werden. Hierzu wird zusätzlich eine Objektreferenz übergeben. Zusätzlich wird ein neues Interface definiert, das vom Client implementiert werden muss. Der Server kann anschließend mit dem Client kommunizieren, indem dieser die Methode beim Client aufruft.
module DatabaseUpdate2 { //Client-Funktion void callback(out string passwort); //Aktualisierung des Spielstands long updateDatenbank(in ClientCallback objRef,in long spiel); }; }; Erzeugen der Implementierungsklasse(n) Die nun folgende Implementierung des Interfaces Functions.java muss die geänderte Anzahl der Parameter umsetzen.
import DatabaseUpdate2.*; public FunctionsImpl() { } String pwd = "PWDREQUEST"; } } Erzeugen des Implementierungs-Servers Im nächsten Schritt muss die Server-Klasse erzeugt werden, die das implementierte Objekt beim ORB und beim Naming Service registriert und die die Verbindung zur Implementierungsklasse herstellt. Diese Klasse entspricht als Verwaltungsklasse genau der Klasse, die im vorherigen Beispiel definiert wurde.
//Server-Klasse public static void main (String args[]){ try { //Erzeugen des Server-ORB //Implementierungsobjekt erstellen //Handle fuer Name Server erzeugen NamingContext nc = NamingContextHelper.narrow(oref); //Binden der Objektreferenz //Aufrufe des Clients erwarten sync.wait(); } }catch (Exception e) { System.err.println("Fehler: "+e); } } } Erzeugen der Client-Anwendung Die Client-Anwendung muss derart erweitert werden, dass das Callback-Interface implementiert wird. Im Folgenden ist die Realisierung in Form einer inneren Klasse angegeben. Hierbei sei auf die Verwendung der Holder-Klasse aufmerksam gemacht. Diese muss benutzt werden, da der Parameter mittels Call-by-Reference übergeben wird.
//Client-Klasse public void callback(org.omg.CORBA.StringHolder passwort){ //weitere Verarbeitung, Passworteingabe ueber Fenster } } public class Client { public static void main (String args[]){ try { //Erzeugen des Client-ORB //Handle fuer Name Server erzeugen //Finden der Objektreferenz //Helper-Klasse verwenden, um Casting vorzunehmen. //Objekt vorbereiten //Aufrufe ausfuehren, 0 heisst Spieler hat verloren int gewonnen = fun.updateDatenbank(callbackRef, 0); }catch (Exception e) { System.err.println("Fehler: "+e); } } } Aufruf der Komponenten Der Aufruf der Komponenten erfolgt in derselben Art und Weise wie beim ersten Beispiel. |
|
|