Java 1.5.0 RMI OHNE Stubs und Skeletons dank Dynamic Proxies ;-)

Thomas Darimont

Erfahrenes Mitglied
Hallo!

Hier mal ein Beispiel wie man mit Java 1.5.0 eine RMI Anwendung schreiben kann ohne den rmic zu verwenden um Stubs zu generieren.... ;-) *dazu gibts bis jetzt noch nicht so viele Beispiele )

(Das Ganze funktioniert jedoch nur, wenn die Client als auch die Server Version unter JDK 5.0 kompiliert und betrieben werden....)

Hierzu bauen wir die schon so oft gesehene ZeitDienst Anwendung nach ...

Unser Interface TimeService
Code:
package de.tutorials.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ITimeService extends Remote{
	public long getServerTime() throws RemoteException;
}

Usere Server Klasse:
Code:
package de.tutorials.rmi.server;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import de.tutorials.rmi.ITimeService;

public class Server implements ITimeService {

	public final static int STD_RMI_PORT = 1099;

	public Server() {
	}

	private void start() {
		try {
			ITimeService stub = (ITimeService) UnicastRemoteObject
					.exportObject(this, 0);

			Registry registry = LocateRegistry.getRegistry("127.0.0.1",
					STD_RMI_PORT);

			System.out.println(registry);

			registry.bind("TIMESERVICE", stub);

		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		new Server().start();
	}

	public long getServerTime() throws RemoteException {
		return System.currentTimeMillis();
	}
}

Unser Client:
Code:
package de.tutorials.rmi.client;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Date;

import de.tutorials.rmi.ITimeService;

public class Client {

	public final static int STD_RMI_PORT = 1099;

	public static void main(String[] args) {
		new Client().start();
	}

	private void start() {
		try {
			Registry registry = LocateRegistry.getRegistry("127.0.0.1",
					STD_RMI_PORT);
			ITimeService timeService = (ITimeService) registry
					.lookup("TIMESERVICE");
			System.out.println(new Date(timeService.getServerTime()));
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}
}

Um das ganze zum Laufen zu bringen müssen wir mit Java 1.5.0 kompilieren (Eclipse Compiler Einstellungen in den preferences setzen Java -> Compiler-> Compliance and Classfiles auf 1.5.0)

Nachdem wir das ganze Kompiliert haben starten wir zuerst mal die rmiregistry (befindet sich im Verzeichnis %JJAVA_HOME%\bin.
Standardmäßig horscht diese RMIRegistry auf Port 1099.

Danach starten wir den Server mit folgenden Optionen:
Code:
-Djava.rmi.server.ignoreStubClasses=true -Djava.rmi.server.codebase=file:/E:/eclipse/workspace/tutorials/ 

Also:
java -Djava.rmi.server.ignoreStubClasses=true -Djava.rmi.server.codebase=file:/E:/eclipse/workspace/tutorials/  de.tutorials.rmi.server.Server
bei ...codebase=file:/ .... die URL so setzen, dass das ITimeService Interface gefunden werden kann...
Das Property java.rmi.server.ignoreStubClasses benötigen wir um die Generierung von StubsProxys zu erzwingen.

Nachdem wir nun den Server gestartet haben sollten wir folgende Ausgabe sehen:
Proxy[Registry,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:[127.0.0.1:1099](remote),objID:[0:0:0, 0]]]]]

Wenn wir nun den Client starten erhalten etwas in der Form:
Tue Nov 09 19:44:29 CET 2004

Viel Spaß beim herumprobieren ;-)

...inspiriert durch Artikel: http://today.java.net/pub/a/today/2004/06/01/RMI.html?page=2
Edit: hier noch ein klareres Beispiel dazu:
http://www.tutorials.de/forum/java/231847-rmi-unter-java-5-a.html?highlight=Remote

Gruß Tom

Ps.: Zitat eines bekannten Java Gurus:" ...HHHOOOORRRAAAYYYY ... no more RMIC.... ... Long live the (DYNAMIC) PROXIES!"
 
Re: Java 1.5.0 RMI OHNE Stubs und Sekeletons dank Dynamic Proxies ;-)

Danke für die Info Thomas, ich sehe schon ich muss mich in nächster Zeit mal mit der neuen Java Version auseinander setzen. Hat sich ja einiges getan. War immer etwas aufwendig RMI Anwendung zum laufen zu bekommen. ;)
 
Re: Java 1.5.0 RMI OHNE Stubs und Sekeletons dank Dynamic Proxies ;-)

