Web Services mit SOAP und WSDL in Java mit dem veralteten Apache SOAP 2.3 (dem Vorgänger von Axis)
In diesem Dokument geht es um Web Services mit Java und Apache SOAP 2.3. Bitte beachten Sie, dass Apache SOAP 2.3 mittlerweile veraltet ist. Verwenden Sie für Neuentwicklungen JAX-WS, Axis2 oder ein anderes Framework für Webservices.
Erklärungen zu Web Services, SOAP, WSDL, UDDI und XML finden Sie in soap.htm und java-xml.htm.
Das Java WSDP (Java Web Services Developer Pack) von Sun bietet eine Sammlung von Hilfsmitteln und APIs für SOAP Web Services. Es ist in neueren Versionen des J2EE integriert.
Zum Java WSDP gehört:
Siehe auch:
Eine recht bekannte SOAP-Implementierungen ist das ursprünglich von IBM entwickelte und dann der Apache Group übergebene Apache SOAP und dessen Nachfolger Apache Axis.
Bei Nutzung solcher Frameworks muss sich der Entwickler nicht unbedingt mit dem von SOAP verwendeten XML-Format ("Envelope", "Header", "Body", ...) auseinander setzen, da die XML-Daten automatisch erzeugt und geparst werden.
Apache SOAP 2.3.x basiert auf Java und besteht aus Client-Bibliotheken, Server-Bibliotheken und einem Servlet, mit dem eingehende Anfragen verarbeitet werden.
Mit den Client-Bibliotheken wird die SOAP-Anfrage erstellt, an den Server gesendet und die Antwort entschlüsselt. Clientseitig müssen spezielle Klassen und Methoden aufgerufen werden, wie zum Beispiel "Call", "setTargetObjectURI()", "setMethodName()", "setParams()", "invoke()" und "getReturnValue()".
Die serverseitigen Module werden in einen Webserver integriert, zum Beispiel im Tomcat. Die per SOAP aufrufbaren Klassen und Methoden brauchen nicht besonders angepasst zu werden. Allerdings muss in einer zusätzlichen XML-Datei ein Deployment-Deskriptor (Bereitstellungs-Beschreibung) zur Verfügung gestellt werden, in dem die zu verwendenden Klassen, Methoden und Aufrufparameter definiert werden und mit dem sie bei der SOAP Engine registriert werden (deployt).
set J2EE_HOME=D:\Tools\j2sdkee set JAVA_HOME=C:\Program Files\Java\jdk1.6 set SOAP_HOME=D:\Tools\soap set CATALINA_HOME=D:\Tools\Tomcat set path=%path%;%JAVA_HOME%\bin;%J2EE_HOME%\bin |
set CLASSPATH=.;D:\Tools\soap\lib\soap.jar; D:\Tools\soap\lib\mail.jar; D:\Tools\soap\lib\activation.jar; D:\Tools\soap; %CLASSPATH% |
Im gezeigten CLASSPATH ist auch "D:\Tools\soap" aufgenommen worden, damit die Apache-SOAP-Beispiele (unter "D:\Tools\soap\samples") ausgeführt werden können. Falls Sie eine Web-Applikation in einem anderen Verzeichnis haben, muss deren "classes"-Pfad (z.B. "C:\MeineWebApp\WEB-INF\classes") eventuell ebenfalls hinzugefügt werden. Bei älteren Tomcat-Versionen (z.B. 3.2.x) müsste außerdem ein JAXP-kompatibler XML-Parser (z.B. Xerces) installiert und im CLASSPATH als erster Bestandteil eingetragen werden.
Je nach Tomcat-Version sind zusätzliche Maßnahmen erforderlich, damit Tomcat den voreingestellten CLASSPATH übernimmt.
Zum Beispiel bei Tomcat 4.0.4 muss eventuell in der Datei
"D:\Tools\Tomcat\bin\setclasspath.bat"
die Zeile
set CLASSPATH=%JAVA_HOME%\lib\tools.jar
ersetzt werden durch
set CLASSPATH=%JAVA_HOME%\lib\tools.jar;%CLASSPATH%
Eventuell darf dann Tomcat nicht mehr per "Start" | "Programme" | "Apache Tomcat" | "Start" gestartet werden,
sondern nur noch über startup.bat:
cd \Tools\Tomcat\bin startup.bat |
<Context path="/soap" docBase="D:/Tools/soap/webapps/soap" reloadable="true" > </Context> |
Öffnen Sie ein Kommandozeilenfenster ('Start' | 'Alle Programme' | 'Zubehör' | 'Eingabeaufforderung') und geben Sie folgende Befehle ein (unter Windows XP können Sie den Kommandotext im Webbrowser mit gedrückter Maustaste markieren, mit 'Strg+C' zwischenspeichern und irgendwo im Kommandozeilenfenster mit rechter Maustaste, 'Einfügen' und 'Return' ausführen):
set CLASSPATH | |
--> | CLASSPATH=.;D:\Tools\soap\lib\soap.jar; D:\Tools\soap\lib\mail.jar; D:\Tools\soap\lib\activation.jar; D:\Tools\soap |
cd D:\Tools\soap\samples\addressbook | |
java org.apache.soap.server.ServiceManagerClient http://localhost/soap/servlet/rpcrouter deploy DeploymentDescriptor.xml | |
java org.apache.soap.server.ServiceManagerClient http://localhost/soap/servlet/rpcrouter list | |
--> | Deployed Services: urn:AddressFetcher |
java samples.addressbook.GetAllListings http://localhost/soap/servlet/rpcrouter | |
--> | alle Adressen als XML-Datei |
java samples.addressbook.GetAddress http://localhost/soap/servlet/rpcrouter "John B. Good" | |
--> | einzelne Adresse im Klartext |
Falls Ihr Webbrowser die mit "java" beginnenden Zeilen auf zwei Zeilen umgebrochen hat: Bitte geben Sie diese als eine einzige Kommandozeile ein.
Die mit " --> " beginnenden Zeilen bitte nicht eingeben, sie demonstrieren nur die Ausgabe.
Mit "PutAddress" und "PutListings" können weitere Adressen hinzugefügt werden.
Die serverseitige Service-Funktion, die per SOAP angeboten werden soll, kann eine normale Java-Methode sein und braucht nicht speziell angepasst zu werden. Sie muss lediglich per Deployment exportiert werden.
Die folgende kurze Java-Klasse "HelloWorldService" soll als SOAP-Service implementiert werden.
Speichern Sie sie in der neu zu erstellenden Datei "D:\Tools\soap\soaptest\service\HelloWorldService.java",
überprüfen Sie, dass der Pfad "D:\Tools\soap" Bestandteil des Tomcat-CLASSPATHs ist,
wechseln Sie in das Verzeichnis "D:\Tools\soap"
und kompilieren Sie mit
"javac soaptest/service/HelloWorldService.java":
package soaptest.service; public class HelloWorldService { public String getMessage( String sCallerName ) { return "Hello " + sCallerName + "!"; } }
Bei neueren Tomcat-Version kann es auch sinnvoll sein, den von Tomcat vorgegebenen Pfad "...\common\classes" zu verwenden.
Der SOAP-Service muss bei der SOAP-Engine angemeldet (registriert, deployt) werden. Dafür gibt es verschiedene Methoden.
Deployment über Apache SOAP Admin
Über "http://localhost/soap" und "Run the admin client" wird der "Apache SOAP Admin" gestartet. Betätigen Sie die linke große "Deploy"-Schaltfläche, geben Sie folgende Werte ein und betätigen Sie zuletzt die kleine "Deploy"-Schaltfläche ganz unten:
Deploy mit Apache SOAP Admin | (Bemerkung) | |
ID | urn:helloworld | (Name für diesen SOAP-Service) |
Scope | Request | (Request, Session oder Application) |
Methods | getMessage | (Methodennamen, falls mehrere: getrennt durch Leerzeichen) |
Provider Type | Java | (Java, EJB, Script oder User-Defined) |
Provider Class | soaptest.service.HelloWorldService | (Klasse inklusive Pfad) |
Static | No | (statische Klassenmethoden oder Instanzmethoden) |
weitere Eingabefelder | Voreinstellung lassen |
Deployment über XML-Datei und Kommandozeile
Die folgende XML-Datei "HelloWorld.xml" könnte ein Deployment-Descriptor für die oben gezeigte SOAP-Service-Klasse "HelloWorldService.java" sein:
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" id="urn:helloworld"> <isd:provider type="java" scope="Request" methods="getMessage"> <isd:java class="soaptest.service.HelloWorldService" static="false"/> </isd:provider> </isd:service>
Mit dieser Datei wird der SOAP-Dienst mit folgender Kommandozeile angemeldet:
java org.apache.soap.server.ServiceManagerClient http://localhost/soap/servlet/rpcrouter deploy HelloWorld.xml
Die folgende Java-Klasse "HelloWorldClient" implementiert einen SOAP-Client für den SOAP-Service "HelloWorldService". Speichern Sie sie in der neu zu erstellenden Datei "D:\Tools\soap\soaptest\client\HelloWorldClient.java" und führen Sie die folgenden drei Kommandos aus:
cd D:\Tools\soap javac soaptest/client/HelloWorldClient.java java soaptest/client/HelloWorldClient MeinName |
package soaptest.client; import java.net.*; import java.util.*; import org.apache.soap.*; import org.apache.soap.rpc.*; public class HelloWorldClient { public static void main( String[] args ) { String sServiceUrl = "http://localhost/soap/servlet/rpcrouter"; String sServiceUri = "urn:helloworld"; // must match HelloWorld.xml String sMethodName = "getMessage"; String sInput = ( 0 < args.length ) ? args[0] : "MeinName"; Response resp; try { Vector params = new Vector(); params.addElement( new Parameter( "sInput", sInput.getClass(), sInput, null ) ); Call call = new Call(); call.setTargetObjectURI( sServiceUri ); call.setMethodName( sMethodName ); call.setEncodingStyleURI( Constants.NS_URI_SOAP_ENC ); call.setParams( params ); resp = call.invoke( new URL( sServiceUrl ), sServiceUri ); } catch( Exception ex ) { System.err.println( "Error while calling '" + sMethodName + "':" ); System.err.println( ex.getMessage() ); return; } if( resp.generatedFault() ) { System.out.println( "Call to '" + sMethodName + "' returned a fault:" ); System.err.println( resp.getFault() ); } else { if( null != resp.getReturnValue() ) { Object result = resp.getReturnValue().getValue(); if( null != result ) { System.out.println( "'" + sMethodName + "' returned an object of " + result.getClass() + ": " ); System.out.println( result ); } } } } }
Die folgende Java-Klasse "SoapResultFromArray" implementiert einen allgemeinen SOAP-Client für einfache SOAP-Dienste. Sie kann zum Beispiel mit obigem HelloWorldService kommunizieren oder im Internet verfügbare SOAP-Dienste zum Beispiel zum AltaVista Babelfish Translation Service und zu eBay nutzen. Allerdings funktioniert sie nur mit sehr einfachen Diensten, die simple Objekte und Namespace-Strukturen verwenden.
Speichern Sie die Java-Klasse in der neu zu erstellenden Datei "SoapResultFromArray.java" und führen Sie bei eingeschalteter Internetverbindung folgende Kommandos aus:
javac SoapResultFromArray.java | |
java SoapResultFromArray http://localhost/soap/servlet/rpcrouter helloworld urn:helloworld getMessage sInput MeinName | |
java SoapResultFromArray http://services.xmethods.net:80/perl/soaplite.cgi urn:xmethodsBabelFish#BabelFish urn:xmethodsBabelFish BabelFish translationmode de_en sourcedata "Hallo Welt, Guten Tag" | |
java SoapResultFromArray http://services.xmethods.net:80/perl/soaplite.cgi urn:xmethodsBabelFish#BabelFish urn:xmethodsBabelFish BabelFish translationmode de_fr sourcedata "Hallo Welt, Guten Tag" | |
java SoapResultFromArray http://services.xmethods.net:80/soap/servlet/rpcrouter EbayWatcher urn:xmethods-EbayWatcher getCurrentPrice auction_id 1234567890 | |
java SoapResultFromArray http://www.soapclient.com/xml/SQLDataSoap.WSDL /SQLDataSRL /SQLDataSRL ProcessSRL SRLFile /xml/whois.sri RequestName whois key "ibm.com" | |
java SoapResultFromArray http://www.soapclient.com/xml/SQLDataSoap.WSDL /SQLDataSRL /SQLDataSRL ProcessSRL SRLFile /xml/web.sri RequestName Google key "SOAP" > Result.html |
Falls Ihr Webbrowser die mit "java" beginnenden Zeilen auf mehrere Zeilen umgebrochen hat: Bitte geben Sie diese als eine einzige Kommandozeile ein.
Zum Babelfish-Beispiel finden Sie weitere Infos unter soap.htm#SOAP-RequestResponse und ein Programmierbeispiel mit JSP Apache Jakarta Taglibs unter jsp-taglibs.htm#ErsteTestanwendungApacheJakartaTaglibs.
Die beim eBay-Beispiel verwendete "auction_id" "1234567890" müssen Sie durch eine aktuell gültige eBay-ID-Nummer ersetzen.
Bei der mit "... > Result.html" endenden Zeile müssen Sie die resultierende Datei "Result.html" im Webbrowser anzeigen.
Informationen zu den benutzten SOAP-Diensten finden Sie unter:
- HelloWorldService
- http://www.xmethods.net: BabelFish
- http://www.xmethods.net: eBay Price Watcher
- http://www.soapclient.com/whois.html
- http://www.soapclient.com/webSearch.html
- http://www.xmethods.net/ve2/Directory.po
- http://www.soapclient.com/XmethodsServices.html
Die Datei "SoapResultFromArray.java":
import java.net.*; import java.util.*; import org.apache.soap.*; import org.apache.soap.rpc.*; public class SoapResultFromArray { public static void main( String[] args ) { if( 6 > args.length ) { System.out.println( "Error: SoapResultFromArray needs at least 6 parameters:" ); System.out.println( " LocationURL SOAPAction NamespaceURI Method" ); System.out.println( " ParmName1 ParmValue1 ParmName2 ParmValue2 ..." ); return; } System.out.println( "Location URL = " + args[0] ); System.out.println( "SOAPAction = " + args[1] ); System.out.println( "Namespace URI = " + args[2] ); System.out.println( "Method Name = " + args[3] ); int i = 3; while( ++i < args.length - 1 ) { System.out.println( i/2-1 + ". Param Name = " + args[i] ); System.out.println( i/2-1 + ". Param Value = " + args[++i] ); } Object oResult = null; try { oResult = getSoapResultFromObjectArray( args ); } catch( Exception ex ) { System.out.println( "Error:\n" + ex.getMessage() ); return; } if( null != oResult ) System.out.println( "Result Class = " + oResult.getClass() ); System.out.println( "Result Value = " + oResult ); } public static Object getSoapResultFromObjectArray( Object[] args ) throws Exception { Object oResult = null; Vector params = new Vector(); int i = 3; while( ++i < args.length - 1 ) params.addElement( new Parameter( args[i++].toString(), args[i].getClass(), args[i], null ) ); // Parameters Call call = new Call(); call.setTargetObjectURI( args[2].toString() ); // Namespace URI call.setMethodName( args[3].toString() ); // Method Name call.setEncodingStyleURI( Constants.NS_URI_SOAP_ENC ); call.setParams( params ); Response resp = call.invoke( new URL( args[0].toString() ), // Location URL args[1].toString() ); // SOAPAction if( resp.generatedFault() ) throw new Exception( resp.getFault().toString() ); else if( null != resp.getReturnValue() ) oResult = resp.getReturnValue().getValue(); return oResult; } }
Die Apache-SOAP-Distribution enthält zum Debuggen das einfache Trace-Tool "TcpTunnelGui", welches sich in den TCP/IP-Datenfluss zwischenschaltet und die übertragenen Request- und Response-Texte übersichtlich anzeigt.
Außer TcpTunnelGui gibt es noch viele weitere sehr ähnliche Tools, zum Beispiel der TCP-Monitor aus dem Web-Services-Projekt "Axis" der Apache-Group ("org.apache.axis.utils.tcpmon" aus "axis.jar"). Viele Trace-Tools werden nahezu identisch benutzt wie TcpTunnelGui.
TcpTunnelGui wird mit folgender Syntax gestartet:
javaw org.apache.soap.util.net.TcpTunnelGui <ListenPort> <TargetHost> <TargetPort>
Um den Datenfluss über das Trace-Tool zu leiten, muss die SOAP-Anfrage an "localhost" mit der als "<ListenPort>" übergebenen Portnummer gesendet werden. Das Tool leitet dann weiter an das eigentliche Ziel ("<TargetHost>" und "<TargetPort>").
Um obiges HelloWorldClient-Beispiel zu debuggen, müsste im Java-Sourcecode die "sServiceUrl"-Definition geändert werden zu:
String sServiceUrl = "http://localhost:8081/soap/servlet/rpcrouter";
Alternativ kann der SoapResultFromArray-Client verwendet werden, dann bleibt der Sourcecode unverändert, da die URL auf der Kommandozeile übergeben wird.
Beides demonstrieren die folgenden Kommandozeilenbeispiele. Falls Ihr Webbrowser die mit "java" beginnenden Zeilen auf mehrere Zeilen umgebrochen hat: Bitte geben Sie diese als eine einzige Kommandozeile ein.
Tracen zum lokalen SOAP-Server zum oben gezeigten Beispiel-SOAP-Service HelloWorldService
rem Starten des Trace-Tools: | |
javaw org.apache.soap.util.net.TcpTunnelGui 8081 localhost 80 | |
rem HelloWorldClient.java mit sServiceUrl geändert zu ...localhost:8081...: | |
java soaptest/client/HelloWorldClient8081 MeinName | |
rem SoapResultFromArray ohne Trace-Tool: | |
java SoapResultFromArray http://localhost/soap/servlet/rpcrouter urn:helloworld getMessage sInput MeinName | |
rem SoapResultFromArray mit Trace-Tool: | |
java SoapResultFromArray http://localhost:8081/soap/servlet/rpcrouter urn:helloworld getMessage sInput MeinName |
Tracen zu entferntem SOAP-Server mit dem oben gezeigten BabelFish-Client
rem Starten des Trace-Tools: | |
javaw org.apache.soap.util.net.TcpTunnelGui 8081 services.xmethods.net 80 | |
rem Normaler Aufruf ohne Trace-Tool: | |
java SoapResultFromArray http://services.xmethods.net:80/perl/soaplite.cgi urn:xmethodsBabelFish BabelFish translationmode de_en sourcedata "Hallo Welt, Guten Tag" | |
rem Debug-Aufruf mit Trace-Tool: | |
java SoapResultFromArray http://localhost:8081/perl/soaplite.cgi urn:xmethodsBabelFish BabelFish translationmode de_en sourcedata "Hallo Welt, Guten Tag" |
Das WSTK (Web Services ToolKit) von IBM (http://www.alphaworks.ibm.com/tech/webservicestoolkit) ist eine Tool-Sammlung für Java-Programmierer zur Einrichtung und Nutzung von Web Services.
Es bietet:
Installation des WSTK von IBM (Web Services ToolKit, Tool-Sammlung zur Einrichtung und Nutzung von Web Services):
WSDL
WSDL (Web Services Description Language) ist ein XML-Derivat zur Beschreibung der Schnittstellen von Web Services. Nachrichtenstrom-Formate und Funktionsaufrufe werden definiert (siehe auch soap.htm#WSDL und soap.htm#Beispiel-UDDI-WSDL-SOAP).
Beispiele für WSDL-Dateien:
WSDL2Java: WSDL-Datei auswerten
Sie können eine WSDL-Datei interaktiv mit generischen SOAP-Clients nutzen, die Ihre Informationen aus der WSDL-Datei beziehen, automatisch eine passende Eingabemaske erzeugen und das Ergebnis des Web Services anzeigen. Entsprechende Tools gibt es zum Beispiel unter: http://www.soapclient.com/SoapTest.html und http://www.soapclient.com/SoapMsg.html.
Normalerweise wird ein SOAP-Web-Service jedoch aus Programmen angesprochen.
Aus einer bestehenden WSDL-Datei können mit "org.apache.axis.wsdl.WSDL2Java" Java-Sourcedateien erstellt werden,
die als Proxy verwendet werden können, worüber der Web Service leicht per Java genutzt werden kann.
Hilfe zu den WSDL2Java-Kommandozeilenparametern gibt es bei installiertem WSTK mit:
call D:\Tools\wstk\bin\wstkenv.bat java -cp "%WSTK_CP%" org.apache.axis.wsdl.WSDL2Java -h |
Proxy-Java-Sourcedateien können folgendermaßen aus einer WSDL-Datei erzeugt werden:
SETLOCAL call D:\Tools\wstk\bin\wstkenv.bat @echo on java -cp "%WSTK_CP%" org.apache.axis.wsdl.WSDL2Java http://www.MeineDomain.net/MeinPfad/MeineWsdlDatei.wsdl |
Dabei bitte die letzten Zeilen (ab "java ...") als eine Kommandozeile eingeben.
Es entsteht eine Java-Klasse, deren Name mit "...Stub" endet. Sie enthält die gewünschten SOAP-Funktion als Java-Methode, die von Ihrem Programm aufgerufen werden kann.
Beispiel: Mit der Kommandozeile
java -cp "%WSTK_CP%" org.apache.axis.wsdl.WSDL2Java http://www.webservicex.net/CurrencyConvertor.asmx?wsdl |
erhalten Sie die Java-Klasse "class CurrencyConvertorSoapStub", welche die Java-Methode "double conversionRate( fromCurrency, toCurrency )" enthält, worüber eine Währungsumrechnung leicht von Java aus durchgeführt werden kann.
Java2WSDL: WSDL-Datei erzeugen
WSDL-Dateien können mit "org.apache.axis.wsdl.Java2WSDL" aus Java-Sourcedateien erstellt werden.
Hilfe zu den Kommandozeilenparametern gibt es bei installiertem WSTK mit:
call D:\Tools\wstk\bin\wstkenv.bat java -cp "%WSTK_CP%" org.apache.axis.wsdl.Java2WSDL -h |
Eine erste einfache WSDL-Datei zum HelloWorldService könnte zum Beispiel folgendermaßen erzeugt werden:
SETLOCAL call D:\Tools\wstk\bin\wstkenv.bat @echo on java -cp "%WSTK_CP%;D:\Tools\soap" org.apache.axis.wsdl.Java2WSDL -o"HelloWorld.wsdl" -m"getMessage" -l"http://localhost/soap/servlet/rpcrouter" soaptest.service.HelloWorldService |
Dabei bitte die letzten Zeilen (ab "java ...") als eine Kommandozeile eingeben.
Bitte beachten Sie, dass eine öffentlich zugängliche WSDL-Datei natürlich keinen Sinn macht, wenn die darin eingetragene "service location url" nur lokal nutzbare Bestandteile wie "localhost" oder nur im Intranet verwendbare IP-Adressen ("192.168....") enthält, wie in obigem Beispielkommando in der vorletzten Zeile gezeigt.
Sie können Ihre WSDL-Datei mit den schon genannten generischen SOAP-Clients testen, die Ihre Informationen aus der WSDL-Datei beziehen (zum Beispiel http://www.soapclient.com/SoapTest.html und http://www.soapclient.com/SoapMsg.html).
Damit tatsächlich jemand über die WSDL-Datei Informationen zu Ihrer Web-Service-Schnittstelle in Erfahrung bringen kann, muss diese Datei an einem öffentlich zugänglichen Ort platziert werden (auf Ihrem Webserver). Ein beschreibender Text zu Ihrem Web Service und die URL der WSDL-Datei sollten Sie in einer UDDI-Registry eintragen (siehe soap.htm#UDDI).