CORBA-/RMI- und SOAP-Interfaces zu WebCal Julius Benkert April 2005 Inhaltsverzeichnis 1 Einleitung 1.1 Voraussetzungen 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Überlegungen zum Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 Kurzreferenz 2 2.1 Start der Server und Clients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2.2 Veränderungen der Socket-Schnittstelle abbilden . . . . . . . . . . . . . . . . . . 3 3 Session-Management 3 3.1 Die WebCalSession-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 3.2 Die WebCalSessionHandler-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 Der Socket-Client 4 5 Die RMI-, CORBA- und SOAP-RPC-Schnittstellen 5 5.1 Kommunikation über RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 5.2 Kommunikation über CORBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 5.3 Kommunikation über SOAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1 1 Einleitung Die vorliegende Dokumentation beschreibt Design, Implementierung und Verwendung der CORBA-, RMI- und SOAP-Schnittstellen für den WebCal-Dienst. 1.1 Voraussetzungen Alle Schnittstellen wurden auf Basis des J2SE SDK in der Version 1.5.0 03 entwickelt und getestet. Dieser kann bei Sun microsystems unter http://java.sun.com/ heruntergeladen werden. Die SOAP-Schnittstelle erfordert das Vorhandensein bzw. die Installation weiterer Software, eine Auflistung und Installationsanweisungen finden sich zu Beginn des entsprechenden Kapitels. Der Java-CLASSPATH muss um das Implementierungsverzeichnis erweitert werden. 1.2 Überlegungen zum Design Um die Migration zwischen verschiedenen Interfaces sowie die Kommunikation zwischen Client und Server so einfach wie möglich zu halten, wurde versucht, soweit wie möglich Unterschiede beim Methodenaufruf und der Fehlerbehandlung zu vermeiden. Der Methodenaufruf sowie die Rückgabewerte sind bei allen Schnittstellen identisch, lediglich schnittstellenspezifische Fehler müssen bei einem Wechsel der Schnittstelle zusätzlich abgefangen werden. Da derzeit von der Socket-Schnittstelle im Fehlerfall ausschließlich Textmeldungen zurückgegeben werden, findet keine Unterscheidung von Fehlertypen statt. Alle Fehler der Socket-Schnittstelle werden auf eine WebCalException aus dem Paket de.lmu.ifi.WebCal mit der vom Socket-Server gelieferten Fehlermeldung abgebildet. Eine Ausnahme hiervon ist wegen einer notwendigen Vererbung bei der CORBA-Schnittstelle notwendig — jeder WebCal-Fehler führt hier zu einer WebCalException aus dem Paket de.lmu.ifi.WebCal.CORBA. 2 Kurzreferenz Der folgende Abschnitt behandelt kurz die notwendigen Schritte für die Verwendung bzw. Anpassung der Schnittstellen. 2.1 Start der Server und Clients Um die Kommunikation über die verschiedenen Schnittstellen zu testen, stehen im Hauptordner die Dateien ”startServers” und ”runClients” zur Verfügung. Beide Skripte sind für die Verwendung unter Linux in der Shell bash vorgesehen. Im Ordner Software steht für 586-kompatible Computer die in den Voraussetzungen genannte Software zur Verfügung. Befinden sich die 2 notwendigen Pakete in andere Ordnern, müssen die jeweiligen Pfade im Skript ”setPaths” angepasst werden. Der zu verwendende Socket-Server wird ebenfalls in diesem Skript spezifiziert. Das Skript ”setPaths” wird in den oben genannten Skripten importiert. Der Start der Serverprozesse über das Skript ”startServers” erfolgt ohne Parameter. Beim Start der TestClients über das Skript ’runClients’ kann ein Server-Host angegeben werden, wird die Parameter nicht übergeben, werden die Anfragen an den localhost gestellt. 2.2 Veränderungen der Socket-Schnittstelle abbilden Diese Kurzreferenz behandelt nicht alle Einzelheiten der Schnittstellen und sollte nur nach der Lektüre der ausführlichen Beschreibung verwendet werden. Um die RMI-, CORBA- und SOAP-Schnittstellen an eine geänderte Socket-Schnittstelle anzupassen, müssen einige Dateien im Implementierungs-Ordner verändert bzw. neu generiert werden: 1. Datei de/lmu/ifi/WebCal/Socket/WebCalClient.java: Der Client für die Socket-Kommunikation dient als Grundlage aller anderen Schnittstellen und sollte daher als erstes angepasst werden. Die SOAP-Schnittstelle basiert auf dieser Klasse, die Serverseite wird daher automatisch mitgeändert. 2. Datei de/lmu/ifi/WebCal/RMI/WebCalClient.java: Java-Schnittstelle für die RMI-Kommunikation. 3. Datei WebCal.idl: IDL-Definition der CORBA-Schnittstelle. Anschließend müssen die Java-Bindungen neu generiert werden mit ”idlj -fallTie WebCal.idl”. 4. Datei de/lmu/ifi/WebCal/CORBA/WebCalImpl.java: Klasse zur Abbildung der allgemeinen WebCalException auf die CORBA-spezifische. 5. Kompilieren der geänderten Dateien: Bitte nicht vergessen, die geänderten Java-Klassen neu zu übersetzen. 6. Neustart aller Dienste: Die Änderungen werden erst nach einem Neustart der Dienste wirksam. 7. Java-Bindungen für SOAP-Clients: Mit dem Befehl ”java org.apache.axis.wsdl.WSDL2Java -p de.lmu.ifi.WebCal.SOAP url ” werden die SOAP-Java-Bindungen neu generiert. 3 Session-Management Im Laufe der Realisation hat sich gezeigt, dass durch die unterschiedliche Fähigkeiten und die unterschiedliche Transparenz der jeweils verwendeten Basissoftware keine zuverlässige, produkt3 unabhängige Isolation der verschiedenen Sessions realisierbar ist. Um eine Bindung an je ein bestimmtes Produkt zu vermeiden, muss deshalb von allen Clients explizit eine Session gestartet werden. Die von der dafür vorgesehenen Funktion startSession zurückgegebene id muss bei allen folgenden Anfragen als erster Parameter angegeben werden und bewirkt durch die Zuordnung zu einem eigenen Socket eine garantierte, vollständige Trennung der Sessions. 3.1 Die WebCalSession-Klasse Die Klasse de.lmu.ifi.WebCal.WebCalSession besteht hauptsächlich aus Instanzvariablen und den dazugehörigen Getter- und Setter-Funktionen. Eine Ausnahme hiervon ist die Variable lastUsage, mit deren Hilfe die Methode hasExpired(int timeoutSeconds) bestimmt, ob die aktuelle Session abgelaufen ist. Häufig verwendete Sicherheitschecks können ebenfalls in die WebCalSession-Klasse integriert werden. Als Beispiel hierfür dient die Methode isHostValid(String host), die prüft, ob der die Session initiierende Host bekannt ist und in diesem Falle gleich dem im Parameter host angegebenen ist. Zur empfohlenen Integration mehr bei der WebCalSessionHandler-Klasse. 3.2 Die WebCalSessionHandler-Klasse Die Grundfunktionen zum Sessionmanagement sind zusammengefasst in der Klasse de.lmu.ifi. .WebCal.WebCalSessionHandler. Die Methoden createSession, getSession und destroySession regeln die Erstellung, den Zugriff sowie das Beenden einer Session. Um abgelaufene, nicht regulär beendete Sessions zu terminieren, wird beim Aufruf der Methode createSession die Methode purgeExpiredSessions aufgerufen. Beim Zugriff auf eine Session mithilfe der Funktion getSession und der sessionId wird zunächst die Funktion checkSession aufgerufen. Über den boolschen Rückgabewert kann die Zugriffsberechtigung des Clients überprüft werden. Da abhängig von der verwendeten Kommunikationsform Informationen über den Client in unterschiedlichem Umfang und über verschiedene Zugriffsmethoden zu erhalten sind, kann die jeweilige Prüfung abhängig von diesen Gegebenheiten für jede Kommunikationsform separat implementiert werden. Ist die Prüfung erfolgreich, wird der Zeitstempel der letzten Benutzung der Session aktualisiert und die Session zurückgegeben. Eine Beispielimplementierung einer erweiterten checkSession-Funktion findet sich in der Klasse de.lmu.ifi.WebCal.RMI.WebCalServer. Für die RMI-Schnittstelle wird hier mittels der Funktion isHostValid überprüft, ob der aktuell anfragende Host identisch ist mit jenem, der die Session gestartet hat. 4 Der Socket-Client Die eigentliche Client-Funktionalität für die Socket-Kommunikation ist in der Klasse de.lmu. .ifi.WebCal.Socket.WebCalClient enthalten. Diese Klasse erweitert die Klasse WebCalSessionHandler. Die Funktionen startSession und endSession rufen die Sessionhandling-Funktionen des 4 WebCalSessionHandlers auf, erweitert diese jedoch um den Auf- bzw. Abbau der Socketverbindung. Beim Aufbau des Sockets wird zunächst die Begrüßungszeile des Servers von der Funktion parseGreeting ausgewertet, anschließend stehen die Versionsinformationen des Servers über die Methoden getWebCalVersion und getProtocolVersion zur Verfügung. Der zu verwendende Host mit dem zugehörigen Port kann entweder direkt über den Konstruktor mit zwei Parametern angegeben werden oder durch Setzen der System-Eigenschaften de.lmu.ifi.WebCal.Socket.host und de.lmu.ifi.WebCal.Socket.port. Sämtliche Anfragen, die nach dem Frage-Antwort-Schema ablaufen, werden mithilfe der Methode execRequest ausgeführt. Diese Methode wirft bei auftretenden Fehlern in der Kommunikation oder fehlerhaften Anfragen — erkennbar an einer Antwort, die mit der Zeichenkette ERROR beginnt — die bereits bekannte WebCalException. Die öffentlichen Funktionen müssen bei Verwendung dieser Methode nur noch den Anfrage-String erstellen und den Antwort-String parsen. Ein Unterschied gegenüber dem Protokoll der Socket-Kommunikation ergibt sich bei überladenen Funktionen wie z.B. der WebCal-Funktion shift. Diese kann in ihrer einfachsten Form ein Intervall um eine gegebene Zeitspanne verschieben, über ein oder zwei optionale Parameter kann aber auch eine Rundung des Ergebnisses vorgenommen werden. Um Probleme bzw. kryptische, schnittstellenabhängige Funktionsnamen in Client-Programmiersprachen ohne die Fähigkeit des Overloadings zu vermeiden, wurde im Socket-Client bei öffentlichen Funktionen auf Überladen verzichtet und es wurden eindeutige Funktionsnamen vergeben. Im angegebenen Beispiel lauten diese shift, shiftAndAdjust sowie shiftAndAdjustWithType. Als Beispiel für die Verwendung dieser Client-Klasse dient die Klasse TestSocket. Beim Aufruf von TestSocket mit den Parametern Host und Port des WebCal-Servers werden einige Anfragen an der Server gestellt und die resultierenden Intervalle ausgegeben. Das Kommando hierzu lautet: java TestSocket host port 5 Die RMI-, CORBA- und SOAP-RPC-Schnittstellen Alle drei Schnittstellen benutzen zur Kommunikation mit dem WebCal-Server direkt den eben beschriebenen Socket-Client. Jede Schnittstelle ist als Activator realisiert, d.h. die ServerProzesse werden bei Client-Anfragen gestartet bzw. wiederverwendet. Im Falle von CORBA ist die gleiche Funktionalität automatisch durch das Threadmanagement des ORBs gegeben. Dieses Design gewährleistet, dass der Dienst selbst bei mehreren simultanen Zugriffen über die gleiche Schnittstelle verfügbar bleibt. Wie bereits beim Sessionmanagement beschrieben findet dabei keine eindeutige Zuordnung eines Server-Objekts zu einer Session statt, vielmehr kann ein Server-Objekt abwechselnd mehrere Sessions bedienen und existiert nach deren Ende weiter. 5 5.1 Kommunikation über RMI Für die Kommunikation über RMI wird zunächst das Interface de.lmu.ifi.WebCal.RMI. .WebCalClient benötigt. Zu beachten ist, dass dieses Interface java.rmi.Remote erweitern muss und alle Funktionen zusätzlich die Ausnahme RemoteException deklarieren müssen. Anmerkung: Wegen eines relativen Pfades in einer Policy-Datei (mehr dazu gleich) müssen die folgenden Befehle im Arbeitsverzeichnis Implementierung gestartet werden. Die Zuordnung der Dienstnamen zu den Stubs (Server-Objekten) wird von der rmiregistry übernommen. Diese wird gestartet mit: rmiregistry & Für das verwendete Activator-Prinzip muss vorab der RMI-Dämon rmid gestartet werden. Dieser benötigt eine sogenannte Policy-Datei, die angibt, welche Berechtigungen und Voreinstellungen die vom RMI-Dämon verwendete Virtual-Machine und damit auch die gestarteten Objekte besitzen sollen. Eine entsprechende Datei namens rmid.policy befindet sich im Ordner Policies. Die eigentlichen Server-Prozesse werden erst bei Client-Anfragen vom rmid generiert. Der Befehl zum Start des rmid lautet: rmid -J-Djava.security.policy=../Policies/rmid.policy & Im nächsten Schritt muss der RMI-WebCal-Service bei der rmiregistry angemeldet werden und der rmid so konfiguriert werden, dass er beim Aufruf des Dienstes ein Server-Objekt generieren kann. Dies geschieht in der Klasse de.lmu.ifi.WebCal.RMI.WebCalActivator. Beim Aufruf der Klasse muss die Systemeigenschaft de.lmu.ifi.WebCal.codeBase auf den Implementierungsordner als Basis für die Activation-Group gesetzt werden. Host und Port des Socket-Dienstes werden als Parameter übergeben. Der Befehl lautet also wie folgt: java -Dde.lmu.ifi.WebCal.codeBase=file:/pfadZurImplementierung / de.lmu.ifi.WebCal.RMI.WebCalActivator host port Im main-Teil des WebCalActivators werden zunächst die Properties gesetzt, mit denen anschließend die Activation-Group erstellt wird. Nachdem die Gruppe registriert wurde und die aufzurufende Klasse spezifiziert ist, wird nun die ActivationDesc unter dem Namen ”WebCal” in der rmiregistry eingetragen. Ab diese Zeitpunkt wird bei einer Anfrage für den WebCalDienst an die rmiregistry vom rmid ein neuer Stub generiert oder ein bereits vorhandener wiederverwendet und dem Client zugewiesen. Die Klasse TestRMI dient als Beispiel eines Clients und führt die gleichen Befehle aus wie die TestSocket-Klasse, diesmal allerdings über RMI. Über die optionalen Parameter host bzw. host und port kann die Adresse der zu verwendenden rmiregistry angegeben werden. Ohne 6 Parameter versucht TestRMI, die rmiregistry auf dem lokalen Rechner zu kontaktieren. Der Aufruf erfolgt mit: java TestRMI host port 5.2 Kommunikation über CORBA Anders als bei RMI dient für die Kommunikation über CORBA kein Java-Interface sondern eine Datei in der Interface-Definition-Language (IDL) als Dienstbeschreibung. Diese befindet sich im Implementierungs-Verzeichnis unter dem Namen WebCal.idl. Zusammengefasst im Modul de. .lmu.ifi.WebCal.CORBA wird die WebCalException sowie das Interface WebCalClient mithilfe der IDL-Datentypen deklariert. Um keine unnötigen Einschränkungen beim Design hervorzurufen wird das Tie-Prinzip verwendet, d.h. die das IDL-Interface implementierende Klasse muss keine bestimmte Klasse erweitern. Der IDL-to-Java Compiler generiert mit folgendem Befehl die für Client- und Server-Implementierungen nötigen Java-Bindungen: idlj -fallTie WebCal.idl Bei der Generierung der Java-Bindungen stößt man auf das Problem, dass CORBA-Ausnahmen die Klasse org.omg.CORBA.UserException erweitern müssen, die WebCalException des SocketClients allerdings ist hierfür nicht ausgelegt. Dieses Problem kann gelöst werden, indem entweder die allgemeine WebCalException die CORBA-UserException erweitert oder die WebCalException in einer Zwischenklasse abgefangen wird und in eine CORBA-spezifische Ausnahme umgewandelt wird. Die vorliegende Implementierung geht letzteren Weg um die anderen Schnittstellen frei von CORBA-Klassen zu halten. Diese Zwischenklasse namens WebCalImpl befindet sich im Paket de.lmu.ifi.WebCal.CORBA. Bei einer Veränderung des Interfaces muss diese daher zusätzlich zur IDL-Datei angepasst werden. Analog zum rmid bei RMI muss zunächst ein Dämon gestartet werden, der die Client-Requests an einen entsprechenden Stub delegiert. Bei CORBA übernimmt der orbd diese Funktion. Standardmässig belegt dieser den Port 900, um Root-Rechte unnötig zu machen wird im Folgenden allerdings abweichend Port 1060 verwendet. Der Befehl zum Start lautet: orbd -ORBInitialPort 1060 & Die Registrierung des Stubs beim orbd übernimmt die Klasse de.lmu.ifi.WebCal. .CORBA.WebCalServer. Der Host und Port des socketbasierten WebCal-Servers muss über die Systemeigenschaften de.lmu.ifi.WebCal.Socket.host und de.lmu.ifi.WebCal.Socket.port gesetzt werden. Nach der Aktivierung des ORBs und des RootPOA (sozusagen der Wurzel aller PortableObjectAdapter) wird ein neues WebCalImpl-Objekt erzeugt. Mit dem Eingangs beschriebenen Tie-Mechanismus wird nun ein Objekt der automatisch generierten Klasse Web7 CalClientPOATie erzeugt, dessen Servant-Referenz anschließend im NamingService unter dem Namen WebCal registriert wird. Der Aufruf erfolgt mit: java -Dde.lmu.ifi.WebCal.Socket.host=host -Dde.lmu.ifi.WebCal.Socket.port=port de.lmu.ifi.WebCal.CORBA.WebCalServer -ORBInitialPort 1060 & Der orbd leitet nun passende Anfragen von Clients an der WebCalServer weiter und übernimmt auch das Threadmanagement, eine explizite Behandlung konkurrierender Zugriffe ist deshalb nicht nötig. Alternativ zum gerade beschriebenen Vorgehen wäre auch die Verwendung von Suns RMI over IIOP möglich, Tests hiermit haben aber gezeigt, dass die resultierende IDL-Beschreibung der Schnittstelle unnötig kompliziert ist und alle Java-spezifischen Vererbungen eins zu eins abbildet. Um eine gute Portierbarkeit auf andere Programmiersprachen zu gewährleisten wurde deshalb der eben beschriebene Weg gewählt. Für diese Schnittstelle gibt es wiederum einen Beispielclient. Über die Optionen ORBInitialHost und -ORBInitialPort können Host und Port des ORBs gesetzt werden: java TestCORBA -ORBInitialHost host -ORBInitialPort port 5.3 Kommunikation über SOAP Als Grundlage für die Implementierung der SOAP-Schnittstelle dient serverseitig Axis für Java, derzeit in der Version 1.2. Download sowie Dokumentation hierzu findet sich auf den Seiten des Apache-Projekts unter http://ws.apache.org/axis/. Für den produktiven Einsatz sollte Axis in einem Servlet-Container wie z.B. dem Apache Jakarta Tomcat (http://jakarta.apache.org/tomcat/) installiert werden, genaue Installationsanweisungen für die jeweilige Systemumgebung sind ebenfalls unter den eben genannten URLs verfügbar. Alternativ ist für den Entwicklungseinsatz ein einfacher, undokumentierter HTTP-Server in Axis integriert, der aufgrund seiner einfachen Verwendbarkeit im Folgenden eingesetzt wird. Für den Betrieb von Axis sind weitere Komponenten notwendig, derzeit sind dies ein JAXP1.1-konformer XML-Parser (z.B. Xerces für Java, http://xml.apache.org/xerces-j/) und der bei Sun entwickelte Java Web Services Developer Pack 1.5 (JWSDP, http://java.sun.com/webservices/jwsdp/). Bei der Verwendung von Tomcat als Application-Container muss außerdem das Java Activation Framework (JAF, momentan Version 1.0.2, http://java.sun.com/products/javabeans/glasgow/jaf.html ) verfügbar sein. Das Verzeichnis ”Axis” ist bereits für den Start des Axis-Servers vorbereitet. Die Erweiterung der Datei mit dem Java-Quellcode muss für eine Benutzung mit Axis ”jws” statt ”java” lauten. Die in der Datei WebCalClient.jws definierte Klasse WebCalClient erweitert die WebCalClientKlasse für die Socket-Kommunikation, der Konstruktor mit Host- und Port-Angabe wird sicherheitshalber deaktiviert. Für die Ausführung der jws-Dateien notwendige Klassen werden 8 von Axis automatisch im Ordner jwsClasses gesucht. Da sich das Implementierungsverzeichnis wie zu Beginn beschrieben bereits im CLASSPATH befinden sollte, stehen aber bereits alle notwendigen Klassen zur Verfügung. Zusätzlich müssen allerdings noch einige Shell-Variablen gesetzt und Pakete zum CLASSPATH hinzugefügt werden: export AXIS HOME=pfadZuAxis export JWSDP HOME=pfadZuJwsdp for packagePath in $AXIS HOME/lib/*.jar $JWSDP HOME/jwsdp-shared/lib/activation.jar $JWSDP HOME/jwsdp-shared/lib/mail.jar; do export CLASSPATH=$CLASSPATH:$packagePath done Anschließend kann nun im Arbeitsverzeichnis ”Axis” der SimpleAxisServer (im folgenden Beispiel auf Port 8080) gestartet werden mit: java org.apache.axis.transport.http.SimpleAxisServer -p 8080 Zur Überprüfung des Dienstes bietet sich an, sich die WSDL-Definition des Socket-Clients durch den Aufruf der Adresse http://localhost:8080/WebCal.jws?wsdl im Browser anzeigen zu lassen. Um nun einen Java-Client für dieses Interface zu erstellen, kann man mit den Bordmitteln von Axis automatisch die nötigen Hilfsklassen erstellen. Im Ordner ”Implementierung” muss hierzu lediglich das Tool WSDL2Java aufgerufen werden, die Option -p legt dabei den Namen des Zielpakets fest: java org.apache.axis.wsdl.WSDL2Java -p de.lmu.ifi.WebCal.SOAP http://localhost:8080/WebCalClient.jws?wsdl Analog zu den anderen Schnittstellen ist die Klasse TestSOAP ein Beispiel für die Verwendung der generierten Klassen in einem Java-Client. Beim Aufruf muss als Parameter die URL des WebServices übergeben werden, für das bisherige Beispiel lautet der Befehl: java TestSOAP http://localhost:8080/WebCalClient.jws 9