Also erklärt mich jetzt nicht für blöd, aber da ich aus der C++ Ecke komme, hab ich mal ne Frage:
Was ist RMI?
Ist das Prinzip mit Remote-COM bzw... DCOM (Distributed COM) zu vergleichen?
Da ich von JAVA eigentlich gar keine Ahnung habe, aber immer noch am überlegen bin, doch mal damit anzufangen, interessiert mich das.

Groß Homer
 
Hallo!

Bei RMI handelt es sich um eine Middlewarekomponente (Vergleichbar mit DCOM , Corba etc.). RMI steht für Remote Method Invocation und bietet RPC Möglichkeiten. RPC steht für Remote Procedure Call -> "Entfernter Prozedur Aufruf". Damit kannst du Methoden an Objekten aufrufen die sich in einer ander VM befinden. Die dazu benötigten Daten werden durch diverse Protokolle (JRMP (Java Remote Method Protocol) oder RMI over IIOP (Internet Inter Orb Protocol) übers Netz gejagd. Auf der Empfängerseite werden dann die Anfragen durch spezielle Komponenten entgegengenommen (Skeletons beim Server [zumindest wars früher so ^^ ) und Stubs beim Client). Die für die Methoden notwendigen Objekte (Parameter, Rückgabewerte etc) werden bei bedarf Serialisiert und zum Anfragenden zurückgeschickt.

Das wars mal kurz und knapp...

Weitere Infos gibts unter:
http://www.galileocomputing.de/open...el_18_000.htm#Rxx365java18000040007341F01510F

Gruß Tom
 
Hallo Thomas!

Ich versuche seit über einer Stunde, dein Beispiel nachzuvollziehen, aber es will einfach nicht klappen!

Zunächst mal mein Server-Interface:

Code:
package server;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Server extends Remote {
	
	public String getData() throws RemoteException;
}

Nun meine Server-Implementation:

Code:
package server;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

class ServerImpl implements Server {

	protected ServerImpl() throws RemoteException {
		super();
	}

	public String getData() throws RemoteException {
		return "Teststring";
	}
	
	private void start() throws Exception {		
		
		Server stub = (Server) UnicastRemoteObject.exportObject(this, 0);		
		Registry reg = LocateRegistry.getRegistry(1099);		
		
		System.out.println(reg);
		
		reg.bind("rmiTest", stub);
	}
	
	public static void main(String[] args) throws Exception {
		new ServerImpl().start();		
	}

}

Das ganze starte ich aus Eclipse mit folgenden Argumenten für die JVM:
-Djava.rmi.server.ignoreStubClasses=true -Djava.rmi.server.codebase=file://F:/Entwicklung/Java/workspace/RMITest_Server/

Das angegebene Verzeichnis ist dabei das Verzeichnis des Eclipse-Projektes, die Klassen liegen im Verzeichnis bin. Es macht aber keinen Unterschied in der Fehlermeldung, wenn ich das Verzeichnis "bin" mit angebe.

Ich erhalte eine ClassNotFoundException, wenn ich die URL so angebe wie im Tutorial (evtl. ein Fehler darin?), ich musste vor dem Laufwerksbuchstaben ein zusätzliches Slash einfügen.

Nun bekomme ich keine ClassNotFoundException, sondern ich bekomme folgenden Fehler (das ganze taucht bei dem Binden an die Registry auf):

Proxy[Registry,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:[192.168.0.2:1099](remote),objID:[0:0:0, 0]]]]]
Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission \\F\Entwicklung\Java\workspace\RMITest_Server\bin read)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.File.exists(Unknown Source)
at sun.net.http://www.protocol.file.Handler.openConnection(Unknown Source)
at sun.net.http://www.protocol.file.Handler.openConnection(Unknown Source)
at java.net.URL.openConnection(Unknown Source)
at sun.rmi.server.LoaderHandler.addPermissionsForURLs(Unknown Source)
at sun.rmi.server.LoaderHandler.access$300(Unknown Source)
at sun.rmi.server.LoaderHandler$Loader.<init>(Unknown Source)
at sun.rmi.server.LoaderHandler$Loader.<init>(Unknown Source)
at sun.rmi.server.LoaderHandler$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.server.LoaderHandler.lookupLoader(Unknown Source)
at sun.rmi.server.LoaderHandler.loadProxyClass(Unknown Source)
at java.rmi.server.RMIClassLoader$2.loadProxyClass(Unknown Source)
at java.rmi.server.RMIClassLoader.loadProxyClass(Unknown Source)
at sun.rmi.server.MarshalInputStream.resolveProxyClass(Unknown Source)
at java.io_ObjectInputStream.readProxyDesc(Unknown Source)
at java.io_ObjectInputStream.readClassDesc(Unknown Source)
at java.io_ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io_ObjectInputStream.readObject0(Unknown Source)
at java.io_ObjectInputStream.readObject(Unknown Source)
at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy1.bind(Unknown Source)
at server.ServerImpl.start(ServerImpl.java:33)
at server.ServerImpl.main(ServerImpl.java:37)

