RMI unter Java 5

Thomas Darimont

Erfahrenes Mitglied
Hallo!

Hier mal ein kleines Beispiel zu RMI unter Java 5.
Welche schicken Neuerungen es für RMI unter Java 5 gibt könnt ihr hier:
http://today.java.net/pub/a/today/2005/10/06/the-new-rmi.html
oder hier:
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/relnotes.html
nachlesen.

In diesem Beispiel verwenden wir zur Laufzeit erzeugte Dynamic Proxies als Client-Stubs.
(D.h. wir werden keinen rmic Stub Compiler verwenden)

In meinem Rechner habe ich mehrere Netzwerkkarten. Da ich die rmiregistry jedoch
explizit an das Network Interface mit der IP 192.168.76.98 binde muss ich beim lookup
der Registry die entsprechende IP Addresse mitgeben.

Unser Service Interface:
Code:
    package de.tutorials.rmi.service;
    
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    public interface IRemoteService extends Remote {
    	String remoteOperation(String[] arguments) throws RemoteException;
    }

Unsere Service Implementierung:
Code:
    /**
     * 
     */
    package de.tutorials.rmi.service.impl;
    
    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    import java.util.Arrays;
    
    import de.tutorials.rmi.service.IRemoteService;
    
    /**
     * @author Tom
     * 
     */
    public class RemoteServiceImpl extends UnicastRemoteObject implements
    		IRemoteService {
    	public RemoteServiceImpl() throws RemoteException {
    		super();
    	}
    
    	public String remoteOperation(String[] arguments) throws RemoteException {
    		String result = Arrays.toString(arguments);
    		System.out.printf("remoteOperation invoked @%s with arguments %s\n",
 				System.currentTimeMillis(), result);
    		return result;
    	}
    }

Unser Server:
Code:
    /**
     * 
     */
    package de.tutorials.rmi;
    
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;
    
    import de.tutorials.rmi.service.IRemoteService;
    import de.tutorials.rmi.service.impl.RemoteServiceImpl;
    
    /**
     * @author Tom
     * 
     */
    public class Bootstrap {
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) throws Exception {
    		Registry registry = LocateRegistry.getRegistry("192.168.76.98");
    		IRemoteService remoteService = new RemoteServiceImpl();
    		registry.rebind("remoteService", remoteService);
    	}
    }

Unser Client:
Der Client liegt in meinem Beispiel auf einem anderen Rechner. Auf Clientseite liegen nur die
Interfaces unseres Services (IRemoteService) vor, nicht jedoch die Implementierung.
(Man könnte den Client natürlich auch so konfigurieren, dass er sich die Interface Klassen selber
lädt:
Beispiel ... die Klassen liegen in einem Verzeichnis namens bin im htdocs Verzeichnis eines Apache Webservers.
Unser Aufruf der RMI Registry sieht dann so aus:
Code:
 C:\>rmiregistry -J-Djava.rmi.server.codebase=http://localhost/bin/ -J-Djava.rmi.server.hostname=192.168.76.98
).

Code:
    /**
     * 
     */
    package de.tutorials.rmi.client;
    
    import java.lang.reflect.Proxy;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;
    
    import de.tutorials.rmi.service.IRemoteService;
    
    /**
     * @author Tom
     * 
     */
    public class Client {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) throws Exception {
    		Registry registry = LocateRegistry.getRegistry("192.168.76.98", 1099);
    		IRemoteService remoteServiceProxy = (IRemoteService) registry
    				.lookup("remoteService");
    		System.out.println("Currently using a JDK Dynamic Proxy: "
   			 + Proxy.isProxyClass(remoteServiceProxy.getClass()));
    		System.out.println(remoteServiceProxy.remoteOperation(new String[] {
    				"a", "b" }));
    	}
    }

Auf dem Server starte ich die rmiregsirty mit folgendem Kommando:
Code:
 C:\>rmiregistry -J-Djava.class.path=E:\eclipse\3.1.1\eclipse\workspace\de.tutorials.rmi\bin -J-Djava.rmi.server.hostname=192.168.76.98
Ich teile der rmiregistry also mit, wo sie unsere Service Interfaces finden kann und dass ich sie
explizit an das Network Interface mit der IP 192.168.76.98 binde.

Nun starten wir den Server über:
Code:
    E:\eclipse\3.1.1\eclipse\workspace\de.tutorials.rmi\bin>java de.tutorials.rmi.Bootstrap


Anschließend starten wir den Client auf dem anderen Rechner:
Code:
    c:\eclipse\3.1.1\eclipse\workspace\de.tutorials.rmi.client\bin>java de.tutorials.rmi.client.Client

Nun erhalten wir eine ähnliche Ausgabe:
Auf dem Server:
Code:
    remoteOperation invoked @1135529217000 with arguments [a, b]

