Aus Linux-Magazin 06/2005

SOAP mit Axis

Remote Procedure Call (RPC) heißt die bewährte Technik, mit der Programme über Rechnergrenzen hinweg kommunizieren. Im WWW-Zeitalter erfüllen diesen Zweck meist die Webservices. Das Apache-Projekt Axis beherrscht das Standardprotokoll SOAP.

Ein Akronym als Programmname wählen liegt seit langem im Trend und so spielte dieses Motiv bei der Namensfindung von SOAP ([1] und [2]) wohl auch eine Rolle: Die Abkürzung steht für Simple Object Access Protocol. Mittlerweile gilt das XML-basierte Protokoll beim Einsatz von XML-RPC (XML Remote Procedure Call) als Standardlösung. Das Projekt Axis ermöglicht dem Webserver Apache die Verwendung von Webservices mit Hilfe von SOAP.

RPC, CORBA, RMI & Co.

Remote Procedure Call (RPC) funktioniert immer gleich: Programm A möchte eine Funktion (Methode, Unterprogramm) eines Programms B aufrufen. B hat einen eigenen Adressraum und befindet sich möglicherweise auf einem entfernten Rechner. Ein so genann- ter Stub simuliert für das Programm A die Funktion des entfernten Pro- gramms.

Der Stub übernimmt die Argumente, wandelt sie in ein Transportformat um – dieser Vorgang heißt Serialisierung – und schickt sie über eine Netzwerkverbindung ans Programm B.

Die Stubs haben also die Aufgabe, die Funktionsargumente korrekt zu serialisieren, eine Netzwerkverbindung zu öffnen und die Daten übers Netz zu verschicken. Dann empfangen sie das Ergebnis des Funktionsaufrufs und wandeln es wieder in einen Wert oder ein Objekt um (Deserialisierung). Der RPC-Programmierer muss sich also nicht um diese technischen Details kümmern, er braucht nur ein sprachenspezifisches Werkzeug, das den Stub generiert.

Die RPC-Implementationen unterscheiden sich durch die verwendeten Programmiersprachen und Transportprotokolle. CORBA[3] zum Beispiel unterstützt verschiedene Sprachen, benutzt aber ausschließlich ein Binärprotokoll. Unter Java findet häufig RMI[4] Verwendung; es kommuniziert zwar über Plattformgrenzen hinweg, ist jedoch stets auf genau diese Programmiersprache angewiesen.

SOAP geht einen Schritt weiter: Dank zahlreicher Stub-Generatoren ist es weitgehend sprachunabhängig. Zur Serialisierung verwendet SOAP XML, so ist es auch in puncto Protokoll nicht wählerisch. Typischerweise benutzt man es über HTTP, aber da es sich bei XML lediglich um Text handelt, sind auch SMTP, Messenger-Protokolle und andere Übertragungswege denkbar.

Bei der Performance bietet SOAP keine überragende Leistung, aber es zielt auch auf einen anderen Verwendungszweck als RMI oder CORBA. Als textbasiertes Übertragungsprotokoll hat es keine Probleme mit Firewalls oder Proxies und eignet sich deshalb dazu, beliebige Funktionen im Internet bereitzustellen, also Webservices.

SOAP-Protokoll und Webservices

SOAP wurde nicht speziell für RPC entworfen, Abbildung 1 zeigt den grundsätzlichen Aufbau einer SOAP-Meldung. Der SOAP-Envelope enthält optional SOAP-Header-Einträge und dazu zwingend einen SOAP-Body. Letzterer enthält die so genannte Nutzlast der Nachricht, also die eigentlichen Daten, in einem applikationsspezfischen Format. Die Header steuern die Verarbeitung und sind ebenfalls applikationsspezifisch, aber unabhängig von der Nutzlast. Listing 1 zeigt ein Beispiel.

Im eingangs beschriebenen Szenario – Programm A ruft eine Methode des Programms B auf – ist der Stub für die formale Implementation der Methode verantwortlich. Dazu definiert man zuerst die Methode, um aus dieser Definition den Stub zu generieren.

