Java RMI – Remote Method Invocation Ziel: Aufruf von Instanzmethoden entfernter Objekte basierend auf Java. Paket: java.rmi und Unterpakete Topologie: RMI Registry RMI Server RMI Client Der Server registriert in der Registry Objekte, deren Methoden von entfernten Clients aufgerufen werden können. Registry ist Name Service, d.h. Abbildung von Name auf Objekt. Der Client führt ein Lookup in der Registry nach einem bestimmten Namen durch und erhält Stub des entfernten Objekts als Ergebnis. Anschließend kommuniziert der Client direkt mit dem Server durch Aufruf von Stub-Methoden. Diese werden über das Netz zur Server geschickt und dort ausgeführt. Das Ergebnis kommt wieder über das Netz zum Client. Aufrufe sind synchron, d.h. Client wartet auf das Ergebnis. Registry kann in Serverprozess sein oder separate JVM. Registry verwendet TCP/IP-Port zur Kommunikation mit Server und Client. Remote Object verwendet eigenen TCP/IP-Port zur Kommunikation mit Client. Hinweis: bei Verwendung von Firewalls kann es sein, dass diese Ports gesperrt sind -> Firewall umkonfigurieren. Entfernte Objekte Schnittstelle für entfernte Objekte definiert durch Interface. Diese Schnittstelle erweitert java.rmi.Remote. Alle Methoden werfen potentiell RemoteException. Beispiel: import java.rmi.*; public interface HelloWorld extends Remote { String sayHello() throws RemoteException; } Serverseitig wird dieses Interface durch eine Klasse implementiert, die von UnicastRemoteObject erbt und die Schnittstelle implementiert. public class HelloServer extends UnicastRemoteObject implements HelloWorld { public String sayHello() throws RemoteException { return "Hello World"; } } Start der Registry lokal in Server mittels Hilfsklasse LocateRegistry. Könnte auch zum Zugriff auf externe Registry verwendet werden. import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; Registry reg = LocateRegistry.createRegistry(1099); Registrieren eines Server-Objektes mit bind (oder rebind). reg.bind("HelloServer", new HelloServer()); Erzeugen einer stub-Klasse für entfernte Klienten aus HelloServer.java mittels Commandline-Tool rmic. rmic HelloServer -d targetDir Die erzeugt Stub-Klasse muss im Klassenpfad des Clients verfügbar sein. Lookup von HelloServer in Client z.B. mittels RMI Pseudo URL: rmi://rmireghost:rmiregport/name. HelloWorld helloStub = (HelloWorld)java.rmi.Naming.lookup( "rmi://localhost:1099/HelloServer"); Verwenden des entfernten Objekts durch Methodenaufrufe. System.out.println(helloStub.sayHello()); Zur Übersetzung des Clients wird die Schnittstelle HelloWorld benötigt. Zur Ausführung wird HelloWorld und die Stub-Klasse benötigt. Serialisierung der Methodenaufrufe Um einen Methodenaufruf über das Netzwerk zu transportieren erfolgt eine Serialisierung aller Parameter. => Alle Parameter müssen serialisierbar sein. Serialisierung ergibt Kopie der Paramter beim Empfänger. Pointeridentität innerhalb der Parameter garantiert. Parameter vom Typ Remote (z.B. UnicastRemoteObject) werden speziell behandelt: Es wird eine serialisierte Objektreferenz übertragen, nicht der Inhalt. Solche Objekte müssen nicht über die RMI Registry registriert werden. Die RMI-Registry verwendet diesen Mechanismus selbst. Die Registry ist ein Client für alle registrierten Objekte. Der Empfänger verwendet Remote Objekte via RMI, ausser das Objekt ist im Empfänger implementiert. Symmetrie: Ein Client kann zusätzlich auch Server sein und umgekehrt. Verteilte Garbage Collection Prinzip der automatischen Speicherbereinigung bei RMI beibehalten durch verteilte GC via Reference Counting und Leases. Server merkt sich exportierte Objekte und Zeit für letzte Verwendung (Lease start). Client merkt sich importierte Objekte und sendet periodisch LeaseRenewal Messages zum Server. Stirbt der letzte Client eines Objekts, so folgen keine Renewal Messages und das Objekt wird nach timeout am Server gelöscht. Typische Lease-Intervalle sind 10 Minuten. Nach 5 Minuten versucht der Client einen Lease-Renewal. Bei Fehlschlag folgen einige Wiederholungen. Durch null-Setzen von Referenzen plus GC in Client kann Freigabe von Objekten am Server beschleunigt werden. Threading Modell RMI Server können von mehreren Clients parallel verwendet werden. Jeder Request läuft in eigenem Thread ab. Synchronisierung der Server-Operationen beachten! Bezug zu Projekt zentrales Modell als RMI remote Object implementieren. JavaBeans Event Pattern berücksichtigen. Lokales Modell wirkt als Bridge für entferntes Modell. registriert (Remote)Listener bei zentralem Server-Objekt. zentraler Server notifiziert remote Clients bei Änderungen. Views verwenden weiterhin lokales Modell wie in lokaler Lösung. Ein Client wird zu irgendeiner beliebigen Zeit notifiziert und dazu am Client ein eigener Thread gestartet. Dieser darf nicht direkt mit Swing arbeiten. Synchronisierung mit Swing event-dispatcher thread durch Registrierung einer Aktion (Runnable), die von Swing aus gestartet wird. javax.swing.SwingUtilities.invokeLater( new Runnable() { public void run() { fireEvent(e); } }); Class Loader und Security Java RMI erlaubt das Nachladen von Klassen zwischen Server und Client (beide Richtungen), also ausserhalb des normalen CLASSPATH. Das ist aus Sicherheitsgründen normalerweise in Java nicht erlaubt. Abhilfe: Setzen eines geeigneten SecurityManagers (oder alle Klassen über CLASSPATH verfügbar machen). System.setSecurityManager(new RMISecurityManager()); Socket Factories Der RMI-Mechanismus verwendet normalerweise TCP/IP Sockets. Erweiterungen (Spezialisierungen) von RMI sind möglich durch Implementierung eigener SocketFactories. Eine global verankerte SocketFactory wird von RMI verwendet, um Sockets zu erzeugen. Spezielle SocketFactory könnte zum Beispiel Request via http oder https transportieren oder einen ganz andern Transport Layer verwenden. SSL und Routing über http bzw. https wird bereits in Java2 RMI unterstützt. Hinweis: http/https sind one way, also nur Client -> Server.