In einem meiner früheren Artikel habe ich beschrieben, wie man mit Apache CXF, Spring Framework sowie Jetty als embedded HTTP Server einen einfachen Webservice implementiert und bereitstellen kann. Dabei wurden JSR-181 und JAXB Annotations verwendet um die Java Klassen mit entsprechenden Metadaten anzureichern. Dies wird auch als „Implementation-First“ oder „Java-First“ Ansatz bezeichnet. In einem weiteren Artikel wurde gezeigt welche Nachteile dieser Ansatz hat und wie man die Generierung der WSDL-Datei mit Annotations beeinflussen und somit den Webservice Contract optimieren kann.
Dieser Artikel beschreibt das Vorgehen nach dem “Contract-First” bzw. „WSDL-First“ Ansatz. Bei diesem Ansatz wird die Schnittstelle vor der Implementierung in Form einer WSDL-Datei definiert. Die entsprechenden Java Klassen werden dann mit einem Codegenerator aus der WSDL-Datei generiert.
Das hier beschriebene Beispiel basiert auf Apache CXF 2.0.3, JSR-181, JAXB 2.1, Spring Framework 2.5, sowie Jetty 6.1.5. Der komplette Source Code inklusive einem Maven Build Script steht zum Download bereit.
Schritt 1: Anforderungen aufnehmen, dokumentieren und Schnittstelle entwerfen
Der Lebenszyklus für jeden Webservice beginnt mit neuen Anforderungen aus dem Fachbereich. Diese Anforderungen müssen wie bei jedem Softwareentwicklungsprojekt aufgenommen und dokumentiert werden. Anhand dieser Anforderungen kann anschließend eine Schnittstelle für den Service entworfen werden. In unserem Beispiel verwenden wir die Schnittstelle aus dem vorherigen Artikel. Das folgende UML-Diagramm zeigt die Schnittstelle des Services sowie die Datenobjekte mit denen der Service arbeitet:
Wie man dem Klassendiagramm entnehmen kann, soll unser Beispiel-Service eine Methode anbieten, die eine Kundenummer als Inputparameter entgegennimmt und einen Kunden als Output zurück gibt. Die Serviceschnittstelle wurde für dieses Beispiel zur besseren Verständlichkeit natürlich stark vereinfacht.
Schritt 2: Datenstrukturen als XML-Schema definieren
Zunächst müssen die Datenstrukturen als XML-Schema definiert werden. Es ist sinnvoll jede Datenstruktur in einer separaten XSD-Datei abzuspeichern. Auf diese Weise können die Datenstrukturen auch in anderen Services wiederverwendet werden. In unserem Beispiel entstehen folgende vier Schema-Definitionen:
Customer.xsd
Person.xsd
Gender.xsd
Address.xsd
Schritt 3: Service Interface in einer WSDL-Datei definieren
Im nächsten Schritt muss das Service Interface in einer WSDL-Datei beschrieben werden. Die WSDL-Datei inkludiert die XML-Schema Dateien, die die Datenstrukturen beschreiben und definiert die vorhanden Operationen sowie die Input- und Output-Nachrichten:
Schritt 4: Java Source Code aus WSDL-Datei definieren
Aus der WSDL-Datei kann nun mit Hilfe eines Genarators Java Code generiert werden. Apache CXF bittet dazu ein entsprechendes WSDL2Java-Tool an, welches sowohl von der Kommandozeile als auch aus Ant und Maven Skripten aufgerufen werden kann. In unserem Beispiel verwenden wir ein Maven Build-Skript und der Codegenerator wird automatisch vor dem Kompilieren aufgerufen. Die Konfiguration für den Codegenerator kann der pom.xml Datei entnommen werden.
Schritt 5: Service Interface implementieren
Nachdem die entsprechenden Java-Klassen für die Datenstrukturen und den Service generiert wurden, muss das Interface implementiert werden. Die Implementierung der Geschäftslogik befindet sich in der Klasse CustomerServiceImpl.java, welche das generierte Interface ContactService implementiert:
ArrayWie man dem Source-Code entnehmen kann, gibt diese Implementierung immer den Kunden „Max Müller“ als Output zurück wenn die Kundennummer „12345“ als Inputparameter übergeben wurde und in allen anderen Fällen wird ein BusinessLogicException mit entsprechender Fehlermeldung geworfen.
Schritt 6: Service bereitstellen
In diesem Beispiel wird der Service mit Hilfe von Jetty über HTTP bereitgestellt. Die Konfiguration der Komponenten erfolgt über das Spring Framework. Die Spring Konfiguration ist in diesem Beispiel in der Datei server-applicationContext.xml enthalten.
Über die main-Methode in der Klasse ServiceServer wird der Spring Context initialisiert und somit der Jetty Webcontainer gestartet, welcher die Service Implementierung unter http://localhost:9090/customerService verfügbar macht.
Schritt 7: Client für den Service implementieren
Um den Service anzusprechen und testen zu können, empfiehlt es sich ein Client zu implementieren. Die Implementierung von Clients ist mit Apache CXF 2.0 relativ einfach deklarativ mit Hilfe des Spring Frameworks möglich. Die Spring Konfiguration für den Service Client ist in diesem Beispiel in der Datei client-applicationContext.xml enthalten.
Die Klasse ServiceClient enthält eine main-Methode in der Spring Context initialisiert wird und auf das entsprechende Client-Bean zugegriffen wird.
Alternativ eignet sich auch soapUI hervorragend zum Testen von Webservices.
Ähnliche Artikel:






