RSS

Schlagwort-Archive: wsgen

JAX-WS: Webservices mit SOAP

JAX-WS: Webservices mit SOAP

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:

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:

  1. 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.
  2. Definition des Web Service Endpoints:
    Ein Web Service Endpoint wird mit @WebService (bzw. javax.jws.WebService) annotiert. Die Klasse ist die Implementierungsklasse des WebServices.
  3. 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.
  4. Server zum Starten des WS implementieren bzw. WS deployen
  5. JAX-WS Client implementieren

Ohne SEI

Ohne die Verwendung von SEIs, sieht eine einfache Implementierung eines JAX-WS WebServices wie folgt aus:
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 + ".";
  }
}
Mit einfachen Mitteln kann man einen Server implementieren, der den WS startet:
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:

Startet man den TestWSServer ohne Argumente, dann kann man sich im Browser die WSDL http://localhost:4434/miniwebservice?wsdl

 

Mit SOAPUI kann man den WebService testen:

 

Mit SEI

Mit Verwendung von SEIs definiert man zusätzlich eine Interface Klasse (das SEI):
package mitsei;

import javax.jws.*;

@WebService
public interface HelloIF
{
   public String sayHello(String name);
}
Die Implementierungklasse sieht dann wie folgt aus:
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

Sowohl das SEI, als auch die Implementierungsklasse müssen mit @WebService annotiert sein (siehe Beispiele vorher). Bei Bedarf kann der Entwickler mittels der Annotationen @WebMethod, @WebParam und@WebResult auf die Schnittstellenbeschreibung Einfluss nehmen.
@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

JDKOpenJDK (Java Development Kit): enthält Tools wie wsgen und wsimport

 
Hinterlasse einen Kommentar

Verfasst von - April 13, 2012 in WebServices

 

Schlagwörter: , , , ,

 
Erstelle eine Website wie diese mit WordPress.com
Jetzt starten