CORBA verwendet dazu eine eigene Sprache, die Interface Definition Language (IDL,[3]). Ein IDL-Compiler erzeugt den Stub, je nach Implementation in C, C++, Java, Python oder einer anderen Sprache. Das Java-basierte RMI braucht keine eigene Sprache zu diesem Zweck, hier reicht ein geeignetes Interface, das der RMI-Compiler anschließend übersetzt[4].

Webservices unterscheiden sich von klassischen Webformularen hauptsächlich durch die Möglichkeit, sie aus anderen Programmen direkt ansprechen zu können. Denn im Gegensatz zu den Formularen ist ein Webservice mit der Webservice Definition Language (WSDL,[5]) formal beschrieben.

Die WSDL entspricht der IDL von CORBA oder im Falle von RMI den Java-Interfaces, sie generiert die Client-Stubs. WSDLs schreibt man aber selten selbst, meist erledigt das ein Anwendungssystem, das auch die Webservices erstellt. Die Implementation der Service-Seite erfolgt dann mit den Mitteln des Anwendungssystems, die des Clients in einer beliebigen Programmiersprache.

Um einen Webservice zu beschreiben, verwendet die WSDL sechs Elemente: »Types«, »Messages«, »PortTypes«, »Bindings«, »Ports« und »Services«. Die »Types« beschreiben Datentypen, aus denen sich die »Messages« zusammensetzen, also die Basis der Kommunikation. Ein »PortType« gibt die abstrakte Beschreibung einer Operation wieder und verwendet Ein- und Ausgabe-Messages. Das »Binding« verknüpft diese Beschreibung mit einem Protokoll, meist SOAP – die W3C-Definition nennt alternativ HTTP GET/POST und MIME. Ein »Port« stellt die Zuordnung eines Bindings an eine Adresse (URI) dar, also die kleinste tatsächlich nutzbare Einheit. Mehrere Ports bilden zusammen einen »Service«, der allerdings lediglich aus einer logischen Klammer besteht.

Abbildung 1: Eine SOAP-Nachricht besteht aus einem optionalen Header und dem Body.

Abbildung 1: Eine SOAP-Nachricht besteht aus einem optionalen Header und dem Body.

Listing 1: Beispiel
SOAP-Nachricht

01 <?xml version='1.0' ?>
02 <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
03  <env:Header>
04   <m:reservation xmlns:m="http://travelcompany.example.org/reservation"
05           env:role="http://www.w3.org/2003/05/soap-envelope/role/next"
06            env:mustUnderstand="true">
07    <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference>
08    <m:dateAndTime>2001-11-29T13:20:00.000-05:00</m:dateAndTime>
09   </m:reservation>
10   <n:passenger xmlns:n="http://mycompany.example.com/employees"
11           env:role="http://www.w3.org/2003/05/soap-envelope/role/next"
12            env:mustUnderstand="true">
13    <n:name>ke Jógvan ¯yvind</n:name>
14   </n:passenger>
15  </env:Header>
16  <env:Body>
17   <p:itinerary
18     xmlns:p="http://travelcompany.example.org/reservation/travel">
19    <p:departure>
20      <p:departing>New York</p:departing>
21      <p:arriving>Los Angeles</p:arriving>
22      <p:departureDate>2001-12-14</p:departureDate>
23      <p:departureTime>late afternoon</p:departureTime>
24      <p:seatPreference>aisle</p:seatPreference>
25    </p:departure>
26    <p:return>
27      <p:departing>Los Angeles</p:departing>
28      <p:arriving>New York</p:arriving>
29      <p:departureDate>2001-12-20</p:departureDate>
30      <p:departureTime>mid-morning</p:departureTime>
31      <p:seatPreference/>
32    </p:return>
33   </p:itinerary>
34   <q:lodging
35    xmlns:q="http://travelcompany.example.org/reservation/hotels">
36    <q:preference>none</q:preference>
37   </q:lodging>
38  </env:Body>
39 </env:Envelope>

Schnellstart mit Axis

Das Projekt Axis[6] der Apache Foundation bietet ein Servlet, das definierte Webservices verwaltet. Außerdem enthält es einen Stub-Generator für die Client-Seite.

