Advanced Java Services | RMI mit PortableRemoteObject |
An Stelle der Klasse java.rmi.server.UnicastRemoteObject kann für RMI auch die neuere Klasse javax.rmi.PortableRemoteObject verwendet werden. Das RemoteInterface bleibt das gleiche wie vorhin.
Zu einem RemoteObjekt soll clientseitig eine Methode getDate() aufgerufen werden, die das aktuelle Datum auf dem Server liefert. Für unser Beispiel brauchen wir eine IP-Adresse für den Server, wir nehmen die folgende.
Server IP-Adresse: 192.168.11.22
Das RemoteInterface muß die gleichen Bedingungen erfüllen wie im vorigen Fall.
Wir verwenden dasselbe Interface.
import java.util.Date; import java.rmi.Remote; import java.rmi.RemoteException; public interface DateRemoteInterface extends Remote { Date getDate() throws RemoteException ; }
Die Implementierung erbt jedoch jetzt von der Klasse PortableRemoteObject.
Für die Implementierung des RemoteInterfaces gelten etwas andere Bedingungen:
Hier unsere Implementierung.
import java.util.*;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject ;
public class DateRemoteInterfaceImpl extends PortableRemoteObject
implements DateRemoteInterface
{
public DateRemoteInterfaceImpl() throws RemoteException
{
super();
}
// vom DateRemoteInterface geforderte Methode
public Date getDate() throws RemoteException
{
return new Date();
}
}
Hier verläuft jetzt die Verknüpfung zwischen Objekt und Namen, die der Server vornehmen muß, auf eine etwas andere Weise. Zuständig dafür ist nun die Klasse javax.naming.InitialContext. Der Konstruktor von InitialContext erhält ein PropertiesObjekt, in dem man die Umgebung angibt, mit der InitialContext arbeiten soll. Mit dem PropertiesObjekt beschreibt man die Klasse, die für die Namensverknüpfung zuständig ist (RegistryContextFactory) und die URL-Adresse des Servers.
Codiert sieht das wie folgt aus.
import java.util.Properties; import javax.naming.Context; import java.rmi.RemoteException; import javax.naming.InitialContext; import javax.naming.NamingException; import java.rmi.registry.LocateRegistry; public class RMIDateServer { // setting up the server public static void main(String args[]) { try { DateRemoteInterfaceImpl dateObj = new DateRemoteInterfaceImpl(); // wirft RemoteException // rmiregistry starten int port = 1099 ; LocateRegistry.createRegistry(port); // Creates and exports a Registry on the local host that accepts // requests on the specified port (wirft java.rmi.RemoteException) Properties env = new Properties(); env.put("java.naming.factory.initial", "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put("java.naming.provider.url", "rmi://localhost:"+port); // für den Defaultport 1099 ist die letzte Zeile nicht notwendig // JNDI naming Context initialNamingContext = new InitialContext(env); // wirft NamingException initialNamingContext.bind("DateService", dateObj); // wirft NamingException } catch(Exception ex) { System.out.println("Exception happened:"); ex.printStackTrace(); } } }
Mit dem Statement LocateRegistry.createRegistry(port); wird ein Server gestartet, der auf dem
angegebenen Port auf Clientanfragen wartet. Diese Anfragen fragen nach einem bestimmten Namen (bei uns
"DateService"). Der Server gibt deshalb die Anfragen weiter an einen Namensdienst (Naming Service)
oder Anfragevermittler (Request Broker). Der Namensdienst, der mit Registry zusammenarbeiten kann
heißt RegistryContextFactory. Wir geben ihn mit dem Statement
env.put("java.naming.factory.initial", "com.sun.jndi.rmi.registry.RegistryContextFactory"); an.
Nach Angabe eines Namensdienstes kann man dann einen Namen an ein Objekt binden:
initialNamingContext.bind("DateService", dateObj);
Auch die Clientklasse muß jetzt mit einem Objekt der Klasse InitialContext arbeiten. Der Konstruktor von InitialContext erhält ein PropertiesObjekt, in dem man die Umgebung angibt, mit der InitialContext arbeiten soll. Mit dem PropertiesObjekt beschreibt man die Klasse, die für die Namensverknüpfung zuständig ist (RegistryContextFactory) und die URL-Adresse des Servers.
Und im Code.
import java.util.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
public class RMIDateClient
{
public static void main(String[] args)
{
try
{
int port = 1099 ;
String url = "rmi://192.168.11.22:"+port ;
String jndiName = "DateServer";
Properties env = new Properties();
env.put("java.naming.factory.initial", "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put("java.naming.provider.url", url);
Context initial = new InitialContext(env); // NamingException
Object ref = initial.lookup(jndiName); // NamingException
DateRemoteInterface remRef =
(DateRemoteInterface)PortableRemoteObject.narrow(ref, DateRemoteInterface.class);
// Client erhält Objekt einer Klasse, die das DateRemoteInterface implementiert hat
System.out.println("Datum auf dem Server: " + remRef.getDate() );
}
catch(Exception ex)
{
System.out.println("Exception happened:");
ex.printStackTrace();
}
}
}
Wie im vorigen Beispiel müssen wir noch die Stubklasse erzeugen. Der Aufruf von rmic ist derselbe.
rmic DateRemoteInterfaceImpl
rmic erzeugt bei diesem Aufruf natürlich den Namen der Stubklasse nach dem gleichen Schema.
Name der implementierenden Klasse | Name der Stubklasse |
---|---|
XxxImpl | XxxImpl_Stub |
Wenn wir nun DateRemoteInterfaceImpl_Stub sowohl beim Server als auch beim Client deponieren, dann sollte das Beispiel laufen.