und auf dem Client
Code:
    Currently using a JDK Dynamic Proxy: true
    [a, b]

ich hoffe dieses Beispiel hat gezeigt, wie man einfache RMI Anwendungen unter Java 5 ohne
umständliche Generierung von ClientsStubs etc aufsetzen kann.

Gruß Tom
 

Anhänge

Hey Tom,
Super Tutorial :)
Zusammen mit meinem Buch hab ich einigermaßen Begriffen wie man entfernte Objekte schreibt und aufruft.
Jedoch habe ich noch ein kleines Problem mit dem rmic und der rmicregistry!
Ich habe in der Classpath den Path zu dem bin meiner JDK.
Nun habe ich es geschafft mit rmic meine Implementation zu compilieren, jedoch weiß ich nicht wie ich mit der rmiregistry umgehen soll. Könntest du bitte nochmal näher erläutern zu was welcher Schritt gut ist und wie man es am sinnvollsten aufruft?
Bei mir läuft es zwar dann, aber er wirft mir immer eine Exception NotBoundException :|
Ich nehme an ich mache etwas bei diesen Schritten in der Registry was falsch...
Vielen Dank schonmal :)
Tsa
 
Hallo,

ich habe ein Problem mit der rmiregistry unter Linux (auf VServer).

starte RMI

$ rmiregistry &

- läuft ohne Probleme
- danach kann ich kein Java mehr starten/ausführen

$ java --version
Error occurred during initialization of VM
Could not reserve enough space for code cache

Wenn ich RMI wieder kille, dann geht Java wieder.

Vielleicht reicht ja ein Xmx für RMI, aber wie Pflege ich den Parameter ein?

Debian System, Speicher nicht voll, sun-java5-jre, root-Zugriff.

--------------------------------------------------------------------------------------------------------
java version "1.5.0_10"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_10-b03)
Java HotSpot(TM) Client VM (build 1.5.0_10-b03, mixed mode)
--------------------------------------------------------------------------------------------------------
Memory
17.9 MB 6.35%
264.6 MB 282.5 MB
Disk Usage
Resource Used Free Total
Disk Space
746.74 MB 36.46%
1.27 GB 2.00 GB
Disk Inodes
57,795 28.90%
142,205 200,000
--------------------------------------------------------------------------------------------------------

Grüße
zui

P.S. auf meinem lokalen Ubuntu geht alles ohne Probleme
 
Hallo,

ist das ein VServer den du da verwendest?

Wieviel memory hast du denn noch frei, wenn du die RMIRegistry gestartet hast?

Versuch mal die RMI Registry bzw. den anderen Java Prozess mit einer kleineren Heap / PermGen Einstellung zu starten, beispielsweise mit -Xmx16m -XX:PermSize=16m.

Dann solltest du die andere JVM starten können.

Gruß Tom
 
Hi,

naja deine Fragen sind doch schon in meinem ersten Posting beantwortet. :)

XmX hat nix gebracht, aber ich glaube es liegt an Java, scheinbar sind da ein paar Bibliotheken auf dem Server die quer schlagen, bzw. ein zweites JRE.

Ich suche da erstmal, denn lokal funktioniert alles wunderbar.

Eine andere Frage:

Ich möchte die Remote Interfaces in eine eigene JAR auslagern, damit Server und Client die einbinden können. Derzeit muss ich das ganze Server.jar noch mit in den Client nehmen.
Dummerweise sind da Daten drin, die im Client nix zu suchen haben.

Eigenes JAR und einbinden in Classpath wird schwierig da die rmiregistry als "-J-Djava.class.path=" die Klassen des Interfaces und der Services brauch. Oder kann ich da 2 Pfade, bzw. später 2 JARs angeben?

Grüße
zui
 
Ist es auch möglich aus seinem ServerProjekt ein .jar File zu machen und dieses dann auf den Server zu legen und dort zu starten?

Irgendwie weiss ich nicht wie ich da ansetzen soll...

Über Hilfe bin ich wirklich dankbar.

Gruß
Sunbird
 
Hallo,
ich versuche im Moment, die Registry und den Dienstanbieter auf verschiedenen Servern unterzubringen.
Verschiedene Server sollen ihre Dienste bei der zentralen Registry eintragen können.
Es tritt aber eine AccessException auf, wenn man versucht, von extern ein Objekt zu binden.
Lösungsansätze, die ich bis jetzt gefunden habe gingen nur in die Richtung, bei dem zentralen Server einen Verweis auf die Registry des zuständigen Servers zu erfragen.
Das wäre mir aber eigentlich zu umständlich, da bei jedem lookup ein eigentlich unnötiger Schritt gemacht werden müsste.

Hat hier vllt. jemand eine Idee wie man diese Sicherheitsvorkehrung ausschalten kann?
 
Zurück