Projektstudium SS98 - distributed computing


vorherige Seite Inhalt nächste Seite

7. Entwicklungsablauf einer RMI-Umgebung

(erklärt anhand des "Hello World"-Beispiels aus der Java-Dokumentation)

  1. Definition des Remote Interface
  2. Implementierung des Remote Interface (Server)
  3. Entwicklung des Client-Programms
  4. Kompilierung der Quelltexte: Remote Interface, Server, Client
  5. Generierung von Stub und Skeleton
  6. Starten der RMI-Registry
  7. Starten des Servers
  8. Starten des Clients

Hinweis für Windows95 Benutzer:

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.

1. Definition des Remote Interface

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;
	}

2. Implementierung des Remote Interface (Server)

Um ein entferntes Objekt zu schreiben, muß man nun eine Klasse schreiben, die ein oder mehrere Remote Interfaces implementiert, dazu muß man:

  1. Alle Remote Interfaces spezifizieren, welche sie implementiert
  2. Die Klasse UnicastRemoteObject direkt oder indirekt erweitern. Sie erbt somit die Methoden der Klassen RemoteObject und RemoteServer.
  3. Den Konstruktor der Klasse UnicastRemoteObject aufrufen (default). (Da auch bei der Instanzierung eines neuen Objekts Kommunikationsfehler auftreten können, muß auch der Konstruktor die Klasse java.rmi.RemoteException erweitern).
  4. Alle im Remote Interface spezifizierten Methoden implementieren. Darüber hinaus kann sie Methoden implementieren, die nicht im Remote Interface angegeben sind, diese sind dann jedoch nur lokal verfügbar.
  5. Entweder den RMI- oder einen benutzerdefinierten SecurityManager installieren.
  6. Zumindest eine Instanz des entfernten Objekts erzeugen und an die RMI Registry binden, um auf Methodenaufrufe zu warten und diese entgegenzunehmen.

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();
			}
		}
	}

3. Entwicklung des Client-Programms

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.

4. Kompilierung der Quelltexte: Remote Interface, Server, Client

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.

5. Generierung von Stub und Skeleton

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.

Aufruf:
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)

6. Start der RMI Registry

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.

Aufruf:
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.

7. Starten des Servers

Der Server wird mit dem Aufruf

java    [*]    examples.hello.HelloImpl gestartet.

*hier kann die Eigenschaft java.rmi.server.codebase ( -Djava.rmi.server.codebase=http://<host>/<Basisverzeichnis>) angegeben werden, daß die Referenzen des entfernten Objekts die URL enthält, von dem die Stub-Klasse dynamisch auf den Clienten geladen werden kann. Im Falle des Einzelplatzrechners ist das nicht nötig.

8. Starten des Clients

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.

Im Falle des Beispiels wird der Client als Applet gestartet, entweder in einem Browser (Netscape oder IExplorer) oder mit dem Appletviewer des JDK:

Appletviewer examples\hello\index.html
Das Ergebnis sollte so ähnlich aussehen:
Applet
vorherige Seite Inhalt nach oben nächste Seite
© Copyright 1998 André Möller, Oliver Mentz & Magnus Wiencke