Zur Installation genügt es, das knapp 8 MByte große Tar-Archiv an beliebiger Stelle zu entpacken – bei Redaktionsschluss war Release Candidate 3 der Version 1.2 aktuell. Außerdem benötigt Axis die Datei »activation.jar« aus dem Java Activation Framework[7] und »mail .jar« aus dem Java-Mail-API[8]. Wer digitale Signaturen verwenden möchte, installiert zusätzlich »xmlsec.jar«[9]. Damit das Servlet und die Tools sie finden, kopiert man sie in die Unterverzeichnisse des Axis-Root »webapps/axis/ WEB-INF/lib« und »lib«.

Stellt der Servlet-Runner diese Bibliotheken bereits zur Verfügung, erübrigt sich das Kopieren ins Verzeichnis »WEB-INF/ lib«. Als Servlet-Container empfiehlt sich Tomcat wegen des reibungslosen Zusammenspiels. Hier erfolgt die Axis-Installation durch das Setzen eines symbolischen Links im Tomcat-Unterverzeichnis »webapps« auf das Axis-Unterverzeichnis »webapps/axis«. Je nach Tomcat-Konfiguration ist Axis nun sofort oder nach einem Neustart verfügbar. Abbildung 2 zeigt die Willkommensseite von Axis.

Ein Klick auf den Link »Validation« überprüft, ob alle benötigten Jar-Dateien existieren. Einen ersten Webservice zeigt der Endpunkt »getVersion« des Service »Version« unter »http://localhost:8800/axis/services/Version/getVersion«. Abbildung 4 zeigt den Aufbau der SOAP-Struktur. Probleme bei der Anzeige hat übrigens der Webbrowser Konqueror in der Version 3.3.

Abbildung 2: Axis verwaltet Webservices und bietet einen Stub-Generator für SOAP.

Abbildung 2: Axis verwaltet Webservices und bietet einen Stub-Generator für SOAP.

Abbildung 3: Eine Liste aller Webservices inklusive der zugehörigen WSDL-Dateien generiert Axis automatisch.

Abbildung 3: Eine Liste aller Webservices inklusive der zugehörigen WSDL-Dateien generiert Axis automatisch.

Konfiguration und Administration

Axis lässt sich über das Kommandozeilenwerkzeug Adminclient oder über Ant-Tasks verwalten. Aus Sicherheitsgründen deaktiviert die mitgelieferte Konfigurationsdatei aber das Administrations-Servlet. Es funktioniert dann erst nach dem Entfernen der Kommentarzeichen in den betreffenden Zeilen der Datei »webapps/axis/WEB-INF/web.xml« unterhalb des Axis-Wurzelverzeichnis- ses.

Die Administration besteht vor allem aus dem Einspielen (Deployment) und Löschen (Undeployment) von Webservices. Beides erfolgt über einen Webservice Deployment Descriptor (WSDD). Die XML-Dateien der Endung ».wsdd« enthalten alle notwendigen Informationen. Das Axis-Paket zeigt Beispiele, die die Administration demonstrieren und Vorlagen liefern.

Den ganzen Arbeitsablauf vom Erstellen eines Webservice über das Deployment und das Entwickeln der WSDL bis zur Clientprogrammierung zeigen die folgenden Abschnitte. Das Beispiel verwendet ein Programm aus dem Coffeeshop über Internationalisierung[10] in abgewandelter Form. Listing 2 beschreibt eine einfache Klasse mit zwei Methoden, die den Service implementieren.

Listing 2:
»I18nServiceImpl.java«

22 import java.util.*;
23 import java.text.*;
24
25 /**
26    This class implements a simple I18n-conversion webservice.
30 */
31
32 public class I18nServiceImpl {
33
36   /**
37      Get date with given country and language
38   */
39
40   public String getDate(String language, String country) {
41     Locale locale = new Locale(language,country);
42     Date now = new Date();
43     DateFormat df = DateFormat.
44       getDateInstance(DateFormat.DEFAULT,locale);
45     return df.format(now);
46   }
47
50   /**
51      Print a currency value.
52   */
53
54   public String getCurrency(double val, String language, String country) {
55     Locale locale = new Locale(language,country);
56     Double value = new Double(val);
57     NumberFormat cf = NumberFormat.
58       getCurrencyInstance(locale);
59     return cf.format(value);
60   }
61 }

