Web Services mit Spring WS

Die Vorgehensweise ist einfach. Erst wird das Element mit der Xpath-Expression (z. B. Category) ermittelt. Die Properties der zugehörigen Klasse werden dann mit der Funktion getChildText ermittelt. Etwas aufwändiger ist die Erzeugung des DateRange-Objekts. Hier müssen wir den Inhalt der Kindelemente in ein Datum wandeln. Dazu können Sie am besten das SimpleDateFormat aus dem java.text Package verwenden. Nun ist es an der Zeit, den Service mit den frisch erstellten Objekten abzufragen:

List products = productService        .findProductsForSupplierAndCategoryInDateRange(category,
            supplier, dateRange);

Jetzt ist das Meiste schon getan. Wir haben eine Liste mit den gefundenen Produkten. Allerdings stellt uns diese Liste vor einige Probleme. Wenn Sie sich das Klassendiagramm aus Abbildung 2 noch einmal ansehen, erkennen Sie, dass die Klasse Product Referenzen auf Category und Supplier hält. Da die Methode invokeInternal ein jDOM-Element als Rückgabe liefert, muss der ganze Objektbaum, der an dem Product hängt, in XML bereitgestellt werden. Diesen Vorgang nennt man Marshalling. Spring WS liefert einen leistungsfähigen O/X-Mapper. Wie bei Spring üblich, ist dies nur eine Abstraktionsschicht über vorhandene Frameworks:

  • JAXB 1 und 2
  • Castor
  • XMLBeans
  • JiBX
  • XStream

Um nun in unserem Endpoint einen Marshaller zu verwenden, reicht es, ein Feld vom Typ org.springframework.oxm.Marshaller (ist ein Interface) und die zugehörige Setter-Methode zu erstellen. Im nächsten Schritt erstellen wir die zugehörige Spring-Konfiguration, die eine konkrete Implementierung in unseren Endpoint injiziert. Das Interface hat nur folgende interessante Methode:

void marshal(Object graph, javax.xml.transform.Result result) 
          throws XmlMappingException, IOException;

Um diese Methode aufzurufen, brauchen wir lediglich das Objekt, das in XML bereitgestellt werden soll und eine Implementierung des Interfaces javax.xml.transform.Result. jDOM liefert bereits eine passende Implementierung dieses Interfaces, sodass wir die Methode nur um einige Zeilen erweitern müssen (Listing 6).

Listing 6
protected Element invokeInternal(Element requestElement) throws Exception {

  .

  JDOMResult result = new JDOMResult();

  marshaller.marshal(products, result);
  return (Element) result.getResult().get(0);
}

Damit der Endpoint nun verwendet werden kann, brauchen wir noch ein paar weitere Bibliotheken. Die wichtigsten davon sind Castor und Jaxen. Castor ist für das Marshalling zuständig und Jaxen wird für die Auswertung der XPath-Expressions benötigt. Allerdings ist die pom.xml von Jaxen nicht sonderlich gut gepflegt. Aus diesem Grund müssen einige Abhängigkeiten, die Jaxen definiert, ausgeschlossen werden.

Schritt 6: Spring konfigurieren

Bisher haben wir von Spring recht wenig gesehen. Die erstellten Klassen waren allesamt POJOs. Lediglich zweimal kamen schon Spring-Artefakte ins Spiel. Einmal die abstrakte Klasse AbstractJDomPayloadEndpoint, mit der wir unseren Endpoint erweitert haben. Das andere Mal als wir den Marshaller des Spring O/X Mappers verwendeten. Doch nun ist es an der Zeit, sämtliche Komponenten miteinander zu verdrahten. Es gibt zwei Klassen, die typische Spring Beans sind: die ProductService– und die ProductServiceEndpoint-Klasse. Der ProductServiceEndpoint hält eine Referenz auf den ProductService. Somit muss dies ebenfalls in der Spring-Konfiguration abgebildet werden (Listing 7).

Listing 7

Diese Spring-Konfiguration können wir als spring-ws-servlet.xml unter META-INF/spring abspeichern. Im nächsten Schritt wird die web.xml dahingehend konfiguriert, dass diese Datei beim Start der Anwendung herangezogen und der Spring-Kontext aufgebaut wird. Zusätzlich ist bereits der Marshaller injiziert worden. Der CastorMarshaller kommt ohne jegliche weitere Konfiguration aus. Erst wenn das XML-Format, in das der Marshaller die Objekte bereitstellen soll, angepasst werden muss, kann der CastorMarshaller konfigurativ angepasst werden.

Neben den beiden Klassen, die jetzt miteinander verdrahtet wurden, müssen noch der Endpoint und die WSDL-Generierung konfiguriert werden. Die Konfiguration des Endpoints ist recht einfach mit einem Mapping zwischen Namespace und der konfigurierten Bean hergestellt. Optional können Sie noch einen Logging Interceptor hinzufügen (Listing 8).

Listing 8

      ProductServiceEndpoint

In Listing 9 sehen Sie die Konfiguration für die automatische WSDL-Generierung. Über die Properties portTypeName und locationURI können Sie die generierte WSDL beeinflussen. Im nächsten Schritt sehen Sie noch, wie die locationURI an die wirkliche Serverumgebung angepasst werden kann. Die Klasse DefaultWsdl11Definition bietet noch einige andere Konfigurationsparameter, die Sie im entsprechenden Javadoc nachlesen können.

Listing 9
Schritt 7: Konfiguration des Web Descriptors

Der letzte Schritt ist es, den Web Descriptor (web.xml) zu konfigurieren. Die web.xml befindet sich im Verzeichnis src/main/web/WEB-INF. In dieser web.xml werden nur das MessageDispatcherServlet aus dem Spring WS Framework und die zugehörigen Mappings konfiguriert (Listing 10). Das MessageDispatcherServlet hat in unserem Beispiel zwei Parameter: contextConfigLocation und tarnsformWsdlLocations. Der erste Parameter gibt an, wo die initiale Kontextkonfiguration zu finden ist. In unserem Beispiel ist das die spring-ws-servlet.xml, die wir im vorangegangenen Schritt erstellt haben. Der zweite Parameter ist fast noch interessanter. Hiermit können Sie angeben, ob die URIs, die in der WSDL enthalten sind, so umgewandelt werden, dass der aktuelle Servername, Port und Anwendungsname enthalten sind.

Listing 10
Product Catalogue Servicespring-ws
      org.springframework.ws.transport.http.MessageDispatcherServlet
    contextConfigLocation
        classpath:META-INF/spring/spring-ws-servlet.xml
      transformWsdlLocationstruespring-ws/*
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.