11.1.2008 um 17:01:38
Ich entwickle mit der gleichen Infrastruktur und bin dabei auf ein Problem gestossen. Vielleicht hat ja einer der hier lesenden eine Idee….
Es geht darum, einen WebService mittels CXF anzubieten. Die Schnittstelle des WebServices soll dabei nur mittels Interfaces in einem eigenen Projekt definiert werden. Diese kann ich dem Client mit einer Spring-Konfiguration zur Verfügung stellen und er kann sie problemlos in seinem eigenen Springcontext verwenden.
Soweit das nur den WebService betrifft, ist das kein Problem. Wenn ich allerdings ein komplexes Objekt definiere bekomme ich Probleme mit JAXB, welches in dieser Situation keine Interfaces zuläßt.
Eine Möglichkeit wäre es, auf Aegis umzusteigen. Dann funktioniert die Kommunikation problemlos. Allerdings muss der Client auch zwingend Aegis verwenden, ansonsten gibt es einen Fehler. Ich würde mich gerne an den Standard (JAXWS) halten.
Ich habe mir jetzt auch versucht mit einem @XmlJavaTypeAdapter die Situation zu umgehen, aber irgendwie geht CXF bzw. JAXB nicht auf meine Konfiguration ein.
Wie könnte ich die Problematik lösen?
Vielen Dank,
Marc
27.4.2008 um 17:04:20
Damit CXF auch wirklich die handgeschriebene Webservicebeschreibung verwendet und nicht sie nicht nochmal u.U. fehlerhaft neu generiert, kann man schreiben:
29.4.2008 um 09:04:03
Das XML-Dokument im vorherigen Post ist “verschluckt” worden. Hier nochmal:
<jaxws:endpoint id=”customerService”
implementor=”com.mycompany.customerrelations.CustomerServiceImpl” address=”http://localhost:9090/customerService”
wsdlLocation=”wsdl/CustomerService.wsdl”
xmlns:ns1=”http://www.mycompany.com/customerrelations”
serviceName=”ns1:CustomerServiceService” />
5.9.2008 um 00:09:43
Ich finde das Beispiel sehr transparent und leicht umzusetzen.
Verwende CXF in Zusammenhang mit Camel und ESB’s und
bin sehr überzeugt davon.
Viele Grüße
Rafael Sobek
28.8.2009 um 16:08:32
Die Methode aus dem Beispiel liefert immer eine Exception:
public Customer getCustomer(String customerNumber) {
Customer result = null;
if(”12345″.equals(customerNumber)) {
Person maxMueller = new Person();
…..
maxMuellerCustomer.setAddress(maxMuellerAddress);
result = maxMuellerCustomer;
}
throw new RuntimeException(”No such customer”);
}