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



## Thomas Darimont (9. November 2004)

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

```
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:

```
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:

```
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:

```
-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!"


----------



## torsch2711 (9. November 2004)

*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.


----------



## Daniel Toplak (9. November 2004)

*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


----------



## Thomas Darimont (9. November 2004)

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


----------



## Daniel Toplak (9. November 2004)

Also im grundegenommen vergleichbar mit DCOM, danke, das wollte ich wissen.

Gruß Homer


----------



## MartinLeim (12. März 2006)

Hallo Thomas!

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

Zunächst mal mein Server-Interface:


```
package server;
import java.rmi.Remote;
import java.rmi.RemoteException;

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

Nun meine Server-Implementation:


```
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)
> ...



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 (12. März 2006)

Hallo1

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

Für ein anderes tutorial schau mal hier:
http://www.tutorials.de/forum/java/231847-rmi-unter-java-5-a.html?highlight=Remote

Gruß Tom


----------



## MartinLeim (12. März 2006)

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:

```
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:

```
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)
> ...



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


----------



## Thomas Darimont (13. März 2006)

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:

```
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


----------



## MartinLeim (13. März 2006)

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!


----------



## 00Stan (10. Dezember 2007)

Is ja krass! Hab schon viele Tutorials gelesen und und ausprobiert. Erst nach dem ich das shier gelesen hab, hats funktioniert
Danke Thomas!


----------



## Xerosaar (10. Dezember 2007)

Moin,

Zu RMI habe ich gerade mal eine grundsätzliche Frage, die ich mir auch mehrere Stunden Recherche im Netz nicht beantworten konnte.

Kann ich mit etwas anderem als einem Java Applet bzw Javaprogramm als Client auf die Methoden eines Servers zugreifen, der RMI Methoden bereitstellt?

Beispielsweise über Javascript? Ich bin dabei über den Begriff DWR gestolpert, habe aber irgendwie das Gefühl, dies geht daran vorbei.
Ich würde gerne einen Browserclient für meinen RMI Server schreiben, ohne auf ein Java Applet zurückgreifen zu müssen.


----------



## Tasm-Devil (25. Mai 2010)

Hi Thomas,

deine Demo funktioniert bei mir leider nicht. Ich bin schon den halben Tag damit beschäftigt dieses RMI zum laufen zu bringen aber ich schaffe es leider nicht. Ich zweifle langsam an meinen Fähigkeiten. Leider habe ich schon lange kein Java mehr programmiert und darum könnte es auch nur ein vollkommen blöder Fehler sein. Wie auch immer ich kriegs nicht hin.

Deine Demo finde ich bin jetzt ganz nett aber bei mir kommen irgendwie nur Fehler.

Ich arbeite mir Eclipse und dem neuen Java 6.

Ich habe mir für deine Dateien 2 Projekte angelegt: de.tutorium.rmi und de.tutorium.rmi.client

zusätzlich habe ich eine bat.Datei angelegt mit der ich die rmiregistry starte:


```
@ECHO OFF

rmiregistry -Djava.rmi.server.ignoreStubClasses=true
                -Djava.rmi.server.codebase=file:/D:/Uni-Kl/Informatik/SS_2010/SEP/Eclipse_workspace/RMI_von_tutorials/
                de.tutorials.rmi.server.Server
Pause
```

wenn ich die starte macht er was aber ich seh nur schwarz in der Eingabeaufforderung. Aber ich denke, dass ist wohl nicht weiter schlimm.
An deinen Quelcodes habe ich nichts geändert.
Dann möchte ich den Server starten und es kommt folgende Fehlermeldung:


```
RegistryImpl_Stub[UnicastRef [liveRef: [endpoint:[127.0.0.1:1099](remote),objID:[0:0:0, 0]]]]
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
	java.lang.ClassNotFoundException: de.tutorials.rmi.ITimeService
	at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:396)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:250)
	at sun.rmi.transport.Transport$1.run(Transport.java:159)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:619)
	at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
	at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
	at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:359)
	at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
	at de.tutorials.rmi.server.Server.start(Server.java:28)
	at de.tutorials.rmi.server.Server.main(Server.java:39)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
	java.lang.ClassNotFoundException: de.tutorials.rmi.ITimeService
	at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
	at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:386)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:250)
	at sun.rmi.transport.Transport$1.run(Transport.java:159)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException: de.tutorials.rmi.ITimeService
	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:247)
	at sun.rmi.server.LoaderHandler.loadProxyInterfaces(LoaderHandler.java:711)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:655)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:592)
	at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:628)
	at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294)
	at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:238)
	at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1531)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1493)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	... 12 more
```

was soll ich damit anfangen? Hilfeeee!
Ich hoffe du kannt mir da weiterhelfen. Ist dringend weil meine Gruppe und ich im Softwareentwicklungspraktikum am Montag abgabe haben.

Danke schon mal im Voraus.

P.S. ich arbeite auf Win XP mit Eclipse 1.2.2 (neuste Version: eclipse-java-galileo-SR2-win32.zip)
und der Java jdk1.6.0_20


----------



## Tasm-Devil (26. Mai 2010)

ok. Got it.

Vergesst diese Batch-Datei von mir oben. Ich hab nicht richtig gelesen was zu tun war. (Ich Depp)
Und jetzt gehts auch einwandfrei mit folgender Batch Datei:


```
@echo off
start C:\Programme\Java\jdk1.6.0_20\bin\rmiregistry
Pause
C:\Programme\Java\jdk1.6.0_20\bin\java -Djava.rmi.server.ignoreStubClasses=true -Djava.rmi.server.codebase=file:/D:/Uni-Kl/Informatik/SS_2010/SEP/Eclipse_workspace/RMI_von_tutorials/  de.tutorials.rmi.server.Server
Pause
```

Ich wollte an rmiregistry Parameter übergeben, welche für java gedacht waren. Da sieht man, dass ich noch nie von der Kommandozeile aus ein Java-Prog. kompiliert habe. *schäm*

Aber Danke dir für dieses Tutorial. Echt klasse. 

Jetzt werd ich noch zusehen, wie ich es mache, dass ich mir die Batch Datei schenken kann indem ich die rmiregistry direkt aus dem Server heraus starten und wie ich Eclipse beibringe Parameter an java zu übergeben. Wird denk ich mal keine große Sache sein.

Trotzdem Danke.


----------

