RMI−Server und −Client RMI−Server Remote−Objekt(e) bereitstellen Wurzel−Objekt in Verzeichnisdienst anmelden RMI−Client Wurzel−Objekt in entfernter JVM mittels Verzeichnisdienst finden Methoden von Remote−Objekten aufrufen Ein Java−Programm kann gleichzeitig RMI−Client und RMI−Server sein. Web−Anwendungen mit Java 551 Beispiel: E−Shop Was passiert, wenn Kunde Artikel kauft? Lieferung der Ware zum Kunden (Logistik) Bezahlen (Rechnung, Lastschrift, Kreditkarte usw.) Typische Aufgaben eines Warenwirtschaftssystems Im E−Shop das Rad nicht neu erfinden, sondern Anbindung an WWS schaffen WWS präsentiert sich als Remote−Objekt. E−Shop faßt Bestelldaten zu einem Objekt zusammen. E−Shop schickt Bestelldaten−Objekt per RMI an WWS. Lieferstatus vom WWS holen Web−Anwendungen mit Java 552 Lösungsidee Remote− Interface E−Shop WWS Stub Remote− Objekt Kasse Bestellung Web−Anwendungen mit Java Bestellung 553 Lösungsskizze: Remote−Interface E−Shop »sieht« WWS als Remote−Interface de.rainer_klute.rmi.WWS. Interface erbt von java.rmi.Remote. Spezifiziert die Methoden des WWS, die ein RMI−Client aufrufen kann. Alle Methoden eines Remote−Objekts können eine RemoteException werfen. Netzprobleme Server−Probleme Web−Anwendungen mit Java 554 Lösungsskizze: Remote−Objekt Remote−Objekt implementiert Remote−Interface. Hier: WWSImpl implementiert WWS. Klasse von UnicastRemoteObject ableiten (oder weitere Möglichkeiten nutzen) Beispiel: import java.rmi.*; import java.rmi.server.*; public class WWSImpl extends UnicastRemoteObject implements WWS Konstruktor des Remote−Objekts macht es im Netz erreichbar. Web−Anwendungen mit Java 555 Lösungsskizze: RMI−Server Erzeugt Instanz des Remote−Objekts Remote−Objekt wird im Netz erreichbar. Meldet Instanz bei RMI−Registrierung an Ordnet Remote−Objekt einen Namen zu. Remote−Objekt wird im Netz adressierbar. RMI−Server oft als main()−Methode des Remote− Objekts realisiert. Web−Anwendungen mit Java 556 Lösungsskizze: RMI−Client Fordert Remote−Objekt von RMI−Registrierung an. Adressierung über den vom RMI−Server festgelegten Namen Ruft Methoden des Remote−Objekts auf. Web−Anwendungen mit Java 557 Beispiel: Remote−Interface package de.rainer_klute.rmi; import java.rmi.*; public interface WWS extends Remote { /** <p>Liefert den Status des WWS.</p> */ public String getStatus() throws RemoteException; /** <p>Übermittelt eine Bestellung an das * Warenwirtschaftssystem.</p> */ public String bestellen(Bestellung bestellung) throws RemoteException; } Web−Anwendungen mit Java 558 Beispiel: Remote−Objekt package de.rainer_klute.rmi.server; import import import import java.net.*; java.rmi.*; java.rmi.server.*; de.rainer_klute.rmi.*; public class WWSImpl extends UnicastRemoteObject implements WWS { /** * <p>Konstruktor hier nur zur Illustration: * Konstruktor der Oberklasse * {@link UnicastRemoteObject} macht Objekt netzweit * erreichbar.</p> */ public WWSImpl() throws RemoteException { super(); } Web−Anwendungen mit Java 559 Beispiel: Remote−Objekt /** * <p>Liefert den Status des WWS.</p> */ public String getStatus() throws RemoteException { System.out.println("getStatus()"); return "All subsystems running."; } /** * <p>Übermittelt eine Bestellung an das * Warenwirtschaftssystem.</p> */ public String bestellen(Bestellung bestellung) throws RemoteException { String s = "Order received: " + bestellung; System.out.println(s); return s; } Web−Anwendungen mit Java 560 Beispiel: RMI−Server /** * <p>Startet den RMI−Server.</p> */ public static void main(String args[]) { /* Security−Manager installieren */ if (System.getSecurityManager() == null) System.setSecurityManager (new RMISecurityManager()); try { /* Remote−Objekt erzeugen */ WWS wws = new WWSImpl(); Web−Anwendungen mit Java 561 Beispiel: RMI−Server /* Remote−Objekt in RMI−Registry anmelden. * Format des Namens: "//host/name" */ String wwsName = "//" + InetAddress.getLocalHost(). getHostAddress() + "/WWS"; System.out.println ("Versuche, WWS als \"" + wwsName + "\" zu registrieren."); Naming.rebind(wwsName, wws); System.out.println("Okay."); } } } catch (Exception e) { System.out.println ("Fehler: " + e.getMessage()); e.printStackTrace(); System.exit(1); } Web−Anwendungen mit Java 562 Beispiel: RMI−Client package de.rainer_klute.rmi.client; import java.net.*; import java.rmi.*; import de.rainer_klute.rmi.WWS; public class WWSClient { public static void main(String args[]) { /* Security−Manager installieren */ if (System.getSecurityManager() == null) System.setSecurityManager (new RMISecurityManager()); String remoteName = args[0]; Web−Anwendungen mit Java 563 Beispiel: RMI−Client try { } WWS wws = (WWS) Naming.lookup(remoteName); String status = wws.getStatus(); System.out.println("WWS−Status: " + status); Bestellung b = new BestellungImpl(); b.setKunde(1234); Posten p = new PostenImpl(); p.setArtikel(4711); p.setAnzahl(2); b.addPosten(p); p = new PostenImpl(); p.setArtikel(1000); p.setAnzahl(1); b.addPosten(p); String s = wws.bestellen(b); System.out.println("WWS−Nachricht: " + s); Web−Anwendungen mit Java 564 Beispiel: RMI−Client } catch (Exception e) { System.out.println("Fehler: " + e.getMessage()); e.printStackTrace(); } } Web−Anwendungen mit Java 565 RMI−Registrierung Einfacher Verzeichnisdienst für RMI Verwaltet Zuordnungen von Namen (Strings) zu Remote−Objekten. Notiert zu jedem Remote−Objekt, woher die Klasse dieses Objekts stammt. Namensformat: //{hostname|ipaddress}[:port]/objectname //rmi.rainer−klute.de/WWS //127.0.0.1:1234/WWS Web−Anwendungen mit Java 566 RMI−Registrierung Starten der RMI−Registrierung: CLASSPATH darf Anwendungsklassen nicht enthalten. bash: (unset CLASSPATH; rmiregistry &) csh: (unsetenv CLASSPATH; rmiregistry &) Web−Anwendungen mit Java 567 Getrennte Entwicklung RMI ermöglicht Trennung der Entwicklung von Server und Client. Beispiel: Web−Anwendung für Kunden einer Versicherung Kunde kann persönliche Daten aktualisieren. Jeder Interessent kann »seinen« persönlichen Berater anhand seiner eigenen Adresse ermitteln und kontaktieren. Entwicklung der Web−Anwendung durch Internet− Agentur Versicherung macht Kundendaten und Vertriebsdaten der Internet−Agentur nicht zugänglich. Datenschutz, Betriebsgeheimnis Host Web−Anwendungen mit Java 568 Getrennte Entwicklung Remote− Interface Host− Daten RMI− Server RMI− Client Web− Oberfläche Versicherung entwickelt RMI−Server mit Zugriff auf die Host−Daten. Internet−Agentur entwickelt RMI−Client und Web− Oberfläche (HTML−Generierung mit JSP). Klassen übersetzen Gemeinsam genutzte Interfaces de.rainer_klute.rmi.WWS de.rainer_klute.rmi.Bestellung de.rainer_klute.rmi.Posten Server−Klasse: de/rainer_klute/rmi/server/WWSImpl Client−Klasse: de/rainer_klute/rmi/client/WWSClient de/rainer_klute/rmi/client/BestellungImpl de/rainer_klute/rmi/client/PostenImpl Web−Anwendungen mit Java 570 Klassen übersetzen Übersetzung durchführen: mkdir classes javac −d classes src/de/rainer_klute/rmi/WWS.java src/de/rainer_klute/rmi/Bestellung.java src/de/rainer_klute/rmi/Posten.java javac −d classes src/de/rainer_klute/rmi/server/WWSImpl.java javac −d classes src/de/rainer_klute/rmi/client/WWSClient.java src/de/rainer_klute/rmi/client/BestellungImpl.java src/de/rainer_klute/rmi/client/PostenImpl.java Web−Anwendungen mit Java 571 Stubs und Skeletons generieren RMI−Server RMI−Client Stub Stub Skeleton Repräsentiert das Remote−Objekt im Client. Skeleton Netzfähiges Gegenstück im Server Leitet Zugriffe an Remote−Objekt weiter Web−Anwendungen mit Java 572 Remote− Objekt Stubs und Skeletons generieren RMI−Compiler rmic generiert Stubs und Skeletons aus Remote−Klassen. Beispiel: rmic −classpath classes −d classes de.rainer_klute.rmi.server.WWSImpl Ergebnis: Klassen de.rainer_klute.rmi.server.WWSImpl_Stub de.rainer_klute.rmi.server.WWSImpl_Skel Web−Anwendungen mit Java 573 JAR−Dateien erzeugen RMI−Client und RMI−Server dürfen nicht den selben Classpath nutzen. Entspräche nicht dem Konzept der getrennten Entwicklung. Unterschiedliche JAR−Dateien mit Klassen für Client und Server erzeugen rmicommon.jar mit gemeinsam genutzten Interfaces: WWS, Bestellung, Posten rmiserver.jar mit Server−Klassen: WWSImpl, WWSImpl_Stub, WWSImpl_Skel rmiclient.jar mit Client−Klasse: WWSClient, BestellungImpl, PostenImpl Web−Anwendungen mit Java 574 Programme starten RMI−Server: java −cp dist/lib/rmicommon.jar:dist/lib/rmiserver.jar −Djava.rmi.server.codebase=file:///home/.../classes/ −Djava.rmi.server.hostname=mark −Djava.security.policy=rmiPolicy de.rainer_klute.rmi.server.WWSImpl Versuche, WWS als "//192.168.10.1/WWS" zu registrieren. Okay. RMI−Client: java −cp dist/lib/rmicommon.jar:dist/lib/rmiclient.jar −Djava.security.policy=rmiPolicy de.rainer_klute.rmi.client.WWSClient //mark/WWS WWS−Status: All subsystems running. Server: getStatus() Web−Anwendungen mit Java 575 Klassen laden mit RMI Wesentlich für das korrekte Funktionieren von RMI ist das Übertragen von Klassen. RMI−Client benötigt die Stub−Klasse(n). RMI−Server benötigt die Klassen der Objekte, die ihm der RMI−Client überträgt. Klassen sollten nicht im Classpath enthalten sein. Getrennte Entwicklung von Client und Server Anbieter des RMI−Servers muß Stub−Klasse(n) zum Herunterladen verfügbar machen. Property java.rmi.server.codebase teilt mit, wo die Klassen liegen. Web−Anwendungen mit Java 576 Risiken bei fremdem Code Das Ausführen heruntergeladener Klassen ist ein Sicherheitsrisiko. Code kann beliebige Aktionen ausführen, zu denen der Benutzer berechtigt ist. Daten ausspionieren Daten verändern System schädigen Web−Anwendungen mit Java 577 Sandkastenmodell Java 1.0 und 1.1 unterstützen das Sandkasten− Modell: Java−Applets dürfen nichts nur Anzeige im Browser. Java−Applikationen dürfen alles. Sandkasten−Modell unzureichend: Applets benötigen ggf. Zugriff auf Systemressourcen. Beispiel: Texteditor−Applet speichert Text in lokaler Datei. Einschränkungen für Applikationen erforderlich Beispiel: Vom RMI−Server geladene Klassen dürfen nur in das Verzeichnis /tmp schreiben. Web−Anwendungen mit Java 578 Sicherheit Java 2 ermöglicht fein granulierte Zugriffsberechtigungen. Welche Klasse darf welche Aktion ausführen? Von welchem Ort stammt diese Klasse (Codebase)? Von welchem Hersteller stammt diese Klasse (digitale Signatur)? Policy−Datei legt Zugriffsberechtigungen fest. Beispiel: Klassen von http://rmi.rainer−klute.de:1111/ dürfen in /tmp lesen und schreiben. Security−Manager führt Zugriffskontrollen durch. Beispiel: Berechtigungsprüfung beim Öffnen der Datei /tmp/foo Web−Anwendungen mit Java 579 Beispiel: Policy−Datei keystore "${user.home}/.keystore", "JKS"; grant { permission java.net.SocketPermission "*", "resolve"; permission java.net.SocketPermission "for.bar.de:1024−2000", "connect, accept"; }; grant codeBase "http://rmi:1111/" { permission java.io.FilePermission "<<ALL FILES>>", "read"; permission java.io.FilePermission "/tmp/−", "read, write"; }; grant signedBy "RainerKlute" { permission java.security.AllPermission; }; Erzeugen der Policy−Datei: Texteditor oder policytool Verwenden der Policy−Datei mit −Djava.security.policy=url Web−Anwendungen mit Java 580 Sicherheitsprüfungen aktivieren Java−Programme laufen normalerweise ohne Security−Manager. Alles ist erlaubt. Kompatibilität zu Java 1.0 und 1.1 ist gewährleistet. Standard−Security−Manager aktivieren Beim Start des Java−Programms: java −Djava.security.manager −Djava.security.policy=policy−url MeineKlasse Programmgesteuert: if (System.getSecurityManager() == null) Sicherheitsprüfungen durchführen Viele Methoden der Java−Standardklassen führen Sicherheitsprüfungen durch. Vor einer potentiell gefährlichen Operation Beispiel: Datei zum Lesen öffnen SecurityManager security = System.getSecurityManager(); if (security != null) security.checkRead(filename); Klasse SecurityManager besitzt zahlreiche checkXXX−Methoden: checkAccept, checkAccess, checkAwtEventQueueAccess, checkConnect, Sicherheitsgrundeinstellungen JDK−Installation gibt Sicherheitsgrundeinstellungen vor. Dateien in ${java.home}/jre/lib/security, z.B. /opt/local/jdk1.3/jre/lib/security Hauptdatei java.security Hier nur einige Einstellungen zum Thema Policy # URLs von Policy−Dateien: policy.url.1=file:${java.home}/lib/security/java.policy policy.url.2=file:${user.home}/.java.policy # −Djava.security.policy=someURL erlaubt? policy.allowSystemProperty=true Web−Anwendungen mit Java 583 Einstellungen in java.policy Standarderweiterungen dürfen alles. grant codeBase "file:${java.home}/lib/ext/*" { permission java.security.AllPermission; }; Jeder Code darf den aktuellen Thread stoppen, auf nichtprivilegierten Ports lauschen und Standard− Properties lesen. grant { permission java.lang.RuntimePermission "stopThread"; permission java.net.SocketPermission "localhost:1024−", "listen"; permission java.util.PropertyPermission "java.version", }; Klassen laden mit RMI RMI−Classloader lädt Klassen nur dann, wenn ein Security−Manager installiert ist. RMI−Security−Manager in Client und Server installieren if (System.getSecurityManager() == null) System.setSecurityManager (new RMISecurityManager()); Web−Anwendungen mit Java 585 RMI−Server starten RMI−Server starten (1. Versuch) java −cp dist/lib/rmiserver.jar:dist/lib/rmicommon.jar de.rainer_klute.rmi.server.WWSImpl Fehler: java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve) Security−Manager verbietet Verbindungsaufbau. Web−Anwendungen mit Java 586 RMI−Server starten RMI−Server registriert Wurzel−Objekt bei einer RMI−Registrierung. Naming.rebind("//localhost/WWS", new WWSImpl()); Verbindung zur RMI−Registrierung aufbauen Hier: localhost, Port 1099 (Voreinstellung) Alternativer Port möglich: "//localhost:1234/WWS" Alternativer Rechner möglich: "//other.host.de/WWS" AccessControlException, weil RMI−Server keine Netzverbindung zur RMI−Registrierung aufbauen kann Web−Anwendungen mit Java 587 RMI−Server starten Policy−Datei mit der benötigten Berechtigung erstellen Mit Texteditor oder policytool grant { permission java.net.SocketPermission "localhost:1099", "connect"; }; RMI−Server starten (2. Versuch mit Policy−Datei) java −cp dist/lib/rmiserver.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiServerPolicy de.rainer_klute.rmi.server.WWSImpl Fehler: RMI−Server starten RMI−Registrierung starten (unset CLASSPATH; rmiregistry &) RMI−Server starten (3. Versuch mit laufender RMI− Registrierung) java −cp dist/lib/rmiserver.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiServerPolicy de.rainer_klute.rmi.server.WWSImpl Fehler java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: de.rainer_klute.rmi.server.WWSImpl_Stub Web−Anwendungen mit Java 589 RMI−Server starten Fehlerursache finden java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: de.rainer_klute.rmi.server.WWSImpl_Stub ... at java.rmi.Naming.rebind at de.rainer_klute.rmi.server.WWSImpl.main RemoteException: Fehler nicht im RMI−Server, sondern in einer Remote−Methode ClassNotFoundException: Kommunikationspartner kennt Klasse WWSImpl_Stub nicht. Naming.rebind: Kommunikationspartner ist die RMI−Registrierung. Web−Anwendungen mit Java 590 Sag mir, wo die Klassen sind RMI−Registrierung soll Instanz von WWSImpl_Stub speichern und auf Anforderung an Clients ausliefern. RMI−Registrierung benötigt die Klasse des Objekts. Klasse nicht im Classpath enthalten. RMI−Registrierung bewußt ohne Classpath gestartet RMI−Server muß der Registrierung verraten, wo die Klassen sind. Property java.rmi.server.codebase URL, z.B. file:///home/klute/classes/ oder http://classes.rainer−klute.de:1111/ RMI−Partner muß Ort der Klassen verraten. Web−Anwendungen mit Java 591 RMI−Registrierung Name: WWS ... RMI−Registrierung Objekt: Codebase: Server: Stub http://cserver/dir/ rserver ... ... ... RMI−Client RMI−Server Stub Stub http://cserver/dir/ java.rmi.server.codebase= http://cserver/dir/ rserver Class−Server cserver Web−Anwendungen mit Java 592 RMI−Server starten RMI−Server starten (4. Versuch mit Codebase) java −cp dist/lib/rmiserver.jar:dist/lib/rmicommon.jar −Djava.rmi.server.codebase=file:///home/klute/classes/ −Djava.security.policy=rmiServerPolicy de.rainer_klute.rmi.server.WWSImpl Ausgabe: Versuche, WWS als "//127.0.0.1/WWS" zu registrieren. Okay. Endlich Erfolg! Für’s erste jedenfalls... Web−Anwendungen mit Java 593 RMI−Client starten RMI−Client starten (1. Versuch) java −cp dist/lib/rmiclient.jar:dist/lib/rmicommon.jar de.rainer_klute.rmi.client.WWSClient //mark/WWS java.security.AccessControlException: access denied (java.net.SocketPermission mark resolve) Client benötigt das Recht, Host−Namen per DNS aufzulösen, sprich: die IP−Adresse zu ermitteln. Policy−Datei für Client anlegen grant { permission java.net.SocketPermission "*", "resolve"; }; Web−Anwendungen mit Java 594 RMI−Client starten RMI−Client starten (2. Versuch mit Policy) java −cp dist/lib/rmiclient.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiClientPolicy de.rainer_klute.rmi.client.WWSClient //mark/WWS java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.2:1099 connect,resolve) Client benötigt das Recht, auf die RMI−Registry zuzugreifen. Hier: Port 1099 auf beliebigem Host grant { }; permission java.net.SocketPermission "*", "resolve"; permission java.net.SocketPermission "*:1099", "connect"; Ggf. Hosts einschränken oder Ports erweitern Web−Anwendungen mit Java 595 RMI−Client starten RMI−Client starten (3. Versuch) java −cp dist/lib/rmiclient.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiClientPolicy de.rainer_klute.rmi.client.WWSClient //mark/WWS java.lang.ClassNotFoundException: java.security.AccessControlException: access denied (java.io.FilePermission /home/klute/classes/− read) Client lernt aus Stub, wo die zugehörige Klasse ist. Client benötigt das Recht, auf das Klassenverzeichnis zuzugreifen. grant { }; permission java.net.SocketPermission "*", "resolve"; permission java.net.SocketPermission "*:1099", "connect"; permission java.io.FilePermission "/home/klute/classes/−", "read"; Web−Anwendungen mit Java 596 RMI−Client starten RMI−Client starten (4. Versuch) java −cp dist/lib/rmiclient.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiClientPolicy de.rainer_klute.rmi.client.WWSClient //mark/WWS java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:2833 connect,resolve) Client benötigt das Recht, eine Verbindung zum RMI− Server aufzubauen (genauer: zum Remote−Objekt). Port−Nummer läßt sich im Konstruktor von UnicastRemoteObject festlegen. grant { }; permission java.net.SocketPermission "*", "connect"; permission java.io.FilePermission "/home/klute/classes/−", "read"; Web−Anwendungen mit Java 597 RMI−Client starten RMI−Client starten (5. Versuch) java −cp dist/lib/rmiclient.jar:dist/lib/rmicommon.jar −Djava.security.policy=rmiClientPolicy de.rainer_klute.rmi.client.WWSClient //mark/WWS java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: java.net.ConnectException: Verbindungsaufbau abgelehnt Stub sucht Skeleton auf dem eigenen Rechner (127.0.0.1 = localhost) statt auf der Server−Maschine. RMI−Server hat sich bei der RMI−Registrierung als »localhost« registriert. Diesen Rechnernamen gibt die RMI−Registrierung mit dem Stub an den Client weiter. Web−Anwendungen mit Java 598 RMI−Server neu starten Property java.rmi.server.hostname legt Hostnamen oder IP−Adresse für die Registrierung fest. RMI−Server neu starten java −cp dist/lib/rmiserver.jar:dist/lib/rmicommon.jar −Djava.rmi.server.codebase=file:///home/klute/classes/ −Djava.security.policy=rmiServerPolicy −Djava.rmi.server.hostname=rserver de.rainer_klute.rmi.server.WWSImpl RMI−Client neu starten Bingo! Web−Anwendungen mit Java 599 Objekte übertragen Client stellt Bestellung zusammen. Interfaces Bestellung, Posten Implementierungen BestellungImpl, PostenImpl Client schickt Bestellung zum Server. Bestellung b = new BestellungImpl(); // ... b.addPosten(p); String s = wws.bestellen(b); System.out.println("WWS−Nachricht: " + s); Übertragen zweier serialisierter Objekte (Marshalling). Klassen−Server Problem in der Beispielanwendung: Zugriff auf Klassen über das Dateisystem Im allgemeinen nicht möglich Lösung für produktive Anwendung: Klassendateien auf FTP− oder HTTP−Server legen Immer noch problematisch: Übertragen der Klassen vom RMI−Client zum RMI−Server Lösung: RMI−Client implementiert eigenen HTTP− Server. RMI überträgt Objekte. HTTP−Server überträgt Klassen. Web−Anwendungen mit Java 601 RMIClassServer RMIClassServer: HTTP−Server für Klassen Basiert auf Mini−Klassenserver von Sun Microsystems. Wird in normale RMI−Client− oder RMI−Server− Anwendung eingebaut. RMIClassServer rcs = RMIClassServer.instance(); int port = rcs.run(); Classpath der Anwendung in System−Property de.rainer_klute.rmi.classserver.classpath vorgeben Ermittelt freien Port und startet Server−Thread. RMI: Nicht behandelte Themen RMI über Firewalls HTTP−Tunnelling Objekte auf festgelegtem Port exportieren Eigene Socket−Typen Z.B. RMI über SSL Remote Object Activation Remote−Objekte auf Anforderung erzeugen Marshalling RMI−IIOP RMI−Programm (Java) spricht mit CORBA− Programm. Web−Anwendungen mit Java 603