An den Dateisystem-Rechten kann es nicht liegen, der Benutzer, unter dem ich entwickele, hat Vollzugriff auf den entsprechenden Ordner. Mich wundert aber, dass vor dem Laufwerksbuchstaben zwei Backslashs stehen. Hat der Fehler vielleicht irgendwie damit zu tun?

Oder sieht jemand, was ich falsch gemacht haben könnte?

Vielen Dank im Voraus.
Martin
 
Thomas Darimont hat gesagt.:
Ich glaube da hatte ich mich bei den Pfaden irgendwo vertippt...
-Djava.rmi.server.codebase=file://F:/Entwicklung/Java/workspace/RMITest_Server/
Versuchs mal mit:
file:/f:/Entwicklung/Java/workspace/RMITest_Server/bin

Genau das hast du doch aber in deinem Tutorial, und so findet er die Klasse nicht.

Nun ja, wenn ich die Registry im Programm erzeuge, brauche ich die Codebase nicht. Nun gelingt es mir immerhin, den Server zu starten, doch wenn ich den Client starte, bekomm ich den Fehler, dass die Skeleton-Klasse nicht gefunden wird.

Der neue Code für den Server:
Code:
Server stub = (Server) UnicastRemoteObject.exportObject(this, 0);		
		Registry reg = LocateRegistry.createRegistry(1099);		
		
		System.out.println(reg);
		
		Naming.bind("REMOTE_TEST", stub);

Ich verwende Naming.rebind() anstelle von reg.rebind(), weil das Programm sonst nach dem Aufruf terminieren würde und nicht in Wartestellung bleibt.

Der Client:
Code:
public class Client {
	
	public static void main(String[] args) throws Exception {
		new Client().start();
	}
	
	private void start() throws Exception {
		Registry reg = LocateRegistry.getRegistry("127.0.0.1");
		Server server = (Server) reg.lookup("REMOTE_TEST");
		System.out.println("Nachricht: " + server.getData());
		
	}

}

Die Fehlermeldung:

Exception in thread "main" java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling call header; nested exception is:
java.rmi.UnmarshalException: skeleton class not found but required for client version
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at client.Client.start(Client.java:17)
at client.Client.main(Client.java:12)
Caused by: java.rmi.UnmarshalException: error unmarshalling call header; nested exception is:
java.rmi.UnmarshalException: skeleton class not found but required for client version
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.rmi.UnmarshalException: skeleton class not found but required for client version
... 7 more

Sie verwundert mich, da ich dachte, dass der Stub und insbesondere der Skeleton automatisch erstellt werden sollten!
 
Hallo!

Sie verwundert mich, da ich dachte, dass der Stub und insbesondere der Skeleton automatisch erstellt werden sollten!
Skeletons werden ueberhaupt nicht mehr erstellt... weil diese nicht mehr benoetigt werden. -> http://java.sun.com/j2se/1.5.0/docs/guide/rmi/spec/rmi-arch2.html

Also bei mir funktioniert das Beispiel.
Ich starte die rmiregistry wie folgt:
Code:
rmiregistry -J-Djava.rmi.server.codebase=file:/D:/development/workspace/de.tutorials.rmi/bin/

Hab die Beispielprojekte mal angehaengt. Hast du fuer jeden Java prozess auch den Java 5 Launcher benutzt? Hast du mehrere Netzwerkkarten in deinem Rechner?

Aber wie schon gesagt, ich glaube das andere tutorial zu RMI unter Java 5: http://www.tutorials.de/forum/java/231847-rmi-unter-java-5-a.html?highlight=Remote
laesst sich einfacher nachvollziehen.

Gruss Tom
 

Anhänge

  • de.tutorials.rmi.examples.zip
    6,8 KB · Aufrufe: 203
So, es funktioniert jetzt, wenn Client und Server sich in unterschiedlichen Projekten befinden und das Classfile des Server-Interface sich im Classpath des Clients befindet.
Sind beide im gleichen Projekte, wird also ein- und dieselbe Classfile benutzt, kommt oben angesprochener Fehler mit dem fehlenden Skeleton.

Mich würde noch interessieren, warum das so ist, im Prinzip ist das Problem aber gelöst.

Danke, Thomas!
 

Neue Beiträge

Zurück