Listing 3 zeigt den zugehörigen Deployment-Descriptor, Listing 4 den Undeployment-Descriptor. Die Axis-Dokumentation erläutert die einzelnen Tags, ihr Zweck ergibt sich aber auch aus dem Namen. Stünde in Zeile 29 von Listing 3 bei »value« als Wert »*«, wären alle Methoden als Endpoints sichtbar.

Das Shellskript in Listing 5 erleichtert die Administration, indem es den richtigen Classpath für »adminclient« setzt. Damit erfolgt das Deployment beziehungsweise Undeployment über diese Aufrufe von Adminclient:

> ./adminclient.sh deploy.wsdd
Processing file deploy.wsdd
<Admin>Done processing</Admin>
> ./adminclient.sh undeploy.wsdd
Processing file undeploy.wsdd
<Admin>Done processing</Admin>

Vor dem Undeployment soll Axis aber noch die WSDL-Datei generieren:

> wget -nd -O I18nService.wsdl http://localhost:8800/axis/services/I18n?wsdl

Der Link »Lists« auf der Axis-Einstiegsseite (siehe Abbildung 2) zeigt alle Services mit den zugehörigen Endpoints sowie jeweils einen Link zur entsprechenden WSDL-Datei (Abbildung 3). Die komplette WSDL-Datei liegt – wie die anderen Listings dieses Artikels auch – unter[11]. Über sie greift nun jede Sprache, Webservice-Unterstützung vorausgesetzt, auf die Klasse »I18nServiceImpl« und ihre Methoden zu.

Abbildung 4: SOAP-Nachrichten und Webservices liegen häufig im XML-Format vor.

Abbildung 4: SOAP-Nachrichten und Webservices liegen häufig im XML-Format vor.

Von der WSDL zum Client

Ein Client benötigt zur Verwendung der Webservices einen Stub. Für einen Java-Client generiert ihn das Axis-Tool »WSDL2Java«. Hier bietet sich analog zu Adminclient ein Wrapper-Skript an, das den Classpath setzt. Die Clientdateien erzeugt man mittels:

> wsdl2java.sh 
-Nhttp://localhost:8800/axis/services/
I18n=client I18nService.wsdl

Normalerweise erzeugt WSDL2Java die Java-Klassen in einem vom Target-Namespace abgeleiteten Package, im Beispiel wäre das »localhost.axis.services .I18n«. Der Parameter »-N« ändert dieses Verhalten und bildet den Namespace stattdessen auf das Package »client« ab. Die »-N«-Option darf mehrmals vorkommen und die Zuordnungen auch in eine Datei ausgelagert werden. Im Alltag würde sich jedoch ohnehin eher die Arbeit mit Ant und den von Axis bereitgestellten Ant-Tasks anbieten.

Das Beispiel generiert vier Dateien mit auf den ersten Blick verwirrender Namensgebung. Das Interface »I18NServiceImpl.java« definiert die Methoden der Ursprungsklasse »I18nServiceImpl«, die der Deployment-Descriptor freigibt. Zu beachten ist der kleine Unterschied im Namen (»I18N« und »I18n«).

Namenswahl

Die Klasse »I18NSoapBindingStub.java« implementiert das Interface bereits und erledigt die Kommunikation via SOAP. Nicht mit der konkreten Implementation, sondern mit Interfaces zu arbeiten beugt Fehlern vor. Deshalb kommen zwei weitere Elemente ins Spiel: die Factory-Klasse »I18NServiceImplServiceLocator.java« sowie das Factory-Interface »I18NServiceImplService.java«. Wer andere Dateinamen vorzieht, passt die WSDL-Datei entsprechend an. Um die Lauffähigkeit zu gewährleisten, müssen nur die Endpoints und die Datentypen gleich bleiben.

