(erklärt anhand des "Hello World"-Beispiels aus der Java-Dokumentation)
Um die Beispiele auf einem einzelnen PC testen zu können, muß das System richtig konfiguriert sein, d.h. der Computer muß eine eigene IP-Adresse besitzen. Dazu muß Microsofts DFÜ-Adapter installiert werden (Systemsteuerung => Netzwerk => Hinzufügen => Netzwerkkarte), sowie das TCP/IP-Protokoll (... => Hinzufügen => Protokoll). Dann muß unter Eigenschaften von TCP/IP auf der Karteikarte "IP-Adresse" "IP-Adresse festlegen" gewählt werden. Unter IP-Adresse trägt man dann eine Adresse (z.B. 192.168.1.1) ein, und eine Subnet-Mask (hier: 255.255.255.0). Danach können die Beispiele ausgetestet werden.
Bevor man mit der Programmierung anfängt, muß man sich für einen Paketnamen für das gesamte Projekt entscheiden, da der Java Compiler ausgehend von einem Basisverzeichnis, diesen Namen auf die Verzeichnisstruktur abbildet, um zu wissen, wo er die benötigten Klassen des Projekts finden kann. Das Basisverzeichnis muß noch in den Pfad der CLASSPATH-Umgebungsvariable aufgenommen werden, damit der Compiler die Klassendateien finden kann.
In diesem Fall ist der Paketname examples.hello und das Basisverzeichnis c:\rmi\examples.
Jedes Remote Interface erweitert, direkt oder indirekt, das Interface java.rmi.Remote
, welches selbst keine Methoden definiert. Das Interface und seine
Methoden müssen public sein. Jede Methode eines Remote Interface muß (neben anwendungsspezifischen Exceptions) die Basisklasse
java.rmi.RemoteException
im Exception-Teil des Methodenkopfes enthalten, um die Robustheit gegenüber jeglichen
Kommunikationsproblemen sicherzustellen.
Der Quellcode für Hello.java:
package examples.hello; public interface Hello extends java.rmi.Remote { String sayHello() throws java.rmi.RemoteException; }
Um ein entferntes Objekt zu schreiben, muß man nun eine Klasse schreiben, die ein oder mehrere Remote Interfaces implementiert, dazu muß man:
Code für das Beispiel:
package examples.hello; import java.rmi.*; import java.rmi.server.UnicastRemoteObject;
Das erben von der Klasse UnicastRemoteObject
gibt an, daß HelloImpl eine Klasse ist, deren Instanzen die
standard-socketbasierte RMI Kommunikation benutzen. Um ein entferntes Objekt aus einer Klasse, die nicht dafür gedacht wurde, zu erzeugen,
muß durch Aufruf der Methode UnicastRemoteObject.exportObject dieses explizit als entferntes Objekt kennzeichnen.
public class HelloImpl implements Hello // 1.) extends UnicastRemoteObject // 2.) { private String name;
Der Konstruktor einer Klasse für entfernte Objekte unterscheidet sich nicht von dem einer lokalen Klasse.
public HelloImpl(String s) throws RemoteException { super(); // 3.) name = s; }
Argumente und Rückgabewerte von entfernten Methoden können alle Datentypen oder Objekte sein, solange sie das Interface
java.io.Serializable
implementieren. Es können hier auch Methoden definiert werden die nicht im Remote Interface deklariert
wurden, diese können jedoch nur lokal aufgerufen werden.
public String sayHello() throws RemoteException { // 4.) return "Hello World!"; }
Die Installation eines SecurityManagers ist notwendig, damit entfernte Klassen geladen werden können.
public static void main(String args[]) { // Erzeugen und installieren des SecurityManagers // 5.) System.setSecurityManager(new RMISecurityManager());
Das RMI System benutzt zum laden von Klassen eine URL-basierte Registry, d.h. eine URL der Form //host/Objektname wird an das entfernte Objekt gebunden. Aufrufe können jetzt das Objekt an ihrem Namen identifizieren und bekommen eine Referenz auf dieses entfernte Objekt, und können nun dessen Methoden aufrufen.
try { HelloImpl obj = new HelloImpl("HelloServer"); //6.) Naming.rebind("//192.168.1.1/HelloServer", obj); System.out.println("HelloServer bound in registry"); } catch (Exception e) { System.out.println("HelloImpl err: " + e.getMessage()); e.printStackTrace(); } } }
Das Client Programm kann jede Art von Java-Programm (Applet oder Applikation) sein, solange es die entfernten Objekte aufruft.
In diesem Beispiel ist es ein Applet:
package examples.hello; import java.awt.*; import java.rmi.*; public class HelloApplet extends java.applet.Applet { String message = ""; public void init() { try {
Das Applet holt sich zuerst eine Referenz auf das entfernte Objekt, indem es in der Registry nachschaut. Dann kann es auf die entfernte Methode zugreifen.
Hello obj = (Hello)Naming.lookup("//" + getCodeBase().getHost() + "/HelloServer"); message = obj.sayHello(); } catch (Exception e) { System.out.println("HelloApplet exception: " + e.getMessage()); e.printStackTrace(); } } public void paint(Graphics g) { g.drawString(message, 25, 50); } }Für das Applet muß noch eine HTML-Datei (z.B. index.html) angefertigt werden, die auf das Applet zugreift, dieses geschieht durch das <APPLET>-Tag.
Die Quellcode-Dateien sollten sich nun im Basisverzeichnis des Projekts befinden. Jetzt geht es an das kompilieren, dies geschieht durch den Aufruf:
javac -d c:\rmi\examples *.java
Dadurch werden die Unterverzeichnisse examples\hello im Basisverzeichnis c:\rmi\examples erzeugt, und die Klassendateien dorthin kompiliert.
Der rmic
-Compiler wird aufgerufen mit der Implementationsklasse (Server) des entfernten Objekts. Dadurch werden die Stub- und die
Skeletondateien erzeugt. Der erzeugte Stub implementiert exakt den gleichen Satz von Interfaces wie das Remote Object und sind typidentisch.
rmic –d c:\rmi\examples examples.hello.HelloImpl
Der d-Parameter gibt hier wieder das Basisverzeichnis an, von dem ausgehend die Stub- und die Skeletondatei in das Verzeichnis examples\hello erzeugt werden.
Hier befindet sich die Implementierung der Stub-Klasse für das Hello World-Beispiel: HelloImpl_Stub.java (durch die keepgenerated-Option erhalten)
Ein Client kommuniziert mit dem entfernten Objekt in erster Linie mit Hilfe der RMI Registry, ein Hintergrundprozeß, der vor dem Verbindungsaufbau vom Server gestartet wird. Die Klasse java.rmi.Naming stellt entsprechende Methoden (lookup, bind,...) zur Verfügung welche auf der URL-Syntax basieren. Bei einem RMI-Aufruf durch den Client wird auf Serverseite ein neues Objekt erzeugt, an eine URL gebunden, in die Registry eingetragen, und diese Referenz dem Client-Stub zurückgesendet. Von diesem Zeitpunkt an kann der Client das Objekt wie ein lokales verwenden. Jeder Aufruf auf diesem Proxy-Objekt wird automatisch zum entfernten Objekt auf dem entfernten Server weitergeleitet.
Start rmiregistry
Jedesmal, wenn Modifikationen am Remote Interface gemacht werden oder zusätzliche Interfaces in einer Implementation eines entfernten Objekts eingebunden werden, muß die RMI Registry neu gestartet werden, ansonsten stimmt die modifizierte Klasse nicht mit der in der Registry angemeldeten überein.
java [*] examples.hello.HelloImpl
gestartet.*hier kann die Eigenschaft java.rmi.server.codebase
Als letztes kann der Client gestartet werden, und wenn alles gutgegangen ist, müßte das Programm einwandfrei laufen, und der Benutzer merkt nicht woher die Methoden aufgerufen werden.
Appletviewer examples\hello\index.html