Teil 1 - auf Matthias

Werbung
Netzprogrammierung:
Java RMI - Remote Method Invocation
(Teil 1)
Robert Tolksdorf und Peter Löhr
Überblick
1. Fernaufrufbare Objekte
2. Das Objektverzeichnis rmiregistry
3. Parametersemantik
Dokumentation RMI:
http://java.sun.com/javase/6/docs/platform/rmi/spec/rmiTOC.html
http://java.sun.com/docs/books/tutorial/rmi/
Robert Tolksdorf und Peter Löhr
2
Fernaufrufbare Objekte
Robert Tolksdorf und Peter Löhr
Ein hypothetischer Ansatz
Einfaches Beispiel - Modul im Gewand einer Klasse :
public class Counter {
static int c = 0;
public static int inc(int i)
public static int value()
}
{ return c += i; }
{ return c; }
Testprogramm:
public class Inc {
public static void main(String[] arg) {
int i = Counter.inc(Integer.parseInt(arg[0]));
System.out.println("counter is " + i);
}
}
Robert Tolksdorf und Peter Löhr
4
Ein hypothetischer Ansatz
Szenario für hypothetisches (!) verteiltes Java mit
hochgradiger Zugriffs- und Ortsabstraktion für Klassen:
1. Generator erzeugt für Counter einen Treiber, der mit
Counter und einem Adapter CounterMain ein lauffähiges
Programm ergibt; dieses wird auf Rechner X gestartet,
wählt einen Port p und macht diesen bekannt.
2. Generator erzeugt - mit X und p als Parametern für Counter eine gleichnamige Vertreter-Klasse Counter,
die mit Inc ein lauffähiges Programm ergibt.
3. Beim Ablauf von Inc wird Counter.inc als Fernaufruf
ausgeführt.
Robert Tolksdorf und Peter Löhr
5
Ein hypothetischer Ansatz
4. Counter ist öffentlich, die Lebensdauer ist unabhängig
von den Lebensdauern der Aufrufer.
--> Achtung Nichtsequentialität:
• Wenn mehrere Aufrufer tätig sind, sind überlappende
Ausführungen von inc möglich (jedenfalls sollte das
Fernaufrufsystem dies unterstützen - mit Threading).
• Daher muss beim Aufgerufenen eine geeignete
Ausschlußsynchronisation vorgenommen werden
(die hier der Einfachheit halber weggelassen wurde).
Robert Tolksdorf und Peter Löhr
6
Ein hypothetischer Ansatz
Hinzunahme von Objekten:
public class Counter {
static int c = 0;
public static int inc(int i) { return c += i; }
public static int value()
{ return c; }
int cc = 0;
public int add(int i) { return cc += i; }
}
public class Inc {
public static void main(String[] arg) {
Counter counter = new Counter();
int i = counter.add(Integer.parseInt(arg[0]));
System.out.println("counter is " + i);
}
}
Robert Tolksdorf und Peter Löhr
7
Ein hypothetischer Ansatz
new Counter():
Vertreter-Klasse hat einen Konstruktor, der über eine
geeignete Nachricht den Treiber zur Erzeugung des
eigentlichen Counter-Objekts veranlasst. Als Ergebnis
dieser Aktion wird ein Fernverweis auf das Objekt
zurückgeschickt und als new Counter() abgeliefert.
Robert Tolksdorf und Peter Löhr
8
Ein hypothetischer Ansatz
Hinzunahme von Schnittstellen:
interface Counter {
int add(int i);
int value();
}
public class CounterA implements Counter {
int cc = 0;
public int add(int i) { return cc += i; }
public int value()
{ return cc; }
}
public class CounterB implements Counter {
int cc = 0;
public int add(int i) { cc += i; return cc; }
public int value()
{ return cc; }
}
Robert Tolksdorf und Peter Löhr
9
Ein hypothetischer Ansatz
public class Inc {
public static void main(String[] arg) {
Counter[] counters = new Counter[10];
.....
//initialize array
.....
//with local and/or remote references
for(Counter c : counters) { c.add(.....); }
.....
}
}
Probleme:
1. statische Bindung einer Klasse an einen Rechner
2. lokale und entfernte Objekte können nicht von ein und
derselben Klasse sein
Robert Tolksdorf und Peter Löhr
10
Elemente der RMI
Die Lösung der RMI-Autoren bei Sun Microsystems:
1. Keine Fernaufrufe von statischen Methoden, d.h.
nicht Klassen, sondern nur Objekte sind
fernaufrufbar.
2. Keine Fernerzeugung von Objekten mit new.
3. Original-Klasse und Vertreter-Klasse haben
verschiedene Namen, aber implementieren die
gleiche Schnittstelle.
4. Diese Schnittstelle muss von java.rmi.Remote erben
und Ausnahmen vom Typ java.rmi.RemoteException
extends java.io.IOException vereinbaren
(für verteilungsbedingte Fehler).
Robert Tolksdorf und Peter Löhr
11
Elemente der RMI
import java.rmi.*;
interface Counter extends Remote {
int inc(int i) throws RemoteException;
int value()
throws RemoteException;
}
public class CounterImpl implements Counter {
int c = 0;
public int inc(int i) { return c += i; } // throws entbehrlich
public int value()
{ return c; }
// throws entbehrlich
}
Achtung - nichtsequentielle Benutzung solcher Objekte
(hier wiederum ignoriert)
Robert Tolksdorf und Peter Löhr
12
Bemerkungen
• Remote ist ein „marker interface“, das vom
Fernaufrufsystem gefordert wird, ebenso wie die throwsKlauseln. Ausnahmen vom Typ RemoteException werden
durch verteilungsbedingte Fehler verursacht.
• Ein Objekt der Klasse CounterImpl ist sowohl fernaufrufbar
als auch lokal aufrufbar, d.h. am Ort seiner Erzeugung.
• Um einen Fernaufruf durchführen zu können, muss sich
der Aufrufer einen Fernverweis auf das Objekt beschaffen.
• Das kann er aber nur durch einen Fernaufruf ...
--> Henne-und-Ei-Problem? (siehe unten, „rmiregistry“)
Robert Tolksdorf und Peter Löhr
13
Stub-Erzeugung
Historisch:
Vertreter-Generator rmic (RMI compiler) erzeugt zu einer vorgegebenen Klasse (!)
einen Vertreter, genannt stub,
und einen Treiber, genannt skeleton.
$ rmic CounterImpl
erzeugt im aktuellen Verzeichnis die Dateien
CounterImpl_Stub.class und
CounterImpl_Skel.class
Aktuell:
Vertreter und Treiber werden zur Laufzeit erzeugt, wenn
der Erzeuger eines Objekts dieses dem Fernaufrufsystem
als fernaufrufbar bekannt macht.
Robert Tolksdorf und Peter Löhr
14
Benutzung eines entfernt vorhandenen Objekts:
public class Inc {
public static void main(String[] arg) {
Counter x = ..........
// Fernverweis beschaffen
int i = x.inc(Integer.parseInt(arg[0]));
System.out.println("counter is " + i);
}
}
Die Lebensdauer des Objekts ist hier unabhängig von den
Lebensdauern der aufrufenden Prozesse! (Nichtsequentialität!)
Wie entsteht das Objekt und wie erhält man den Fernverweis?
Robert Tolksdorf und Peter Löhr
15
Das Objektverzeichnis rmiregistry
Robert Tolksdorf und Peter Löhr
rmiregistry
• Das rmiregistry (dt. Register) ist ein Netzdienst, der ein
Objektverzeichnis verwaltet. Typischer Start mit
rmiregistry <port> & (und zu gegebener Zeit kill !)
• Dieses Verzeichnis bildet mnemonische Objektnamen auf
Fernverweise ab. Zu den angebotenen Operationen gehören:
• bind: fügt einen neuen Eintrag hinzu
• lookup: liefert zu einem Namen den zugehörigen Fernverweis
• Ein rmiregistry wird mit host und port identifiziert und ist
über geeignete Bibliotheksklassen benutzbar.
• Ein rmiregistry verwaltet ausschließlich lokale Objekte.
Robert Tolksdorf und Peter Löhr
17
rmiregistry
mein.rechner.de
dein.rechner.de
JVM1
rmiregistry
mein.rechner.de
JVM1
JVM2
dein.rechner.de
lookup
rmiregistry
mein.rechner.de
JVM1
bind
JVM2
dein.rechner.de
doit
Robert Tolksdorf und Peter Löhr
rmiregistry
JVM2
18
rmiregistry
import java.rmi.server.UnicastRemoteObject;
Statische Methode exportObject(x,port) macht das
Objekt x der RMI-Verwaltung als fernaufrufbar bekannt,
erzeugt einen unsichtbaren Treiber und einen Vertreter
und liefert einen Verweis auf den Vertreter.
Der zu verwendende Port kann angegeben werden;
bei Angabe von 0 wird irgendein Port gewählt.
import java.rmi.registry.LocateRegistry;
Statische Methode getRegistry(host,port) liefert
Fernverweis auf ein (lokales oder entferntes) rmiregistry
angesprochen werden kann.
import java.rmi.registry.Registry;
Schnittstelle mit Methoden bind, lookup, .....
Robert Tolksdorf und Peter Löhr
19
vo
rmiregistry
import java.rmi.*;
interface Registry extends Remote {
void bind(String name, Remote object) throws
AlreadyBoundException,
RemoteException,
AccessException;
Remote lookup(String name) throws
RemoteException
NotBoundException,
AccessException;
...
}
Robert Tolksdorf und Peter Löhr
// weitere Operationen: rebind, unbind, list
20
Registrieren im rmiregistry
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class CounterImpl implements Counter {
int c = 0;
public int inc(int i) { return c += i; }
public int value()
{ return c; }
public static void main(String[] arg) throws Exception {
Counter x = new CounterImpl();
Counter stub = (Counter) UnicastRemoteObject.
exportObject(x, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("mycounter", stub);
// main thread dies; hidden thread waits for invocation
}
}
Robert Tolksdorf und Peter Löhr
21
Suchen im rmiregistry
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Inc {
public static void main(String[] arg) throws Exception {
Registry registry = LocateRegistry.getRegistry();
Counter x = (Counter) registry.lookup("mycounter");
int i = x.inc(Integer.parseInt(arg[0]));
System.out.println("counter is " + i);
}
}
Typanpassung - kann scheitern!
Robert Tolksdorf und Peter Löhr
22
Lokaler Test:
$ javac Counter.java
$ javac CounterImpl.java
$ javac Inc.java
$ java CounterImpl
Exception in thread "main" java.rmi.ConnectException:
Connection refused to host: 192.168.2.20
rmiregistry läuft nicht!
$ rmiregistry &
[1] 8774
$
Robert Tolksdorf und Peter Löhr
23
$ java CounterImpl &
[2] 8777
$ java Inc 10
counter is 10
$ java Inc 10
counter is 20
$ java Inc -20
counter is 0
$ kill 8777
CounterImpl-Prozess löschen
$ kill 8774
rmiregistry-Prozess löschen
Robert Tolksdorf und Peter Löhr
24
Test im Netz:
lohr@troll: rmiregistry &
[1] 8774
lohr@troll: java CounterImpl &
[2] 8777
lohr@troll:
Klasse Inc (S. 21) ändern: ....getRegistry("troll.mi.fu-berlin.de“);
kpl@human: java Inc 10
counter is 10
kpl@human:
Robert Tolksdorf und Peter Löhr
25
Parametersemantik
Robert Tolksdorf und Peter Löhr
Parameterübergabe
• Zur Erinnerung: Java kennt nur einen Parametermechanismus
- Wertparameter (call by value)
• ! Das ist gut für die Verteilung (vgl. 04-Fernaufrufe, S.27/28) !
• ! Aber Achtung bei Parametern mit Verweistyp:
• Wenn die Klasse des aktuellen Parameters Remote implementiert:
Übergabe eines Fernverweises.
• Wenn die Klasse des aktuellen Parameters nicht Remote, aber
Serializable implementiert: Übergabe einer Objektkopie.
• Sonst: Laufzeitfehler MarshalException
• In Objekte eingebettete Verweise werden ebenso behandelt.
• Dies gilt für Argumente und Ergebnisse von Fernaufrufen.
Robert Tolksdorf und Peter Löhr
27
Serializable
• Zur Erinnerung: die Schnittstelle java.io.Serializable dient zur
•
•
•
•
Markierung von Klassen, deren Objekte für die unformatierte
(binäre) Ein/Ausgabe in E/A-Strömen vorbereitet sein sollen.
Problem in Java: Felder sind Objekte - können aber nicht
fernaufrufbar sein, weil sie über Indizierung statt über Methoden
angesprochen werden. Felder sind aber Serializable.
Für ein als Parameter eines Fernaufrufs übergebenes
Serializable Objekt erhält der Empfänger keinen Fernverweis,
sondern einen lokalen Verweis auf eine Kopie des Objekts.
Merke: dies ist eine subtile Verletzung der Zugriffsabstraktion!
Folgerung: alle nicht aufrufbaren Objekte, die als Parameter
übergeben werden sollen, müssen Serializable sein. Beispiel:
class Complex implements Serializable { re, im: Float; }
Auch String-Objekte sind Serializable. Das ist aber nicht
problematisch, da sie ohnehin konstant sind.
Robert Tolksdorf und Peter Löhr
28
Subtile Fehler
Beispiel: Aufruf zweier Remote Objekte soll jeweils Feld liefern
Fernaufruf
Feld
Feld
Feld
lokaler Aufruf
Feld
Robert Tolksdorf und Peter Löhr
29
Subtile Fehler
import java.rmi.*;
interface RemoteStack extends Remote {
void push(int elem) throws RemoteException;
void pop() throws RemoteException;
int top() throws RemoteException;
int[] dump() throws RemoteException;
}
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RemoteStackImpl implements RemoteStack {
int[] x = new int[10]; // exceptions ignored for brevity
int sp = 0;
public void push(int elem) { x[sp++] = elem; }
public void pop() { sp--; }
public int top() { return x[sp-1]; }
public int[] dump() { return x; }
}
Robert Tolksdorf und Peter Löhr
30
Subtile Fehler
Erzeugung und Registrierung eines fernaufrufbaren Objekts:
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class CreateRemoteStack {
public static void main(String[] arg) throws Exception {
RemoteStack s = new RemoteStackImpl();
RemoteStack stub = (RemoteStack) UnicastRemoteObject.
exportObject(s, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("remote", stub);
}
}
Robert Tolksdorf und Peter Löhr
31
Subtile Fehler
Weiteres Programm: Erzeugung eines fernaufrufbaren Objekts,
lokaler Aufruf dieses Objekts und Fernaufruf des anderen:
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class RMITest {
public static void main(String[] arg) throws Exception{
Map<String,RemoteStack> stacks =
new HashMap<String,RemoteStack>();
stacks.put("this", new RemoteStackImpl());
Registry registry = LocateRegistry.getRegistry();
RemoteStack x = (RemoteStack) registry.lookup("remote");
stacks.put("that", x);
// jetzt 2 Objekte im Verzeichnis stacks
Robert Tolksdorf und Peter Löhr
32
Subtile Fehler
RemoteStack s1, s2;
s1 = stacks.get("that");
s2 = stacks.get("this");
s1.push(3);
s2.push(3);
int[] there = s1.dump();
int[] here = s2.dump();
there[0] = 100;
here[0] = 100;
System.out.print("there: " + s1.top() + " ");
System.out.print("here: " + s2.top() + "\n");
}
}
Test nach Start von rmiregistry und CreateRemoteStack:
$ java RMITest
there: 3 here: 100
$
Robert Tolksdorf und Peter Löhr
Warum das?? --> Bild S. 29
33
Zusammenfassung
Robert Tolksdorf und Peter Löhr
Zusammenfassung
• Fernaufrufbare Objekte
• müssen eine Schnittstelle implementieren;
• diese Schnittstelle muss von java.rmi.Remote erben;
• client stub implementiert diese Schnittstelle.
• Das Objektverzeichnis rmiregistry
• verzeichnet fernaufrufbare Objekte;
• diese werden vom jeweiligen Erzeuger eingetragen
• und stehen beliebigen Klienten zur Verfügung.
• Parametersemantik
• Für fernaufrufbare Parameter wird Netzverweis übergeben.
• Für serialisierbare Parameter wird Objektkopie übergeben,
• was die Verteilungsabstraktion schwächt!
Robert Tolksdorf und Peter Löhr
35
Literatur
Sun Microsystems: Java Remote Method Invocation Specification
http://java.sun.com/javase/6/docs/platform/rmi/spec/rmiTOC.html
Sun Microsystems: Java Remote Method Invocation Tutorial
http://java.sun.com/docs/books/tutorial/rmi/
Robert Tolksdorf und Peter Löhr
36
Herunterladen