Listing 6 zeigt die wichtigsten Auszüge aus dem Code des Clients. Die Methode »getStub()« (Zeilen 61 bis 65) erzeugt die Factory und implementiert den Service über die Methode »getI18n(URL)« des Factory-Interface. Auf diese Weise können die Zeilen 48 bis 50 mit dem Stub wie mit lokalen Methoden arbeiten (siehe Abbildung 5).

Abbildung 5: Der Stub lässt einen Client via SOAP entfernte Methoden verwenden.

Abbildung 5: Der Stub lässt einen Client via SOAP entfernte Methoden verwenden.

Die Methode »getI18n(URL)« in Zeile 64 erwartet eine URL; es gibt aber auch ein Pendant ohne Argumente. Es nutzt den in der WSDL-Datei fest kodierten Endpunkt. Wer mit einem externen und unveränderbaren Webservice arbeitet, sollte diesen Weg wählen. Beim Entwickeln eines eigenen Systems bietet sich aber eine konfigurierbare URL an.

Mehr als RPC

Die Anzahl der durch WSDL2Java erzeugten Dateien beschränkt sich nicht auf vier wie in diesem Beispiel, es können hunderte sein. Kommen als Argumente und Rückgabewerte auch Klassen und nicht nur einfache Datentypen zum Einsatz, generiert WSDL2Java ebenfalls entsprechende Klassen. Benutzen diese wiederum komplexe Attribute, steigt die Gesamtzahl der Dateien schnell.

Aber auch unabhängig von diesem Aspekt variiert die Zahl der Klassen, da das gezeigte RPC nur einen von vier möglichen Service-Styles darstellt. Daneben gibt es Document-, Wrapped- und Message-Style-Services. Sie unterscheiden sich beim Umsetzen der WSDL-Definition und in der Art des Encoding der Daten. Der Message-Service zum Beispiel arbeitet mit rohem XML und bietet sich damit an, wenn Server oder Client die Daten nicht verarbeiten, sondern lediglich weiterreichen.

Listing 3:
»deploy.wsdd«

24 <deployment name="i18n" xmlns="http://xml.apache.org/axis/wsdd/"
25     xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
26
27   <service name="I18n" provider="java:RPC">
28     <parameter name="className" value="I18nServiceImpl"/>
29     <parameter name="allowedMethods" value="getDate getCurrency"/>
30   </service>
31 </deployment>

Listing 4:
»undeploy.wsdd«

24 <undeployment name="i18n" xmlns="http://xml.apache. org/axis/wsdd/"
25     xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
26
27   <service name="I18n"/>
28 </undeployment>

Sicher kommunizieren

Bei jedem Internetdienst spielt die Sicherheit eine Rolle. Die Kommunikation zwischen Client und Axis erfolgt über einen Web- oder einen Applikations-Server und lässt sich somit über HTTPS verschlüsseln – eine Java-Runtime-Umgebung ab Version 1.4 vorausgesetzt. HTTPS nimmt mit Zertifikaten sowie über Benutzername und Passwort auch eine Authentifizierung vor. Über den »MessageContext« verfügt der eigentliche Service ebenfalls über diese Möglichkeit und damit auch der Axis-Service. Eine weiter gehende Integration ins Tomcat-Konzept planen die Entwickler für die Zukunft.

Für das Verschlüsseln und Signieren auf XML-Ebene statt über HTTPS gibt es ebenfalls Vorschläge, aber bislang unterstützt Axis diese Methode nicht. XML-Signaturen ermöglicht aber das XML-Apache-Projekt. Webservices erlauben es allerdings, Firewalls einfach zu überwinden. Beinahe beliebige Anwendungs-protokolle laufen über den offenen HTTP-Port. Um das hierbei gegebene hohe Sicherheitsrisiko für den Dienst zu minimieren, ist ein sorgfältiges Security-Audit unerlässlich.

Listing 5:
»adminclient.sh«

