Nachdem ich bereits JAX-RS in der Reihe „JSON Provider erstellen“ behandelt habe, bleibt noch das Pendant, nämlich JAX-WS über. Während sich JAX-RS mit REST-Services befasst, geht es bei JAX-WS um SOAP. Die Referenzimplementierung für JAX-WS ist unter http://jax-ws.java.net/ zu finden.
Wer sich für JAX-RS interessiert, der sei auf folgende Artikel in diesem Blog verwiesen:
- JSON Provider erstellen. Part 6: Ressourcen, Sub-Ressourcen
- JSON Provider erstellen. Part 5: Ressourcen, Parameter, CRUD, JSON- und XML-Response
- JSON Provider erstellen. Part 4: Mit Tomcat und Jersey REST Services erstellen
Bei JAX-WS gibt es zwei Herangehensweisen, wie man einen Web-Service erstellt:
- Bottom-Up:
Erstellen der Java-Klassen (POJOs) und generieren des Web-Services - Top-Down:
Erstellen der WSDL und generieren der Java-Klassen daraus
Bottom-Up
Vorgehensweise:
- Erstellen eines einfachen „Java“ Projektes in Eclipse.
Hinweis: Will man den WebService anschließend auf einem Applikationsserver oder Tomcat deployen, macht es mehr Sinn ein Dynamisches Web-Projekt mit EAR als Projekt-Template zu verwenden. - Definition des Web Service Endpoints:
Ein Web Service Endpoint wird mit @WebService (bzw. javax.jws.WebService) annotiert. Die Klasse ist die Implementierungsklasse des WebServices. - Optional: Definition des Service Endpoint Interfaces (SEI):
Das SEI ist ein Java-Interface, das die Methoden deklariert, die ein WS Client aufrufen kann. Es ist nicht notwendig SEIs zu deklarieren, da durch die @WebService Annotation der Implementierungsklasse implizit ein SEI deklariert wird. Meiner Meinung nach kann die Verwendung von SEIs aber die Übersichtlichkeit erhöhen. Verwendet man SEIs, dann muss man in der @WebService Annotation der Implementierungsklasse das Element endpointInterface hinzufügen. - Server zum Starten des WS implementieren bzw. WS deployen
- JAX-WS Client implementieren
Ohne SEI
package ohnesei;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService()
public class Hello {
private String message = new String("Hello, ");
public void Hello() {}
@WebMethod()
public String sayHello(String name) {
return message + name + ".";
}
}
package ohnesei;
import javax.xml.ws.Endpoint;
public class TestWsServer
{
public static void main( final String[] args )
{
String url = ( args.length > 0 ) ? args[0] : "http://localhost:4434/miniwebservice";
Endpoint.publish( url, new Hello() );
}
}
Inhalt in Eclipse:
Mit SOAPUI kann man den WebService testen:
Mit SEI
package mitsei;
import javax.jws.*;
@WebService
public interface HelloIF
{
public String sayHello(String name);
}
package mitsei;
import javax.jws.WebService;
@WebService( endpointInterface="mitsei.HelloIF" )
public class Hello implements HelloIF
{
private String message = new String("Hello, ");
public void Hello() {}
public String sayHello(String name) {
return message + name + "!";
}
}
Man beachte die Erweiterung:
@WebService( endpointInterface="mitsei.HelloIF" )
Der Server, zum Starten des WS:
package mitsei;
import javax.xml.ws.Endpoint;
import mitsei.Hello;
public class TestWsServer
{
public static void main( final String[] args )
{
String url = ( args.length > 0 ) ? args[0] : "http://localhost:4435/miniwebservice";
Endpoint.publish( url, new Hello() );
}
}
Der Client zum Testen:
package mitsei;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class TestWsClient
{
public static void main( final String[] args ) throws Throwable
{
String url = ( args.length > 0 ) ? args[0] : "http://localhost:4435/miniwebservice";
Service service = Service.create(
new URL( url + "?wsdl" ),
new QName( "http://mitsei/", "HelloService" ) );
HelloIF halloWelt = service.getPort( HelloIF.class );
System.out.println( "n" + halloWelt.sayHello( args.length > 1 ? args[1] : "" ) );
}
}
Inhalt in Eclipse:
Annotationen
@WebMethod(operationName="sayHello")
public @WebResult(name="hellomessage") String sayHello(@WebParam(name="name") String name) {
return message + name + "!";
}
Mit @Oneway werden Methoden definiert, die asynchronen ausgeführt werden sollen, d.h. der Client wird für die Ausführung der Methode nicht blockiert. Diese Vorgehensweise nach dem Fire-and-Forget-Prinzip funktioniert allerdings nur bei Methoden, die kein Ergebnis zurückliefern.
In der Implementierungsklasse kann man mit @PostConstruct und @PreDestroy Methoden definieren, um Initialisierungs- bzw. Aufräumarbeiten nach Initialisieren des WebServices bzw. vor dem Zerstören des WebServices zu erledigen.
Top-Down
Der Top-Down Ansatz, auch Contract-First genannt, beruht darauf, aus einer bestehenden Schnittstellen-Beschreibung (WSDL) einen WebService zu erstellen. JAX-WS bietet hierzu das Tool „wsimport“ an, welches aus der vorhandenen WSDL die entsprechenden Java-Artefakte generiert.
Vorausgesetzt, der im letzten Abschnitt erstellte WebService läuft noch, kann man wie folgt die Artefakte erstellen:
juergen@vostro:~$ cd juergen@vostro:~$ mkdir loeschen juergen@vostro:~$ cd loeschen juergen@vostro:~/loeschen$ mkdir generated juergen@vostro:~/loeschen$ wsimport -d generated http://localhost:4435/miniwebservice?wsdl
Im Beispiel werden die Java-Artefakte in folgendem Verzeichnis abgelegt:
juergen@vostro:~$ ls -R ~/loeschen/generated /home/juergen/loeschen/generated: mitsei /home/juergen/loeschen/generated/mitsei: HelloIF.class ObjectFactory.class SayHello.class HelloService.class package-info.class SayHelloResponse.class
Will man die generierten Java Dateien beibehalten, muss man die Option „-keep“ anwenden:
juergen@vostro:~/loeschen$ wsimport -d generated -keep http://localhost:4435/miniwebservice?wsdl juergen@vostro:~/loeschen$ ls -R ~/loeschen/generated /home/juergen/loeschen/generated: mitsei /home/juergen/loeschen/generated/mitsei: HelloIF.class ObjectFactory.class SayHello.class HelloIF.java ObjectFactory.java SayHello.java HelloService.class package-info.class SayHelloResponse.class HelloService.java package-info.java SayHelloResponse.java
Mit der Option „-p <Package Name>“ kann man noch einen zu generierenden Package-Namen mitgeben:
juergen@vostro:~/loeschen$ wsimport -d generated -p mitsei.generated -keep http://localhost:4435/miniwebservice?wsdl juergen@vostro:~/loeschen$ ls -R ~/loeschen/generated /home/juergen/loeschen/generated: mitsei /home/juergen/loeschen/generated/mitsei: generated /home/juergen/loeschen/generated/mitsei/generated: HelloIF.class ObjectFactory.class SayHello.class HelloIF.java ObjectFactory.java SayHello.java HelloService.class package-info.class SayHelloResponse.class HelloService.java package-info.java SayHelloResponse.java
Wie kann man sich nun damit einen WebService bauen?
Zuerst kann man die generierten Dateien in das Workspace Verzeichnis von Eclipse kopieren. Da wir bereits ein Projekt mit einem Package „mitsei“ im vorigen Abschnitt erstellt haben, kopieren wir die Dateien einfach hier hinein.
Beispiel:
juergen@vostro:~/workspace/JAXWSTest/src/mitsei$ cp -R ~/loeschen/generated/mitsei/generated .
Anschließend refreshen wir in Eclipse das Projekt (Projekt-Explorer + F5):
In vielen Fällen wird es so sein, dass man eine WSDL von einem bereits existierenden WebService erhält und diesen nun nutzen will. D.h. man erstellt einen Client, der den WebService aufruft. Im folgenden will ich den WebService einfach nutzen. Nachdem ich mir via „wsimport“ alle notwendigen Artefakte erzeugt habe, erstelle ich einen Java-Client, der in der Lage ist, den WebService aufzurufen:
package mitsei.generated;
import javax.xml.ws.BindingProvider;
public class TestWsClient {
public static void main(String[] args) {
HelloService service = new HelloService();
HelloIF port = service.getHelloPort();
// BindingProvider bp = (BindingProvider) port;
// bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "test");
// bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "test");
String request = "Jürgen";
System.out.println (" Request: "" + request + """ );
String response = port.sayHello(request);
System.out.println ("Response: "" + response + """ );
}
}
Den Client „TestWsClient“ erstelle ich der Einfachheit halber direkt im Package „mitsei.generated“:
Hier die Ausgabe:
XSD und WSDL
Eine XSD (XML Schema Definition) beschreibt den Aufbau einer XML Datei. Sie ist der Nachfolger der klassischen DTD (Document Type Definition).
Die WSDL (Web Services Description Language) hingegen beschreibt den Aufbau eines SOAP WebServices, also u.a. die Operationen bzw. Schnittstellen, deren Übergabe- und Rückgabe-Parameter. Damit ist es auch notwendig, erlaubte bzw. verwendete Datentypen zu beschreiben. Man tut dies in Form einer oder mehrerer XSDs, die man von extern inkludiert oder inline innerhalb der WSDL definiert.
Für die Erstellung von XSDs und WSDLs gibt es zahlreiche Tools, siehe hierzu die Abschnitte „Links„.
XSD in WSDL inkludieren
Hier ein Beispiel (lokale Datei):
<wsdl:types> <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.websale-ag.com/StockUpdateService"> <xsd:import namespace="http://www.websale-ag.com/xsd/StockUpdateService" schemaLocation="stockupdate.xsd"></xsd:import> </xsd:schema> </wsdl:types>
Hier ein Beispiel (XSD über URL):
<types> <xsd:schema> <xsd:import namespace="http://ohnesei/" schemaLocation="http://localhost:4434/miniwebservice?xsd=1"></xsd:import> </xsd:schema> </types>
XSD in WSDL inline definieren
Hier ein Beispiel (mit zwei XSD Inline Defintionen):
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:websale="http://www.websale.net/soap/faults" xmlns:p0="http://www.websale-ag.com/xsd/StockUpdateService" targetNamespace="http://www.websale-ag.com/xsd/StockUpdateService">
<xsd:complexType name="SoapFault">
<xsd:sequence>
<xsd:element name="faultcode" type="xsd:string" />
<xsd:element name="faultstring" type="xsd:string" />
<xsd:element name="details" type="p0:Details" />
</xsd:sequence>
...
</xsd:schema>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:p0="http://www.websale.net/soap/faults" targetNamespace="http://www.websale.net/soap/faults">
<xsd:simpleType name="Code">
<xsd:restriction base="xsd:int">
<xsd:minInclusive value="0" />
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
...
Aufbau einer WSDL Datei
Die folgende Beschreibung einer WSDL Datei, habe ich bei http://www.torsten-horn.de/techdocs/jee-jax-ws.htm gefunden:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" WSDL-Namespace
targetNamespace="..."> Target-Namespace
<import> ... </import> Importe externer WSDL-Dateien
<types> Typdeklarationen:
<xsd:schema> ... </xsd:schema> Wahlweise Einbettung von Schema-Informationen
</types> oder Importe externer Schema-XSD-Dateien
<message ...><part ... /></message> "Messages" sind Parameter und Rückgabewerte (z.B. definiert über XSD)
<portType ...> "Port Types" entsprechen Java-Interfaces:
<operation ...> "Operations" entsprechen Java-Methoden:
<input ... /> Input-Messages (Argumentparameter)
<output ... /> Output-Messages (Rückgabewerte)
<fault ... /> Fehlernachrichten (z.B. Exceptions)
</operation>
</portType>
<binding ...> "Bindings" sind Verbindungen von "Port Types" zu Protokollen:
<soap:binding ... /> Z.B. SOAP-Protokoll über HTTP mit style="document"
<operation ...> Pro "Operation" Zusatzangaben:
<soap:operation ... /> Z.B. soapAction=""
<input> <soap:body ... /></input> Z.B. use="literal"
<output><soap:body ... /></output> Z.B. use="literal"
<fault ...><soap:fault ... /></fault> Z.B. use="literal"
</operation>
</binding>
<service ...> "Services" sind Gruppen von "Ports"
<port ...> "Ports" beschreiben den Zugang zu Diensten
<soap:address ... /> Z.B. location="..." (URL des Dienstes)
</port>
</service>
</definitions>
Links
Tutorials:
http://www.mkyong.com/tutorials/jax-ws-tutorials/
Tools:
freeformatter.com: zahlreiche Online-Tools für JSON, XML, HTML, XSD: Formatter, Validator, Encoder
xmlforasp.net: Online-Umwandlung einer XML-Datei in eine XSD-Datei (XML-Schema)
Microsoft Windows SDK for Windows 7 and .NET Framework 4: enthält u.a. auch Tools, wie xsd.exe oder wsdl.exe
JDK, OpenJDK (Java Development Kit): enthält Tools wie wsgen und wsimport







