19.1.3 | Anwendungsentwicklung am Beispiel RMI |
Bei der Anwendungsentwicklung mit einem Broker werden immer folgende Schritte durchgeführt:Diese Schritte werden im Folgenden am Beispiel einer einfachen, auf RMI-basierenden, »Hello World«-Anwendung demonstriert.
- Zunächst wird definiert, welche Methoden der Server zur Verfügung stellen soll. Alle diese Methoden werden in einer logischen Server-Schnittstelle definiert.
- Danach wird die Server-Schnittstelle in der Server-Implementierung ausprogrammiert.
- Über einen Compiler, der üblicherweise mit dem Broker mitgeliefert wird, werden anschließend die Proxy-Objekte aus der Schnittstellendefinition erzeugt.
- Zur Laufzeit melden sich serverseitige Objekte üblicherweise bei einem Namensdienst an. Die Registrierung erfolgt in der Regel über einen logischen Namen.
- Unter diesem Namen kann eine Client-Anwendung einen Verweis eines zuvor registrierten Server-Objekts abrufen und anschließend Methoden des Server-Objekts aufrufen.
Folgender Code zeigt die Definition einer Server-Schnittstelle in RMI:import java.rmi.Remote; import java.rmi.RemoteException; public interface HelloServer extends Remote { public String sayHello() throws RemoteException; }Bei RMI werden alle Methoden, die serverseitig ausgeführt werden sollen, in einem Java-Interface definiert, das von java.rmi.Remote abgeleitet ist. Jede Methode, die dieses Interface definiert, kann außerdem eine RemoteException auslösen, da die Methoden über das Netzwerk ausgeführt werden.
Die Implementierung der Server-Schnittstelle könnte folgenden Code haben:import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; import java.rmi.Naming; import de.dpunkt.rmi.hello.HelloServer; public class HelloImpl extends UnicastRemoteObject implements HelloServer { public HelloImpl() throws RemoteException { } public String sayHello() { return "Hello World"; } }Implementierungen von Server-Objekten sind in RMI zum einen von UnicastRemoteObject abgeleitet, zum anderen implementieren sie die Server-Schnittstelle (hier das Interface HelloServer). Dabei ist zu beachten, dass der Konstruktor einer RMI-Objekt-Implementierung eine RemoteException auslösen kann, die Methoden, die die Server-Schnittstelle implemetieren, jedoch nicht, obwohl in der Schnittstellen-Definition diese Exception definiert ist. Die Implementierung der Methode, die über Netzwerk ausgeführt werden soll, liefert im Beispiel lediglich den Text »Hello World« zurück.
Bei RMI werden die Proxy-Objekte mit RMI-Compiler rmic erzeugt, der beim Java SDK mitgeliefert wird. Dabei wird rmic mit dem vollqualifizierten Klassen-Namen der Objekt-Implementierung aufgerufen:rmic de.dpunkt.rmi.hello.server.HelloImplDer Aufruf funktioniert nur dann richtig, wenn der Quellcode der Objekt-Implementierung bereits zuvor kompiliert wurde und sich die Klassen im aktuellen Klassenpfad der Java-Laufzeitumgebung befindet. Als Ausgabe generiert der RMI-Compiler einen Client-Side Proxy in der Java-Package-Struktur. Die Ausgabe erfolgt relativ zum aktuellen Verzeichnis.
In einem kurzen Hauptprogramm kann man ein Server-Objekt beim Namensdienst registrieren. Voraussetzung für die Registrierung ist, dass der Namensdienst zuvor gestartet wurde. Bei RMI wird zu diesem Zweck die so genannte RMI-Registrierung mitgeliefert. Sie wird über das Tool rmiregistry gestartet:rmiregistryEin Hauptprogramm, das eine Objekt-Implementierung beim Namensdienst anmeldet, kann z. B. folgendermaßen aussehen:public static void main(String args[]) { try { Naming.rebind("hello-server", new HelloImpl()); } catch(Exception ex) { ex.printStackTrace(); } }Wichtig für die fehlerfreie Registrierung ist, dass sich die Klassen, die von der Objekt-Implementierung benutzt werden, auch im Klassenpfad der RMI-Registrierung befinden. Sie übernimmt bei RMI die Funktion des Namensdienstes:rmiregistryBeim Start der RMI-Registrierung sollten sich die Klassen der Server-Anwendung (inkl. generierte Proxy-Klassen) im aktuellen Klassenpfad befinden. Danach wird in einem neuen Prozess die Server-Anwendung gestartet, die die Registrierung durchführt und anschließend auf eingehende Verbindungen wartet:java de.dpunkt.hello.HelloImplZum Schluss folgt der Start der Client-Anwendung ebenfalls in einem neuen Prozess.java de.dpunkt.hello.HelloClientBeim Start ruft die Client-Anwendung zunächst beim Namensdienst einen Verweis auf das Server-Objekt ab. Über den zurückgelieferten Verweis kann der Client dann direkt das serverseitige Objekt kontaktieren und dessen Methoden aufrufen.
An diesem Ablauf kann man sehen, dass für die Ausführung einer kompletten Anwendung in der Regel drei Prozesse erforderlich sind: Client, Server und Namensdienst. Dabei kann der Namensdienst von mehreren Servern benutzt werden, ein Server wird wiederum von mehreren Clients verwendet.
Um die Anwendung auszuführen, muss zunächst die RMI-Registry gestartet werden
Folgender Beispielcode zeigt, wie eine Client-Anwendung über Netzwerk auf ein Server-Objekt zugreifen kann:import java.rmi.Naming; public class HelloClient { public static void main(String args[]) { try { HelloServer server; server = (HelloServer)Naming.lookup("hello-server"); String result = server.sayHello(); System.out.println(result); } catch(Exception ex) { ex.printStackTrace(); } } }Zunächst wird über den Namensdienst ein Verweis auf das Server-Objekt abgerufen. Dabei erfolgt die Identifizierung über den Namen, der zuvor vom Server-Objekt bei der Registrierung benutzt wurde. Da die lookup()-Methode untypisierte Objekte zurückliefert, muss der zurückgelieferte Verweis über einen Cast in die passende Server-Schnittstelle umgewandelt werden. In Wirklichkeit liefert die lookup()-Methode ein Exemplar des Client-Side Proxy zurück, der die Server-Schnittstelle ebenfalls implementiert. Beim Aufruf von sayHello() wird schließlich der Aufruf vom Client-Side Proxy zur serverseitigen Objekt-Implementierung übertragen und dann das Ergebnis zurückgeliefert.
Wenn man ein Objekt abrufen möchte, dass bei einem Namensdienst auf einem anderen Rechner registriert wurde, muss man dem lookup()-Aufruf den Rechnernamen mit übergeben:Naming.lookup("//myhost/hello-server");Material zum Beispiel
- Quelltexte: