RMI bietet leider nur begrenzte Verteilungsabstraktion.

Werbung
8.1.5 Java RMI – Remote Method Invocation
(http://java.sun.com/products/jdk/rmi )
(http://java.sun.com/j2se/1.5/docs/guide/rmi )
(http://java.sun.com/docs/books/tutorial/rmi )
(http://java.sun.com/developer/onlineTraining/rmi/RMI.html )
Unterstützung von Fernaufrufen durch Bibliothekspakete
java.rmi
java.rmi.server
java.rmi.registry
und weitere . . .
RMI bietet leider nur begrenzte Verteilungsabstraktion.
vs8.1.5
1
8.1.5.1 Grundzüge der Fernaufruf-Programmierung in Java
1
interface Remote
Für ein fernaufrufbares Objekt und sein Vertreterobjekt muss
eine gemeinsame Schnittstelle definiert werden, die von
java.rmi.Remote erben muss, z.B.
import java.rmi.*;
interface RemoteService extends Remote {
String echo(String s) throws RemoteException;
}
Spätere Vertretererzeugung gelingt nur dann, wenn die Ausnahme
java.rmi.RemoteException vereinbart wird.
vs8.1.5
2
2
class UnicastRemoteObject
Klasse eines fernaufrufbaren Objekts muss von
java.rmi.server.UnicastRemoteObject erben, z.B.
import java.rmi.*;
import java.rmi.server.*;
class
Server extends UnicastRemoteObject
implements RemoteService {
public Server() throws RemoteException { } nötig!
private String memory = "";
public
String echo(String s) // throws entbehrlich!
{ return memory += s; }
}
UnicastRemoteObject redefiniert die Operationen von Object
(z.B. equals() für Fernvergleich) und registriert das fernaufrufbare
Objekt bei der RMI-Verwaltung (≈ Adapter).
3
vs8.1.5
Achtung: Ein Server-Objekt kann
durchaus auch lokal benutzt werden.
3
class Naming:
Namensdienst ist über statische Operationen der Klasse
java.rmi.Naming erreichbar:
public static void rebind(String name, Remote object)
throws RemoteException,
MalformedURLException // java.net
public static Remote lookup(String name)
throws NotBoundException,
RemoteException,
MalformedURLException
(weitere Operationen: bind, unbind, list )
vs8.1.5
4
Der Parameter String name ist im URL-Format anzugeben –
er identifiziert Station und Port des Namensdienstes und
enthält den eigentlichen Objektnamen:
[ // host [ : port ] / ] name
Adresse des Namensdienstes
Standard-Host = lokale Station
Standard-Port = 1099
Ein Namensdienst wird (unter Unix) gestartet mit
rmiregistry [ port ] &
(und sollte – wenn nicht mehr benötigt – mit kill beendet werden)
Achtung: Einträge verändern nur lokal, aber abfragen auch entfernt !
vs8.1.5
5
Aufsetzten und Verwendung
Anbieterseitig:
Server -Objekt erzeugen und beim (lokalen) Namensdienst registrieren:
Naming.rebind("Service", new Server());
Klientenseitig:
Klient erfragt Objekt beim Namensdienst:
RemoteService s =
(RemoteService)Naming.lookup("//obelix/Service");
System.out.println(s.echo("bla");
Casting erforderlich! Es wird geprüft, ob das von lookup gelieferte
Vertreterobjekt tatsächlich die benötigte Schnittstelle implementiert.
vs8.1.5
6
8.1.5.2 Vertretererzeugung und -installation
(< JDK 1.5)
Vertretergenerator heißt RMI Compiler und wird aufgerufen mit
rmic <ServerClass>
also z.B.
rmic Server
( - nicht mit Schnittstelle RemoteService !)
und generiert Stubs – bereits als .class -Dateien
Eingabe
Server.class
Ausgabe
Server_Stub.class (Vertreter)
Server_Skel.class (Treiber)
vs8.1.5
7
Installation der .class-Dateien:
CLASSPATH geeignet setzen!
 Beim Erzeugen des fernaufrufbaren Objekts
muss der zugehörige Treiber-Code greifbar sein.
 Beim Registrieren des fernaufrufbaren Objekts beim Namensdienst
muss auch der zugehörige Vertreter-Code greifbar sein
- ferner: codebase wird dem Namensdienst zur späteren
Weitergabe mitgeteilt (s.u. ).
 Beim Erzeugen des Vertreter-Objekts beim Klienten
– als Folge der Anfrage beim Namensdienst –
muss dort der Vertreter-Code greifbar sein.
(Alternative: Namensdienst liefert codebase des Vertreter-Objekts
– Security Manager muss das Herunterladen erlauben; vgl. 8.1.5.6)
vs8.1.5
8
8.1.5.3 Von der Programmierung bis zur verteilten Ausführung
// RemoteService.java – sowohl für Client als auch Server
interface RemoteService extends Remote {
String echo(String s) throws RemoteException;
}
// Server.java
class Server extends UnicastRemoteObject
implements RemoteService {
public Server() throws RemoteException { }
private String memory = "";
public String echo(String s) { return memory += s+" "; }
public static void main(String[] arg) throws Exception {
Naming.rebind("Service", new Server());
// Programm stoppt hier nicht – wegen verborgener RMI Threads
}
}
vs8.1.5
9
Übersetzen auf Server-Maschine obelix:
javac RemoteService.java liefert RemoteService.class
liefert Server.class
javac Server.java
Erzeugung der Stubs (optional*):
liefert Server_Stub.class
Server_Skel.class
rmic Server
Namensdienst einrichten (falls nicht schon vorhanden):
rmiregistry &
Server starten (er registriert sich selbst beim Namensdienst):
java Server &
vs8.1.5
10
… und hier ein Beispiel-Klient:
// Client.java, benötigt RemoteService.java
class Client {
public static void main(String[] arg) throws Exception {
RemoteService s =
(RemoteService)Naming.lookup("//"+arg[0]+"/Service");
System.out.println(s.echo(arg[1])+"\n");
System.exit(0);
}
}
Damit das Programm nicht
Fernaufrufe
wegen der RMI Threads hängenbleibt
vs8.1.5
11
Übersetzen auf irgendeiner Klienten-Maschine:
javac RemoteService.java
javac Client.java
Vertreter-Code bereitstellen,
Server_Stub.class ,
über Netzdateisystem oder
Dateiübertragung von Server-Maschine obelix
Klient z.B. wiederholt starten:
> java Client obelix hallo
hallo
> java Client obelix hallo
hallo hallo
> java Client obelix hallo
hallo hallo hallo vs8.1.5
12
Alternatives Szenario mit separater Entwickler-Station:
Entwickler-Station
>
>
>
>
Klienten-Station
Anbieter-Station
javac RemoteService.java
javac Server.java
javac Client.java
rmic Server
RemoteService.class RemoteService.class RemoteService.class
Server.class
Server.class
Client.class
Client.class
Server_Stub.class
Server_Stub.class
Server_Stub.class
Server_Skel.class
Server_Skel.class
> rmiregistry &
> java Server &
> java Client elfe bla
bla
vs8.1.5
>
13
Mit JDK 1.5 wird der Generator rmic nicht mehr unbedingt benötigt.
Stattdessen kommt Java Reflection zum Einsatz. Zur Erinnerung:
// Dynamischer Aufruf
Class[] types = { String.class, Integer.class };
Method method = MyClass.getDeclaredMethod("myMethod", types);
Object[] args = { "Hallo", new Integer(42) };
method.invoke(obj, args);
// Dynamischer Proxy
public interface Foo { public void bar(); }
public class MyHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals("bar")) ...
}
};
Class[] interfaces = { Foo.class };
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
interfaces, handler);
f.bar(); // -> handler.invoke(...) vs8.1.5
14
8.1.5.4 Verweise in Fernaufruf-Parametern
Übergeben wird entweder Vertreter oder Kopie des Objekts:
1 Schnittstelle des formalen Parameters erbt von Remote:
aktueller Parameter muss
(statisch) gleiche Schnittstelle implementieren,
(dynamisch) von UnicastRemoteObject erben,
andernfalls MarshalingException
 Netzverweis/Vertreterobjekt wird übergeben
vs8.1.5
15
2 Schnittstelle des formalen Parameters erbt nicht von Remote:
aktueller Parameter muss
(statisch) gleiche Schnittstelle implementieren,
(dynamisch) Schnittstelle java.io.Serializable
implementieren, andernfalls MarshalingException
 Objektkopie wird übergeben
In Objekte eingebettete Verweise werden entsprechend behandelt !
Felder (arrays) und Zeichenketten (strings) sind serializable !
vs8.1.5
16
Achtung!
Wenn ein formaler Parameter einen Schnittstellentyp hat, kennt man
vom aktuellen Parameter nur diese Schnittstelle (unabhängig davon,
ob es sich um einem lokalen oder einen Fernaufruf handelt).
● Wird
ein fernaufrufbares UnicastRemoteObject übergeben, so weiß
man nicht, ob das Objekt tatsächlich entfernt oder aber lokal vorliegt.
● Handelt
es sich um ein serialisierbares Objekt, so wird in Abhängigkeit
von seiner Lage entweder eine Kopie oder das Objekt selbst geliefert!
 Die Semantik des Aufrufs ist nicht eindeutig bestimmt !
(siehe dazu auch Brose/Löhr/Spiegel: Java resists transparent distribution)
vs8.1.5
17
8.1.5.5 Aktivierbare Objekte
werden erst bei Bedarf erzeugt – allerdings nach vorangegangener
Registrierung über den RMI Daemon rmid
import java.rmi.activation.*;
class
Server extends Activatable
implements RemoteService {............
public Server(ActivationID id, MarshalledObject data)
throws RemoteException {
super(id, 0); }
}
Server.class muss bei rmid, die zugehörige Vertreterklasse beim
Namensdienst registriert werden – dafür muss ein setup-Programm
geschrieben werden.
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/activation/overview.html
vs8.1.5
18
8.1.5.6 Dynamisches Nachladen von Code
Stationen müssen den Code von Vertreter-Objekten (Remote) oder
sogar Implementierungen (Serializable) kennen!
Java-Code ist plattformunabhängig, kann über das Netz
nachgeladen werden (via HTTP, FTP, ...)
Für jedes Programm, das Code nachladen muss (rmiregistry, Client):
● Security-Manager
am Anfang des Programms festlegen:
System.setSecurityManager(new RMISecurityManager());
in policy -Datei gewähren
grant { permission java.net.SocketPermission
"*", "accept,connect,listen,resolve"; };
(in ~/.java.policy oder mit -Djava.security.policy=...)
● Socket-Verwendung
vs8.1.5
19
Für jedes Programm, das nachladbaren Code anbietet (Server):
● Klassen
oder JAR auf Web-Server bereitstellen:
http://myhost.de/RemoteService.jar
(enthält RemoteService.class, Server_Stub.class)
● Codebase
des Servers beim Start mit angeben:
java -Djava.rmi.server.codebase=
"http://myhost.de/RemoteService.jar" Server &
Die Codebase-URL wird bei der rmiregistry hinterlegt.
Klienten erhalten bei lookup nicht nur einen Netzverweis,
sondern auch die zugehörige Codebase-URL, und laden den Code
von dort nach.
20
vs8.1.5
[Siehe http://java.sun.com/j2se/1.5.0/docs/guide/rmi/codebase.html]
Herunterladen