19.1.3 | Anwendungsentwicklung am Beispiel RMI
|
Bei
der Anwendungsentwicklung mit einem Broker werden immer
folgende Schritte durchgeführt:
- 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.
Diese Schritte werden im Folgenden am Beispiel
einer einfachen, auf RMI-basierenden, »Hello World«-Anwendung demonstriert.
Definition der Server-Schnittstelle
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.
Implementierung des Server-Objektes
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.
Generierung der Proxy-Objekte
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.HelloImpl
Der 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.
Anmeldung beim Namensdienst
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:
rmiregistry
Ein 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:
rmiregistry
Beim 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.HelloImpl
Zum Schluss folgt der Start der Client-Anwendung ebenfalls in einem neuen Prozess.
java de.dpunkt.hello.HelloClient
Beim 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.
Starten der Anwendung
Um die Anwendung auszuführen, muss zunächst die RMI-Registry gestartet werden
Clientseitiger Aufruf von Methoden
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
Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.