Stand: 09.2005 Praktikum Rechnernetze Praktikumsversuch Remote Method Invocation (java.rmi) - verteilter Methodenaufruf mit RMI – Praktikumsdokumentation -1- Stand: 09.2005 1 Einführung .................................................................................................................... - 3 - 2 Grundlagen ................................................................................................................... - 3 2.1 2.1.1 Stub................................................................................................................. - 4 - 2.1.2 Remote Refence Layer ................................................................................... - 4 - 2.1.3 Transport Layer .............................................................................................. - 5 - 2.2 4 5 Aufbau und Ablauf einer RMI-Anwendung...................................................... - 5 - 2.2.1 Bestandteile der Anwendung ......................................................................... - 5 - 2.2.2 Ablauf von RMI ............................................................................................. - 5 - 2.3 3 RMI-Architektur .................................................................................................. - 4 - Besonderheiten von Java-RMI ............................................................................ - 6 - 2.3.1 Parameterübergabe ......................................................................................... - 6 - 2.3.2 Dynamisches Laden von Klassen ................................................................... - 7 - RMI-Beispiel ................................................................................................................. - 8 3.1 Interface ................................................................................................................ - 8 - 3.2 Implementierung (MultiplyImpl.java) ............................................................... - 8 - 3.3 Generierung des Stubs ......................................................................................... - 9 - 3.4 Implementierung von Server und Client ........................................................... - 9 - 3.5 Ausführen der Anwendung ............................................................................... - 11 - Der Versuch ................................................................................................................ - 12 4.1 Versuchsbeschreibung ....................................................................................... - 12 - 4.2 Starten des Versuchs .......................................................................................... - 14 - 4.3 Sicherung der Versuchsergebnisse ................................................................... - 14 - Literatur ...................................................................................................................... - 15 - -2- Stand: 09.2005 1 Einführung Der Wunsch, Funktionalitäten und Ressourcen auf einem entfernten Rechner zu nutzen ist die Grundmotivation für den Einsatz von Computernetzwerken. Während der letzten Jahrzehnte wurden zahlreiche Möglichkeiten entwickelt, um diesen Wunsch auf möglichst einfache und effiziente Art zu erfüllen. Einer der erfolgreicheren Lösungsansätze ist der Remote Procedure Call (RPC), der es dem Anwendungsentwickler ermöglicht, einen entfernten Prozeduraufruf ähnlich zu einem lokalen Aufruf durchzuführen. Maßgebliche Innovation war hierbei die Abstraktion von der tatsächlichen Netzwerkübertragung, wie sie z.B. bei der SocketProgrammierung nicht erreicht wird. Dennoch weist der RPC einige Nachteile auf, die aus seinem Wesen als prozedurales Modell resultieren. Hervorzuheben sind hier vor allem die mangelnde Möglichkeit der Migration von Logik zwischen verschiedenen Rechnern oder die strenge Client-/Serversemantik, die teilweise zu einem unflexiblen System führen. Mit der allgemein zunehmenden Bedeutung der Objektorientierung wurde das Konzept, auf dem der RPC basiert in die Welt der Objektorientierung übertragen. Remote Method Invocation RMI ist dabei der Java-Mechanismus, der den Aufruf einer Methode eines entfernten Objektes ermöglicht. Somit stellt RMI die Erweiterung des klassischen Client/Server-Modell auf ein verteiltes objektorientiertes Modell dar. Dabei beseitigt es die angedeuteten Schwächen und bildet somit die Grundlage zum Design flexibler, verteilter Systeme auf Basis der Objektkommunikation. Die grundlegende Funktionsweise von RMI soll in der Folge beschrieben und an einem Beispiel praktisch dargestellt werden. 2 Grundlagen Abbildung 1: RMI-Architektur -3- Stand: 09.2005 2.1 RMI-Architektur Das Hauptdesignziel von RMI war die Trennung der Beschreibung eines Verhaltens (Interface) von der eigentlichen Implementierung dieses Verhaltens. Auf diese Weise ist es möglich, Teile der Logik auf einem entfernten Rechner bzw. genauer in einer anderen Java Virtual Machine (JVM) auszuführen. Ein Client-Programm erhält durch das Interface nur die Information, wie eine bestimmte Methode eines Objektes zu verwenden ist. Die eigentliche Ausführung dieser Methode bleibt ihm allerdings verborgen. Das ClientProgramm soll von dem, was wirklich bei dem Aufruf auf einem entfernten Objekt geschieht, möglichst wenig erfahren. Aus seiner Perspektive soll der gesamte Ablauf vielmehr wie ein Methodenaufruf eines lokalen Objektes aussehen. Um die Details der Kommunikation kümmern sich die drei Schichten des RMI-Systems: der Stub, die Remote Reference Layer und die Transport Layer. 2.1.1 Stub Damit die Methodenaufrufe auf einem entfernten Objekt nicht ins Leere verlaufen, müssen sie abgefangen und an ein reales, entferntes Objekt weitergeleitet werden. Dafür ist der Stub verantwortlich. Er ist ein Stellvertreter (Proxy) der eigentlichen Implementierung einer Klasse, der die genaue Aufrufsemantik beschreibt. Somit ist also eine Referenz eines Clients auf ein entferntes Objekt zunächst nichts anderes, als eine Referenz auf einen lokalen Stub. Ruft ein Client-Programm eine entfernte Methode auf, so leitet der Stub diesen Aufruf weiter, indem er auf einem RemoteRef-Objekt die so genannte invokeMethode unter Angabe der entfernten Methode verwendet. Des Weiteren sorgt er für die Umwandlung der Parameter in ein sequentielles Format (Marshalling), so dass sie daraufhin über ein Netzwerk übertragen werden können bzw. für die Regeneration der Objekte aus diesem Format (Unmarshalling). Nachdem die Parameter übergeben wurden, wartet der Stub auf das Ergebnis, das er dann an den Aufrufer zurückliefert. Diese Stellvertreter können automatisch aus der Klasse des entfernten Objektes unter Verwendung eines Hilfsprogramms generiert werden, so dass sich ihre Implementierung aus dem Verantwortungsbereich des Programmierers entzieht. In der ersten Version von Java-RMI (JDK 1.1) existierte auf Server-Seite zum Stub ein direktes Gegenstück, das Skeleton. Dieses war dazu gedacht, die Kommunikation mit dem Stub durchzuführen. Seit dem JDK 1.2 wird allerdings eine erweiterte Version des Java Remote Method Protocol (JRMP) verwendet, welche das Skeleton überflüssig macht. 2.1.2 Remote Refence Layer Eine weitere wichtige Aufgabe auf dem Weg von entfernter Objektreferenz auf Clientseite zur realen Objektimplementierung auf Serverseite ist die Verwaltung solcher Referenzen. Wie bereits erwähnt wurde, setzt der Stub den entfernten Methoden-Aufruf auf den Aufruf der invoke-Methode des RemoteRef-Objektes um und gibt dabei als Parameter die Bezeichnung der entfernten Methode an. Dieses RemoteRef-Objekt wird von der RemoteReference Layer zur Verfügung gestellt und dient der Anpassung einer lokalen Aufrufsemantik an die Aufrufsemantik einer entfernten Methode. Mit dem Java 2 SDK wurde die Remote Reference Layer um neue Formen des Aufrufs erweitert. Nun ist es beispielsweise möglich, nicht nur einfache Unicast-Anfragen per RMI zu stellen, sondern mehrere Objekte per Multicast anzusprechen oder entfernte Objekte erst -4- Stand: 09.2005 bei Bedarf zu aktivieren. 2.1.3 Transport Layer Die unterste Schicht der RMI-Architektur, die Transport Layer, kümmert sich um die eigentliche Verbindung und Datenübertragung zwischen den beteiligten JVMs. Bei der Netzwerkverbindung handelt es sich bevorzugt um TCP/IP. Darauf aufbauend kommt häufig das Java Remote Method Protocol zum Einsatz, das sich um die Kommunikationsdetails kümmert. Eine Alternative dazu stellt RMI-IIOP dar, durch das eine Integration von Java-RMI in Corba erzielt wird. 2.2 Aufbau und Ablauf einer RMI-Anwendung Mittlerweile existiert eine Vielzahl von Optionen, wie RMI im Detail eingesetzt und an spezifische Anforderungen angepasst werden kann. Nachfolgend werden die grundlegenden Bestandteile einer Anwendung auf Basis von RMI beschrieben und der Kommunikationsablauf abstrakt dargestellt. Anhand eines Beispiels erfolgt im nächsten Abschnitt dann die Demonstration des Vorgehens bei der Anwendungsentwicklung. 2.2.1 Bestandteile der Anwendung Durch die klare Trennung von Beschreibung eines Verhaltens und dessen Implementierung ergibt sich geradezu intuitiv der Aufbau einer RMI-Anwendung. Der Client, der nun mal selbst nicht über ein entferntes Objekt verfügt, muss wissen, wie er eine bestimmte Methode dieses Objektes verwenden kann. Er muss also über eine Beschreibung der Methode mit ihren Parametern und ihrem Rückgabewert verfügen. Anstatt für diesen Zweck eine spezielle Beschreibungssprache wie z.B. IDL zu verwenden, setzt RMI hierbei auf die Java-eigene Beschreibung durch ein einfaches Java-Interface. Dieses Interface muss (direkt oder indirekt) von der Schnittstelle Remote erben und die deklarierten Methoden müssen eine RemoteException werfen können, um Fehler während der Übertragung oder entfernten Ausführung anzeigen zu können. Auf Server-Seite ist natürlich die Implementierung der Methoden dieser Schnittstelle notwendig. Sobald das geschehen ist, kann der Stub für diese Implementierung generiert werden. Nun existiert noch das Problem, dass ein Client-Programm eine Referenz auf ein entferntes Objekt erhalten muss, um das entfernte Objekt über diese Referenz ansprechen zu können. Die Suche nach dem Objekt geschieht mit Hilfe eines Namensdienstes, der somit notwendiger Bestandteil der verteilten Anwendung ist. 2.2.2 Ablauf von RMI Das definierte Ziel bei der Verwendung eines entfernten Objektes ist es, dieses wie ein lokales Objekt behandeln zu können. In der Tat unterscheidet den entfernten Methodenaufruf nur ein klein wenig Vorarbeit von der lokalen Variante. Der Ablauf ist in der folgenden Abbildung dargestellt. -5- Stand: 09.2005 Abbildung 2: Ablauf von RMI Der erste Schritt ist die Erzeugung eines entfernten Objektes und seine Anmeldung beim Namensdienst durch das Server-Programm. Der Namensdienst kann sowohl auf dem gleichen Rechner, wie das Server-Programm laufen oder aber sich auf einem anderen Rechner befinden. Im einfachsten Fall handelt es sich bei diesem Dienst um einen Assoziativspeicher, der logische Namen auf die zugehörigen Stub-Objekte abbildet. Sobald dieser Export geschehen ist, kann ein Client eine Anfrage nach einem bestimmten Objekt durch die Angabe des logischen Namens formulieren. Die Adresse des Namendienstes muss dem Client dazu natürlich bekannt sein. Als Rückgabe seiner Anfrage erhält der Client das gewünschte Stellvertreterobjekt. Im dritten Schritt kann der Client analog zu lokalen Aufrufen nun Methoden auf dem entfernten Objekt aufrufen. Diese Aufrufe fließen durch das RMI-System, werden über das Netzwerk zum Server übertragen, dort entgegen genommen und ausgeführt. Im letzten Schritt versendet der Server auf gleichem Wege die Rückgabe, die dann vom ClientProgamm entgegen genommen wird. 2.3 Besonderheiten von Java-RMI In diesem Abschnitt soll ganz kurz auf zwei Eigenarten von RMI eingegangen werden: die Parameterübergabe und die Möglichkeit des dynamischen (Nach-)Ladens von Klassen. 2.3.1 Parameterübergabe Ein wichtiger Aspekt von Java-RMI wurde bis jetzt ausgelassen, der vom normalen JavaMechanismus abweicht: Die Form der Parameter- und Ergebnisübergabe. Grundlegend unterscheidet man bei der Form der Übergabe zwischen Wertparametern (pass-by-value) und Referenzparametern (pass-by-reference). Bei primitiven Datentypen (boolean, byte, short, int, ...) gibt es keinerlei Unterschiede zum lokalen Aufruf, da diese Parameter als Wertparameter übergeben werden. Sobald allerdings Objekte ins Spiel kommen, ändert sich der Ablauf. Hierbei muss zwischen der Übergabe lokaler Objekte und der Übergabe von entfernten Objektreferenzen differenziert werden. Bei Java-RMI erfolgt die Übergabe eines normalen Objekts als Wertparameter, sprich es wird eine Kopie erzeugt. Die Frage ist natürlich nun, wie eine solche Übertragung von statten geht. Für solche Aufgaben hält Java den Mechanismus der ObjektSerialisierung bereit. Durch die Serialisierung wird ein Objekt auf ein sequenzielles (lineares) Format abgebildet, das dann beispielsweise auf einen Datenträger gespeichert -6- Stand: 09.2005 oder über ein Netzwerk übertragen werden kann. Damit ein Objekt serialisierbar ist, muss es das Interface java.io.Serializable implementieren. Leider gibt es einige Einschränkungen, die für serialisierbare Objekte bestehen. So werden z.B. statische Klassenvariablen nicht mit in den Datenstrom geschrieben. Die andere Form, bei der Objekte als Parameter vorkommen, sind Referenzen auf entfernte Objekte (Remote Objects). Diese werden grundsätzlich auch als Referenzparameter übergeben. Dabei wird ausschließlich der Stub des entfernten Objekts übertragen. 2.3.2 Dynamisches Laden von Klassen Bis jetzt ist davon ausgegangen worden, dass ein Objekt einer Klasse angefordert wird, die dem Client bekannt ist. Allerdings kann auch der Fall eintreten, dass der Client plötzlich mit Objekten zu tun bekommt, deren Klassen er nicht kennt. Man denke beispielsweise an den einfachen Fall, dass eine entfernte Methode eine Liste zurückgibt, in der sich unbekannte Objekte befinden. Bei einem solchen Szenario ist die Fähigkeit von RMI, dynamisch Klasseninformationen nachzuladen notwendig. Für diesen Fall ist der RMIClassLoader geschaffen worden, der Stellvertreter oder benötigte Klassen bei Bedarf in die lokale JVM laden kann. Natürlich sieht diese Möglichkeit unter einem anderen Licht betrachtet nach einem riesigen Sicherheitsloch aus, da man prinzipiell jede beliebige Klasse übertragen könnte. Aus diesem Grund gibt es die Möglichkeit, Sicherheitsrichtlinien zu definieren, die standardmäßig eingehalten werden und mit Hilfe eines speziellen Sicherheitssystems (SecurityManager) angepasst werden können. Der SecurityManager wird auch im Praktikumsversuch zum Einsatz kommen. -7- Stand: 09.2005 3 RMI-Beispiel Die im vorherigen Abschnitt dargestellten theoretischen Grundlagen sollen nun anhand eines kleinen Beispiels verdeutlicht werden. Das Ziel dieses Abschnittes ist die Entwicklung eines verteilten Programms, bei dem ein Client mit Hilfe von RMI eine Methode zur Multiplikation zweier Integer-Werte auf einem entfernten Objekt aufrufen kann. Dazu ist eine Schnittstelle notwendig (Interface), die die Methode definiert, die Implementierung dieser Methode, der Stub und natürlich ein Client- und ein ServerProgramm. Um das hier beschriebene Vorgehen wirklich zu verinnerlichen ist es das Beste, die einzelnen Schritte selbst durchzuführen und auf einem Rechner zu testen. 3.1 Interface Das Interface ist natürlich sehr einfach, da es im gewählten Beispiel nur eine einzige Methode beschreibt. Wichtig ist hier, dass das Interface von Remote erbt und dass durch die entfernte Methode eine RemoteException geworfen werden kann. //Multiply.java import java.rmi.*; public interface Multiply extends Remote{ public int multiply(int a, int b) throws RemoteException; } 3.2 Implementierung (MultiplyImpl.java) Nachdem durch das Interface das abstrakte Verhalten der Methode beschrieben wurde, wird das Verhalten nun implementiert. Die Implementierung muss zum einen eine spezielle Klasse erweitern und zum anderen einen Konstruktor anbieten. Bei der hier erweiterten Klasse handelt es sich um UnicastRemoteObject, die eine Übertragung der Daten mittels TCP-Sockets realisiert und sich um die Anmeldung des Objektes beim RMISystem kümmert. Hieraus entsteht ein potentielles Problem, falls aufgrund mangelnder Mehrfachvererbung von Java eine Erweiterung von UnicastRemoteObject nicht möglich ist. In diesem Fall muss sich ein Objekt selbst um seine Anmeldung kümmern. Das geschieht mit Hilfe der Methode UnicastRemoteObject.exportObject(Remote). -8- Stand: 09.2005 //MultiplyImpl.java import java.rmi.*; import java.rmi.server.*; public class MultiplyImpl extends UnicastRemoteObject implements Multiply{ public MultiplyImpl() throws RemoteException{} public int multiply(int x, int y) throws RemoteException{ return x * y; } } 3.3 Generierung des Stubs Wie bereits erwähnt, muss sich der Anwendungsentwickler nicht selbst um die Generierung des Stubs kümmern. Für diese Arbeit existiert das Programm rmic (RMICompiler), das im SDK (seit Version 1.1.) enthalten ist. Es erstellt aus der bereits übersetzten, entfernten Klasse die Datei Klassenname_Stub.class. Falls die Anwendung für ein JDK ab Version 1.2. entwickelt werden soll muss als Option -v1.2. angegeben werden, da hier keine Skeleton-Klasse benötigt wird. Eine weitere interessante Option ist -keep, durch die rmic angewiesen wird, die zwischenzeitlich erstellte Quellcodedatei des Stubs nach der Übersetzung in ein class-File nicht zu löschen. Sich diese Datei einmal genauer anzusehen, ist sicher lohnenswert. Somit werden im Verzeichnis, in dem sich das Projekt befindet folgende zwei Schritte ausgeführt: $ javac MultiplyImpl.java $ rmic -v1.2 -keep MultiplyImpl 3.4 Implementierung von Server und Client Im letzten Schritt hin zur fertigen Anwendung müssen nun noch ein Server- und ein ClientProgramm erstellt werden. Das Entscheidende beim Server ist, dass er den angebotenen Dienst bei einem Namensservice unter einem bestimmten Namen anmeldet, damit dieser von einem Client gefunden werden kann. Die Anmeldung wird durch die Methode Naming.rebind() oder Naming.bind() erzielt. Der Unterschied der beiden Methoden liegt darin, dass bind() im Falle eines unter dem gewählten Namen bereits vorhandenen Eintrag eine AlreadyBoundException wirft. Will man an dieser Stelle eine Exception vermeiden, sollte rebind() verwendet werden, was allerdings dazu führen kann, dass ein vorhandener Eintrag überschrieben wird. -9- Stand: 09.2005 //Server.java import java.rmi.*; import java.rmi.server.*; public class Server{ public static void main(String args[]) throws Exception{ Naming.rebind("//localhost/multiply", new MultiplyImpl()); System.out.println("MultiplyImpl wurde eingetragen."); } } Soll das Objekt später wieder abgemeldet werden, so ist dies mit der Methode Naming.unbind(String name) möglich. Der Client ist ähnlich einfach gehalten wie der Server. Das Einzige, was an ihm vermuten lässt, dass es sich bei dem Objekt Multiply nicht um ein lokales Objekt handelt ist, dass es initial mit Hilfe des Namendienstes gesucht wird. Dazu dient die Methode Naming.lookup(String name), deren Parameter im URL-Format die Lokalisierung des Objektes ermöglicht. //Client.java import java.rmi.*; public class Client{ public static void main(String args[]) throws Exception{ Multiply m=(Multiply)Naming.lookup("//localhost/multiply"); int result = m.multiply(5,2); System.out.println(result); } } Alternativ zum Aufruf von Naming.lookup() existiert die Möglichkeit, sich mittels LocateRegistry.getRegistry(String servername) zuerst einen Verweis auf den Namensdienst eines Servers zurückgeben zu lassen und darauf die lookup()-Methode aufzurufen. Der Rückgabewert der lookup()-Methode ist vom Typ Remote. Daher muss die Rückgabe noch auf den Typ Multiply gecastet werden. Wie man hier sieht verwendet man ein entferntes Objekt tatsächlich wie ein Lokales. Das verdeutlicht, dass das Ziel, die Integration eines entfernten Aufrufs in die Sprache Java auf eine sehr transparente Weise gelungen ist. - 10 - Stand: 09.2005 3.5 Ausführen der Anwendung Nachdem Server und Client übersetzt wurden, soll nun die Anwendung getestet werden. Dazu muss ein Namensdienst gestartet werden, bei dem der Server das erzeugte Objekt anmelden kann. Ein sehr schlichter, aber für dieses Beispiel ausreichender Namensdienst ist der im SDK enthaltene rmiregistry. Dieser läuft standardmäßig auf Port 1099, kann aber durch Angabe eines Ports als Argument selbstverständlich auch auf anderen Ports laufen. Der Dienst kann entweder aus dem Serverprogramm heraus mit Hilfe spezieller Methoden gestartet werden oder aber manuell von der Kommandozeile aus. $ start rmiregistry $ rmiregistry & // starten unter Windows // starten unter Unix-Systemen Sobald der Dienst gestartet ist kann das Programm ausgeführt werden. $ java Server MultiplyImpl wurde eingetragen $ java Client 10 Wie man sieht, hat das Programm seine Aufgabe erfüllt und das richtige Ergebnis zurück geliefert. Soll das Beispiel auf zwei über ein Netzwerk verbundene Rechner getestet werden, so ist anstelle des String „localhost“ in der Methode lookup() die IP-Adresse des entfernten Rechners zu spezifizieren. - 11 - Stand: 09.2005 4 Der Versuch Ausgehend von den bis hierhin geschaffenen Grundlagen soll nun der Versuch im Detail dargestellt werden. Es handelt sich bei dem Szenario um das bereits bekannte verteilte Flugbuchungssystem, bestehend aus einer Client- und einer Server-Anwendung. Der Server verwaltet die vorhandenen Sitzplätze verschiedener Flüge, die von den Clients gebucht, storniert oder angezeigt werden können. Während seiner Initialisierung legt der Server dazu 20 Flüge (Flugnummer 1-20) an, in denen jeweils 300 Plätze (Platznummer 1300) verfügbar sind. Die Plätze 1-149 sind dabei Raucherplätze, der Rest NichtRaucherplätze. Bei der Reservierung hat ein Client die Möglichkeit, sich entweder einen Platz frei auszusuchen und zu reservieren, wobei hier die Angaben in Bezug auf die zusätzlichen Wünsche (Smoker, Window) nicht berücksichtigt werden oder nach seinen Zusatzanforderungen (Smoker, Window) zuweisen zu lassen. Für die automatische Zuweisung wird für den gewünschten Flug als Platznummer einfach 0 angegeben. Beim Stornieren eines Platzes muss der Name, auf den der Flug gebucht wurde mit dem bei der Stornierung angegeben Namen übereinstimmen. Werden Client und Server auf unterschiedlichen Rechner ausgeführt, so muss beim Aufruf des Clients die IP-Adresse des Servers als Parameter angegeben werden. 4.1 Versuchsbeschreibung Nach dem Entpacken der Quellcodedateien befinden sich folgende Dateien im Zielverzeichnis: - - Client-Programm: ClientApplication.java Server-Programm: ServerApplication.java Interface zur Definition der entfernten Methoden: Platzbuchung.java die Klasse, die sich um die notwendige Vorarbeit des Clients für Java-RMI kümmert und die entfernten Methoden aufruft: Platzbuchung_client.java die Klasse, die sich um die notwendige Vorarbeit des Servers für Java-RMI kümmert und die entfernten Methoden zur Verfügung stellt (Implementierung des Interfaces): Platzbuchung_server.java Klassen für den Nachrichtenaustausch, die über das Netzwerk übertragen werden müssen: Request.java und Occupation.java zusätzliche Klassen zur Modellierung der Datenbank: Flight.java und SeatAttr.java Sowohl am Client-Programm, als auch am Server-Programm sind keine Änderungen erforderlich. Beide Programme stellen eine grafische Oberfläche bereit, über die der Nutzer die zur Verfügung stehenden Funktionalitäten verwenden kann und seinen Eingaben entsprechende Rückmeldungen bekommt. Ebenso sollen keine Veränderungen an den Klassen für die Datenbank (Flight.java und SeatAttr.java) durchgeführt werden. - 12 - Stand: 09.2005 Folgende Erweiterungen/Implementierungen sind durchzuführen: I. Das Interface Platzbuchung.java ist um die Beschreibung der Methoden book, storn und bookedSeats zu erweitern. Ihre Rückgabewerte und die notwendigen Parameter können in der (noch unvollständigen) Datei Platzbuchung_server.java nachgeschaut werden. Wichtig ist, dass die drei Methoden die entsprechende Exception werfen können. II. Da Objekte übergeben werden, muss ein RMISecurityManager installiert werden. Er ist in der Methode initSever der Klasse Platzbuchung_server zu initialisieren. Seit Java 2 erwartet der RMISecurityManager die Angabe von Sicherheitsrichtlinien. Diese können entweder systemweit angegeben werden oder aber in einer Datei spezifiziert werden, die in z.B. das Verzeichnis abgelegt wird, in das der Versuch entpackt wurde (dieses soll im Versuch geschehen). Eine solche Datei kann folgendermaßen aussehen: // policy.txt grant { permission java.security.AllPermission; }; Wie man anhand der Rechtevergabe vermuten kann, werden hier volle Rechte an Jedermann vergeben. In einem Produktivsystem sollten natürlich engere Restriktionen eingeführt werden. Für den Versuch ist die angegeben Richtlinie allerdings ausreichend. Zur Vertiefung der Sicherheitsmechanismen des SDK ab Version 1.2 und den PolicyDateien finden sich unter [4] und [5] weiterführende Informationen. Des Weiteren soll in der Methode initServer der Klasse Platzbuchung_server die Registrierung des Objektes (this) beim lokalen Namensdienst durchgeführt werden. Der Registry-Dienst wird bereits vom Server gestartet und muss somit nicht manuell gestartet werden. III. Die Klasse Platzbuchung_client ist um die Ermittlung des Registry-Dienstes zu erweitern. Nachdem der Namensdienst ermittelt wurde, soll mit seiner Hilfe das entfernte Objekt referenziert und in der Variable Server gespeichert werden. Zudem sind in der Klasse die drei Methoden book, storn und bookedSeats zu implemetieren. Dazu wird durch sie jeweils die Arbeit an die entsprechende Methode des entfernten Objektes delegiert. Danach soll mit Hilfe des Aufrufs von Application.listAnswer das Answer-Objekt (die Rückgabe der entfernten Methoden) ausgegeben werden. IV. Das Answer-Objekt ist zu implementieren. Das Answer-Objekt benötigt folgende öffentliche Attribute: FlightNr (int), SeatNr (int), ErrorMsg (String), BookedSeats (Vector), AnswerType (char). Des Weiteren werden vier statische, öffentliche Attribute benötigt, die den angegebenen festen Wert besitzen: - AnswOcc = 'G' (char) - AnswOk = 'O' (char) - 13 - Stand: 09.2005 - AnswWarn = 'W' (char) - AnswErr = 'E' (char) Der Konstruktor der Klasse belegt die nicht statischen Attribute der Klasse und besitzt folgende Parameterliste: Answer(char answerType, int flightNr, int seatNr, String errorMsg, Vector bookedSeats). Diese Klasse soll als Parameter übergeben werden können. V. Die notwendigen Ergänzungen in den Klassen Request und Occupation sind durchzuführen, damit diese als Parameter übergeben werden können. VI. Die Stub-Klasse muss erzeugt werden. Wie bereits zuvor gesehen steht dazu das Programm rmic zur Verfügung. Beim Aufruf von rmic sollte an die Option –v1.2 gedacht werden. 4.2 Starten des Versuchs Nachdem alle Dateien vervollständigt und übersetzt ($javac *.java) und die Stub-Klasse generiert wurde ($rmic Flugbuchung_server), können Server und Client(s) gestartet werden. Beim Server ist zu beachten, dass die angelegte Policy-Datei als Argument angegeben wird. Durch den Aufruf $java -Djava.security.policy=./policy.txt ServerApplication wird das Server-Programm gestartet. Nun kann der/die Clients aufgerufen werden: java ClientApplication 4.3 Sicherung der Versuchsergebnisse Nach der vollständigen Bearbeitung des Versuchs und dem Test der richtigen Ergebnisse sind folgende Dinge zu tun: 1. Anlegen des Ordners „LösungenRMI“ im Unterverzeichnis „Lösungen“ des eigenen Verzeichnisses auf dem Praktikumsrechner 2. Speicherung der Dateien in diesem Verzeichnis 3. Mail mit dem Betreff „Versuch-RMI“ an [email protected] unter Angabe des Namens, Vornamens, der Matrikelnummer und des Logins verschicken 4. Kopie der Versuchsergebnisse bis zur Ausgabe der Scheine sichern - 14 - Stand: 09.2005 5 Literatur [1] ein weiteres einführendes RMI-Programmierbeispiel: http://java.sun.com/j2se/1.4.2/docs/guide/rmi/getstart.doc.html [2] Java-RMI-Spezifikation: http://java.sun.com/j2se/1.4.2/docs/guide/rmi/spec/rmiTOC.html [3] umfassende Einführung: http://java.sun.com/developer/onlineTraining/rmi/RMI.html [4] Erläuterungen zum SecurityManager und zu Policy-Dateien http://java.sun.com/j2se/1.3/docs/guide/security/permissions.html [5] Erläuterungen zum SecurityManager und zu Policy-Dateien http://java.sun.com/j2se/1.3/docs/guide/security/PolicyFiles.html allgemeiner Überblick über entfernte Prozedur-/Methodenaufrufe: Folien der Lehrveranstaltung „Distributed Systems“ zum Thema RPC und RPCErweiterungen, zu finden auf den Seiten des Lehrstuhls - 15 -