01 #!/bin/bash
11 addjars() {
12   for d in $@; do
13     jars=`find $d -name "*.jar" -maxdepth 1`
14     for f in $jars; do
15       cp=$cp:$f
16     done
17   done
18   CPATH=$cp:$CPATH
19 }
20
21 AXIS_HOME=/usr/local/axis-1_2RC3
22 HOST="localhost"
23 PORT="8800"
24
25 addjars $AXIS_HOME/lib
26
27 java -cp $CPATH org.apache.axis.client.AdminClient -lhttp://$HOST:$PORT/axis/ services/AdminService "$@"

Listing 6:
»I18nClient.java«

22 package client;
23
24 import java.util.*;
25 import java.text.*;
26 import java.net.*;
27
35 public class I18nClient {
36
37   static final String PORT_ADDRESS =
38     "http://localhost:8800/axis/services/I18n";
39
40   public static void main(String[] args) {
41     if (args.length != 3) {
42       System.out.println("usage: java clientI18nClient amount language country");
43       System.exit(3);
44     }
45     try {
46       I18nClient client = new I18nClient();
47       I18NServiceImpl stub = client.getStub();
48       System.out.println("Date: " + stub.getDate(args[1],args[2]));
49       System.out.println("Amount: " +
50         stub.getCurrency(Double.parseDouble(args[0]),args[1],args[2]));
51     } catch (Exception e) {
52       e.printStackTrace();
53     }
54   }
55
61   private I18NServiceImpl getStub() throws Exception {
62     I18NServiceImplServiceLocator serviceLocator =
63       new I18NServiceImplServiceLocator();
64     return serviceLocator.getI18n(new URL(PORT_ADDRESS));
65   }
66 }

finally{}

Im Alltag braucht man häufig nur einen Client zu implementieren, der einen vorhandenen Webservice nutzt. Viele ERP-Systeme bieten inzwischen einen direkten Zugang über Webservices oder der Anwender spricht sie über den Webservice einer EAI-Middleware (Enterprise-Application-Integration) an.

Oft bildet der Webservice-Client einen Teil einer Webanwendung und läuft selbst als Servlet in einem Servlet-Container. In diesem Fall müssen die Axis-Runtime-Jar-Archive im Verzeichnis »WEB-INF/lib« liegen, aber hier lauert die Gefahr der Inkompatibilität mit der Laufzeitumgebung des Servlets.

Dieses Problem tritt beispielsweise beim Zusammenspiel mit Websphere von IBM auf. Als einer der größten Sponsoren des Apache-Projekts verwendet IBM zahlreiche Apache-Komponenten für seine Produkte. Jedoch ist beispielsweise die von Websphere verwendete Version des Apache-Logging-Framework nicht kompatibel zu Axis.

Zum besseren Verständnis der Axis-Architektur verhilft die Dokumentation[6]. Als nützlich erweist sich vielfach die Axis-Serverkomponente SOAP-Monitor, der die Kommunikation zur genaueren Analyse mitschneidet. Ebenso dient der TCP-Monitor zum Debugging, indem er auch unabhängig von SOAP als Proxy eingebunden wird. (csc)

Infos

[1] SOAP Version 1.2 Part 1: Messaging Framework: [http://www.w3.org/TR/soap12-part1/]

[2] SOAP Version 1.2 Part 0: Primer: [http://www.w3.org/TR/soap12-part0/]

[3] Coffeeshop, “Mit CORBA über den Kaffeetassenrand schauen”: Linux-Magazin 09/99, S. 100, [https://www.linux-magazin.de/Artikel/ausgabe/1999/09/Corba/corba.html]

[4] Coffeeshop, “Objekte ohne Grenzen mit RMI”: Linux-Magazin 06/99, S. 95 (auch online verfügbar)

[5] Web Services Description Language: [http://www.w3.org/TR/wsdl]

[6] Axis: [http://xml.apache.org/axis]

[7] Java Activation Framework: [http://java.sun.com/products/javabeans/glasgow/jaf.html]

[8] Java-Mail: [http://java.sun.com/products/javamail/index.jsp]

[9] XML-Signaturen: [http://xml.apache.org/security/]

[10] Coffeeshop, “Jenseits von Babel”: Linux-Magazin 07/04, S. 115

[11] Komplette Listings: [https://www.linux-magazin.de/Service/Listings/2005/06/coffeeshop]

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben