inkl. JAVA Mag Java Magazin 3.2010 Y spoke. We listened. You Introducing Liferay Enterprise Edition. CD Konzepte nebenläufiger Programmierung Liferay 5.1 Enterprise Edition Platinum Support (24x7) 19.950 EUR / server / year Compare Oracle WebCenter Suite: 125.000 EUR / processor, support 27.500 EUR / yr, as of 6 / 2008 ® Mashups mit Flex For more information, email us at [email protected]. Amazon Web Services Maintenance Subscription 2.950 EUR / server / year Generational Garbage Collection And with version 5.1, get the latest in SOA, Social Networking, and Collaboration technology, all at a fraction of the cost of Oracle ® or IBM ®. Java EE 6 Liferay Enterprise Edition gives you all the benefits of open source with the stability, security, and reliability of an enterprise subscription. LIFERAY GMBH — ROBERT - BOSCH - STRASSE 11, 63225 LANGEN, GERMANY TEL: +49 - (0) 6103 - 3018570 Ý FAX: +49 - (0) 6103 - 3018571 Österreich € 9,80 Schweiz sFr 16,80 Java t Architekturen t SOA t Agile From Java to Groovy Video von der W-JAX 2009 in voller Länge HIGHLIGHT 3.2010 JSR 330 www.javamagazin.de Grails 1.2 Wie weit geht die Standardisierung wirklich? 34 » Welche Features einen Blick wert sind 14 » Alle Infos im Heft Concurrency Konzepte für nebenläufige Programmierung » 42 in-Step Scrum Edition WEITERE INHALTE GPars Java EE 6 ausgepackt Enterprise JavaBeans 3.1 » 85 BlazeDS Jet Profiler Amazon Web Services Get into my Cloud Alle CD-Infos ab Seite 3 EUROPEAN HEADQUARTERS - LANGEN, GERMANY Deutschland € 8,50 magazin CD-INHALT Now get the best of both worlds. » 54 Mashups mit Flex: Implementierung eines Editors » 16 Generational Garbage Collection Memory-Management » 26 Enterprise Web Services mit Spring WS Zu Ihren Diensten, Monsieur Für den Datenaustausch oder das Anbieten von Diensten zwischen Anwendungen werden oft Web Services verwendet. Um einen Web Service anzubieten oder zu verwenden, gibt es sehr viele Möglichkeiten und Vorgehensweisen. Dieser dreiteilige Artikel stellt Ihnen die Möglichkeiten vor, die Spring WS Ihnen bietet. von Thorsten Kamann as Spring Framework [1] bietet nativ eine Unterstützung für die Verwendung von Web Services. Sie können dabei das zugrunde liegende Framework selbst bestimmen, z. B. Axis [2] oder Apache XCF [3]. Artikelserie Quellcode auf CD Teil 1: Basics zu Spring WS Teil 2: Spring WS und JaxB Teil 3: Spring WS und Spring Security 66 javamagazin 3|2010 Spring WS [4] ist ein Projekt aus dem SpringSource-Umfeld und hat das Ziel, die Entwicklung von Web Services zu vereinfachen. Dabei werden viele der etablierten JAXP-Frameworks, z. B. DOM, SAX und StAX, aber genauso JDOM, dom4j und XOM unterstützt. Darüber hinaus existiert innerhalb von Spring WS ein Modul, das für das XML-Mapping (O/X Mapper) verantwortlich zeichnet. Dieses Modul unterstützt JAXB 1/2, Castor, XMLBeans, JiBX und XStream. Das OXM-Modul ist inzwischen in das Spring Framework übernommen worden und steht somit auch für andere Anwendungen zur Verfügung. Alle Features des Spring Frameworks können hier ebenfalls genutzt werden. Das betrifft nicht nur die Konfiguration und Dependency Injection, sondern auch Sicherheitsaspekte, wenn Sie Spring Security verwenden wollen oder können. Abgerundet wird die Featureliste mit der Unterstützung für WS-Security, wenn Sie die SOAP-Nachrichten ver- und entschlüsseln oder signieren wollen. Die ganze Featureliste wird von den Entwicklern als „Makes the Best www.JAXenter.de Web Services mit Spring WS Enterprise Practice an Easy Practice“ bezeichnet. Die Artikelserie wird zeigen, ob sie Recht behalten. Der erste Teil der Serie enthält die Basics zu Spring WS. Hier werden wir ein kleines Projekt entwickeln, indem Sie einen Web Service implementieren, der die Funktionsweise und Features von Spring WS beleuchtet. Der zweite Teil der Serie knüpft nahtlos an den ersten Teil an und diskutiert die Schwachstellen der Lösung. Danach werden wir gemeinsam eine bessere Lösung entwickeln. Im dritten Teil werden wir den Web Service mit Spring Security etwas sicherer machen, zusätzlich einen Blick auf die Architektur von Spring-WS-Projekten werfen und das Thema Integrationstest betrachten. Doch nun genug geredet, ... ... beginnen wir mit einem Beispielprojekt. Wir wollen in diesem Projekt einen Web Service entwickeln, wie er in Abbildung 1 dargestellt ist. Der ProductService enthält lediglich eine WebService-Methode. Diese findet Produkte anhand der Warengruppe (Category), des Lieferanten (Supplier) und einer frei konfigurierbaren Zeitspanne (DateRange). Das dazu gehörende Domänenmodell ist in Abbildung 2 zu sehen. Die Warengruppe (Category) und der Lieferant (Supplier) haben lediglich eine ID und name-Property. Das gefundene Produkt hält Referenzen auf den Lieferanten und die Warengruppe. Auf diese beiden Re- Abb. 1: Der ProductService Abb. 2: Domänenmodell für den ProductService ferenzen komme ich später noch genauer zu sprechen. Schritt 1: Projekt erstellen Spring WS bietet einen Maven-Archetypen, auf den wir hier aber bewusst verzichten, um die Schritte besser nachvollziehen zu können, die benötigt werden, um ein solches Projekt zu erstellen. Als Erstes brauchen wir ein Eclipse-Projekt. Da dieses später auf einem Tomcat deployt werden soll, bietet sich als Projekttyp ein Dynamisches Webprojekt an. Beenden Sie den Projekt-Wizard nicht mit Finish schon auf der ersten Seite, sondern gehen Sie alle drei Seiten mit Next durch. Auf der zweiten Seite ändern Sie den Pfad zu Contract First vs. Contract Last Es gibt zwei Ansätze, wie Web Services entwickelt werden können. Entweder man beginnt, eine WSDL zu definieren und erzeugt aus dieser den benötigten JavaQuellcode (Contract First), oder man schreibt zuerst den Java-Quellcode und erzeugt daraus eine WSDL (Contract Last). Doch welche Methode ist die bessere? Wie bei Vielem kann man das nicht abschließend beantworten. Doch es gibt einige Argumente für den Contract-FirstAnsatz. Definieren Sie zuerst die WSDL, dann haben Sie viel mehr Steuerungsmöglichkeiten, um die WSDL zu optimieren. Oft referenzieren Klassen ja andere Klassen, die nicht in der WSDL definiert werden sollen. Dies kann besser in einer WSDL umgangen werden. Ein anderer Punkt ist die Interoperabilität. Beschränkt man sich bei den Datentypen auf die Typen, die in einem XML Schema definiert werden können, dann wird es in den wenigsten Fällen zu Problemen in den verschiedenen Sprachen kommen, die den Web Service später konsumieren. Es gibt noch einige andere Punkte zu diesem Thema, die die Spring-WS-Entwickler unter [5] zusammengetragen haben. www.JAXenter.de dem Source-Verzeichnis auf src/main/ java und auf der dritten Seite das Verzeichnis für den Web-Content auf src/ main/web. Zusätzlich brauchen Sie ein weiteres Source-Verzeichnis, das weitere Ressourcen aufnehmen kann. Nennen Sie das Verzeichnis src/main/ resources. Schritt 2: Ein XML Schema erstellen XML Schema? Wofür das denn? Spring WS geht streng nach dem Ansatz Contract-First (Kasten: „Contract-First vs. Contract-Last“) vor. Deswegen benötigen Sie zuerst einen Vertrag. Dieser Vertrag, der die Schnittstelle des Web Service beschreibt, kann eine WSDL sein oder in diesem Fall ein XML Schema. Der Vorteil eines XML Schemas gegenüber einer WSDL ist, dass man sich bei der Definition des Vertrags auf die eigentliche Schnittstelle konzentrieren kann, ohne von den technischen Details einer WSDL abgelenkt zu werden. In Abbildung 3 sehen Sie eine Darstellung des Schemas. Da wir lediglich eine Web-Service-Methode anbieten wollen, brauchen wir auch nur ein Ele- Abb. 3: Schematische Darstellung des verwendeten XMLSchemas javamagazin 3|2010 67 Enterprise Web Services mit Spring WS Abb. 4: Dialog zum Aktivieren des MavenDependencyManagements benötigten Attribute (Abb. 2), und fertig ist das Schema (Listing 1). Das Schema speichern Sie am besten unter src/main/resources/META-INF/ schema/Products.xsd. Schritt 3: Domänenmodell und Serviceklasse implementieren ment. Im Sinne der Namensgebung in WSDLs nennen wir das Element hier ProductRequest. Wie in Abbildung 1 zu sehen ist, benötigt dieser Request drei Listing 1 <?xml version="1.0" encoding="UTF-8"?> <schema targetNamespace="http://www.itemis.de/hoa/ spring/ws/product"elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:p="http://www.itemis.de/hoa/spring/ws/product"> <element name="ProductRequest"> <complexType> <all> <element name="Category" type="p:CategoryType" /> <element name="Supplier" type="p:SupplierType"> </element> <element name="DateRange" type="p:DateRange"> </element> </all> </complexType> </element> <complexType name="CategoryType"> <sequence> <element name="id" type="int"></element> <element name="name" type="string"></element> </sequence> </complexType> <complexType name="SupplierType"> <sequence> <element name="id" type="int"></element> <element name="name" type="string"></element> </sequence> </complexType> <complexType name="DateRange"> <sequence> <element name="startDate" type="date"></element> 68 javamagazin 3|2010 Parameter: Category, Supplier und DateRange. Diese werden in dem Schema als anonyme komplexe Datentypen angelegt. Diese Typen bekommen noch die <element name="endDate" type="date"></element> </sequence> </complexType> </schema> Listing 2 public class ProductService { public List<Product> findProductsForSupplierAndCategory InDateRange( Category category, Supplier supplier, DateRange dateRange) { List<Product> products = new ArrayList<Product>(); products.add(new Product(12345, "Product1", category, supplier)); products.add(new Product(12346, "Product2", category, supplier)); return products; } } Listing 3 <dependencies> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.jdom</groupId> <artifactId>jdom</artifactId> <version>1.1</version> </dependency> </dependencies> Insgesamt müssen Sie dafür fünf Klassen implementieren. Wir verzichten für dieses Beispiel komplett auf Interfaces. Die vier Domänenklassen sind einfache POJOs mit Gettern und Settern. Diese finden Sie natürlich schon implementiert in dem mitgelieferten Beispiel. Die Serviceklasse implementiert die eigentliche Businesslogik. In einem realen Projekt würde die Servicemethode über Datenzugriffskomponenten auf eine Datenbank zugreifen und die in Frage kommenden Produkte dort abfragen. Für dieses Beispiel habe ich es uns etwas einfacher gemacht und gebe immer eine Liste mit zwei Produkten zurück (Listing 2). Schritt 4: Maven konfigurieren Bisher haben wir nur die Bordmittel der JVM verwendet. Der nächste Schritt ist allerdings die Implementierung des Endpoints. Der Endpoint ist die Instanz, die die Anfragen von einem WebService-Konsumenten beantwortet. In diesem Teil verwenden wir JDOM, um die Anfrage zu analysieren und aus dem SOAP-Body die benötigten Daten zu extrahieren. Außerdem greifen wir zum ersten Mal auf Spring-WS-Funktionen zurück. Um die benötigten Bibliotheken in das Projekt zu integrieren, verwenden wir Maven. Dazu aktivieren wir für unser Projekt das Maven Dependency Management (Rechtsklick auf Projekt | Maven | Enable Dependency Management). Daraufhin erscheint ein Dialogfenster, wie in Abbildung 4 zu sehen ist. Tragen Sie hier die gewünschten Daten ein und klicken auf Finish. Um den Endpoint zu implementieren, benötigen wir zuerst nur zwei Abhängigkeiten: ■ spring-ws-core ■ jdom Mit Maven ist es sehr einfach, diese Abhängigkeit hinzuzufügen. Sie brauchen www.JAXenter.de Anzeige Enterprise Web Services mit Spring WS Abb. 5: Typhierarchie des Interfaces PayloadEndpoint nur die pom.xml und in dem POM-Editor die Quellansicht zu öffnen (der rechte Tab mit der Beschriftung pom.xml). Dort fügen Sie den Inhalt aus Listing 3 ein. Nun haben Sie alles beisammen, um mit der Implementierung des Endpoints zu beginnen. Schritt 5: Implementierung des Endpoints Ein Endpoint wird benötigt, damit die Anfragen beantworten werden können. Listing 4 Der Endpoint muss den SOAP-Request verarbeiten können und auch wissen, was er mit den Daten anfangen soll. Damit Spring WS eine Klasse als Endpoint erkennt, muss diese das Interface org. spring framework.ws.server.endpoint. PayloadEndpoint implementieren. Aber Spring wäre nicht Spring (das gilt auch für Spring WS), wenn es da nicht schon was vorbereitet hätte. Es gibt schon einige abstrakte Implementierungen, die Sie direkt verwenden können und die Ihnen viel Arbeit ersparen. Abbildung 5 zeigt die verfügbaren Implementierungen. Da wir für diesen Artikelteil jDOM verwenden wollen, benutzen wir die AbstractJDomPayloadEndpoint-Implementierung. Die einzige Methode, die wir implementieren müssen, ist die protected Element invokeInternal(Element requestElement) throws Exception {}. Listing 4 zeigt eine erste Implementierung unseres Endpoints. Im Konstruktor des Endpoints definieren wir schon die Listing 5 public class ProductServiceEndpoint extends AbstractJDomPayloadEndpoint { private XPath categoryExpression; private Namespace namespace; private XPath supplierExpression; private XPath dateRangeExpression; public ProductServiceEndpoint () throws JDOMException { namespace = Namespace.getNamespace("prod", "http://www.itemis.de/hoa/spring/ws/product"); categoryExpression = XPath.newInstance("//prod:Category"); protected Element invokeInternal(Element requestElement) throws Exception { Category category = null; Supplier supplier = null; DateRange dateRange = null; Element categoryElement = (Element) categoryExpression .selectSingleNode(requestElement); category = new Category(); category.setId(Integer.parseInt (categoryElement.getChildText("id", namespace))); category.setName(categoryElement.getChildText ("name", namespace)); categoryExpression.addNamespace(namespace); supplierExpression = XPath.newInstance("//prod:Supplier"); supplierExpression.addNamespace(namespace); dateRangeExpression = XPath.newInstance ("//prod:DateRange"); dateRangeExpression.addNamespace(namespace); } @Override protected Element invokeInternal(Element requestElement){ return null ; } } 70 javamagazin 3|2010 Element supplierElement = (Element) supplierExpression .selectSingleNode(requestElement); supplier = new Supplier(); supplier.setId(Integer.parseInt (supplierElement.getChildText("id", namespace))); supplier.setName(supplierElement.getChildText("name", namespace)); SimpleDateFormat dateFormat = new SimpleDateFormat ("MM-dd-yy"); Element dateRangeElement = (Element) dateRangeExpression .selectSingleNode(requestElement); dateRange = new DateRange(); dateRange.setStartDate(dateFormat. parse(dateRangeElement.getChildText( "startDate", namespace))); XPath Expressions, mit denen wir später den Body des SOAP-Requests parsen wollen, um an die notwendigen Daten zu kommen. Die Methode invokeInternal bekommt als Parameter den Body des SOAP-Requests. Mit den drei XPath Expressions werden die Elemente für die drei Parameter für die Servicemethode ermittelt. Doch halt! Wie rufen wir diese Servicemethode überhaupt auf? Wir haben noch keine Referenz auf die Serviceklasse. Das erledigen wir schnell, indem wir ein Feld und eine zugehörige Setter-Methode für den ProductService definieren. Nachdem dies erledigt ist, können wir uns der Implementierung der invokeInternal-Methode widmen. Der eigentliche Serviceaufruf geschieht mit Java-Mitteln, d. h. aus den Daten des SOAP-Bodys müssen die entsprechenden Objekte erzeugt werden. In Listing 5 finden Sie eine mögliche Lösung dieser Aufgabe. 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<Product> 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 jDOMElement als Rückgabe liefert, muss der ganze Objektbaum, der an dem Product hängt, in XML bereitgestellt werden. Diesen Vorgang nennt man www.JAXenter.de Web Services mit Spring WS Enterprise 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: 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 ProductServiceund die ProductServiceEndpoint-Klasse. Der ProductServiceEndpoint hält eine Referenz auf den ProductService. Somit muss dies ebenfalls in der SpringKonfiguration abgebildet werden (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 in- jiziert 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 WSDLGenerierung 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). In Listing 9 sehen Sie die Konfiguration für die automatische WSDLGenerierung. Über die Properties portTypeName und locationURI können Sie die generierte WSDL beeinflussen. Im nächsten Schritt sehen Sie noch, wie die void marshal(Object graph, javax.xml.transform. Result result) throws XmlMappingException, IOException; Listing 6 Listing 8 protected Element invokeInternal(Element requestElement) <bean class="org.springframework...mapping. PayloadRootQNameEndpointMapping"> <property name="mappings"> <props> <prop key="{http://www.itemis.de/hoa/spring/ws/product} ProductRequest"> ProductServiceEndpoint</prop> </props> </property> <property name="interceptors"> <bean class="org.springframework...interceptor. PayloadLoggingInterceptor"/> </property> throws Exception { Um diese Methode aufzurufen, brauchen wir lediglich das Objekt, das in XML bereitgestellt werden soll und eine Implementierung des Interface javax. xml.transform.Result. jDOM liefert bereits eine passende Implementierung dieses Interface, sodass wir die Methode nur um einige Zeilen erweitern müssen (Listing 6). 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. Die vollständige pom.xml kann auf der Heft-CD eingesehen 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 www.JAXenter.de … JDOMResult result = new JDOMResult(); marshaller.marshal(products, result); return (Element) result.getResult().get(0); } Listing 7 <beans xmlns="http://www.springframework.org/schema/ beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/ schema/beans http://www.springframework.org/schema/ beans/spring-beans-2.5.xsd"> <bean id="ProductService" class="de.itemis.hoa.spring.ws.service.ProductService"/> <bean id="ProductServiceEndpoint" class="de.itemis.hoa.spring.ws.endpoint. ProductServiceEndpoint"> <property name="productService" ref="ProductService"/> <property name="marshaller" ref="castorMarshaller"/> </bean> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> </beans> </bean> Listing 9 <bean id="product" class="org.springframework.ws.wsdl.wsdl11. DefaultWsdl11Definition"> <property name="schema" ref="schema" /> <property name="portTypeName" value="ProductResource" /> <property name="locationUri" value="/productService/" /> <property name="targetNamespace" value="http://www.itemis.de/hoa/spring/ws/product/ definitions" /> </bean> <bean id="schema" class="org.springframework.xml.xsd. SimpleXsdSchema"> <property name="xsd" value="classpath:META-INF/schema/ Product.xsd" /> </bean> javamagazin 3|2010 71 Enterprise Web Services mit Spring WS locationURI an die wirkliche Serverumgebung angepasst werden kann. Die Klasse DefaultWsdl11Definition bietet noch einige andere Konfigurationsparameter, die Sie im entsprechenden Javadoc nachlesen können. Schritt 7: Konfiguration des Web Descriptors Anwendung enthält noch keinen Clientcode. Wir können den Web Service nicht ausprobieren. Jetzt kommt SoapUI [6] ins Spiel. SoapUI ist ein Web-Service-Testing-Tool. Damit können SOAP-Requests abgesetzt werden und genau diese Funktion verwenden wir auch, um den Web Service aufzurufen. Im Beispielprojekt finden Sie ein Verzeichnis soapui. Die dort enthaltene XML-Datei ist ein SoapUI-Projekt. Wenn Sie dieses Projekt öffnen, finden Sie links im Navigator einen vorbereiteten Request. Diesen können Sie ausführen und erhalten einen Response mit zwei Products. Mit dem Testing-Tool SoapUI können SOAPRequests abgesetzt werden. 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 vo- Listing 10 <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Product Catalogue Service</display-name> rangegangenen 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. Schritt 8: Starten der Webanwendung und Testen des Web Service mit SoapUI In den letzten 7 Schritten haben wir eine Anwendung erstellt, die einen Web Service zur Verfügung stellt. Der letzte Teil unseres Vorhabens besteht darin, die Anwendung auf einem Tomcat zu deployen. Mit Eclipse geht das sehr einfach mit Run as... | Run On Server. Die Server-Runtime haben Sie bereits bei der Erstellung des Projekts festgelegt. Die Anwendung wird automatisch auf dem Server deployt und gestartet. Nach wenigen Sekunden ist das erledigt. Ein Aufruf des URLs http://localhost:8080/ articles-spring-ws-dom/productService/ product.wsdl zeigt die von Spring WS generierte WSDL. Doch was nun? Die Zusammenfassung In diesem ersten Teil der Artikelserie haben wir bereits einen funktionsfähigen Web Service erstellt. Allerdings sind viele Dinge noch nicht optimal. Zum einen haben wir noch keinen richtigen Client. Zum anderen arbeiten wir direkt auf dem SOAP-Body. Das ist bei größeren Requests nicht zielführend. Viel lieber wollen wir direkt mit JavaObjekten arbeiten. Deswegen werden wir im nächsten Teil den Web Service auf JAXB 2 umstellen und einen Client implementieren, sodass wir SoapUI nicht mehr für das Testen der Anwendung brauchen. <servlet> <servlet-name>spring-ws</servlet-name> <servlet-class> org.springframework.ws.transport.http.MessageDispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:META-INF/spring/spring-ws-servlet.xml </param-value> Thorsten Kamann ist als Softwarearchitekt und als Coach bei itemis tätig. Seine Schwerpunkte sind webbasierte Technologien, MDSD, leichtgewichtige und flexible Architekturen und Open Source. Er ist Project Lead bei der Fornax Platform, einer Plattform für die Entwicklung von MDSD-related Tools und Groovy Commiter. Darüber hinaus schreibt er Bücher, veröffentlicht regelmäßig Artikel in Fachmagazinen und hält Vorträge auf Fachkonferenzen zu obigen Themen. </init-param> <init-param> <param-name>transformWsdlLocations</param-name> <param-value>true</param-value> Links & Literatur </init-param> [1] http://www.springsource.org/ </servlet> [2] http://ws.apache.org/axis/ <servlet-mapping> <servlet-name>spring-ws</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> [3] http://cxf.apache.org/ [4] http://static.springsource.org/spring-ws/sites/1.5/ [5] http://static.springsource.org/spring-ws/sites/1.5/reference/html/ why-contract-first.html [6] http://www.soapui.org/ [7] http://www.thorsten-kamann.de/ 72 javamagazin 3|2010 www.JAXenter.de