In einem meiner früheren Artikel habe ich erläutert wie man mit Hilfe von Annotations unter Verwendung von JSR-181, JAXB sowie Apache CXF und Spring Framework ein Webservice implementieren kann. Dieser Artikel beschreibt wie man die Generierung der WSDL Datei mit Annotations beeinflussen und somit den Webservice Contract optimieren kann.
Update: Siehe auch Artikel zur WSDL-First Development.
Die Beispiele in diesem Tutorial basieren auf den Source Code aus dem vorangegangem Artikel. Der komplette Source Code aus diesem Artikel inklusive einem Maven Build Script steht zum Download bereit.
Schritt 1: Legen Sie den Namespace fest.
Bei der Generierung der WSDL-Datei verwendet Apache CXF den Java Package Namen als Standard-Namespace. Aus der Klasse Customer, die im Package com.mycompany.customerrelations liegt, wird standardmäßig das Element Customer im Namespace http://customerrelations.mycompany.com/ generiert. Oft ist es jedoch gewünscht dieses Verhalten zu verändern und z.B. zuerst den Domainnamen im Namespace zu verwenden. Der Namespache der Elemente lässt sich über die Annotations @XmlSchema und @WebService verändern. Dabei wird @XmlSchema für Datenstrukturen und @WebService für Service-Interfaces verwendet. Hier ein Beispiel:
@XmlSchema(namespace="http://www.mycompany.com/customerrelations")
@WebService(targetNamespace="http://www.mycompany.com/customerrelations", name="CustomerService")
Schritt 2: Definieren Sie die SOAP-Action für alle Operationen.
Anhand der SOAP-Action Property im Transport-Protokoll wird definiert wie eine SOAP-Nachricht interpretiert werden sollte bzw. welche Operation aufgerufen werden sollte. Apache CXF setzt standardmäßig den SOAP-Action Namen auf einen leeren String, also z.B: <soap:operation soapAction=""/>. Dies hat zur Folge dass die Bedeutung der SOAP-Nachricht anhand von HTTP Request URL interpretiert wird. Bei vielen Protokollen wie z.B. JMS oder SMTP ist diese Information jedoch nicht vorhanden und sollte daher im Header des Transport-Protokolls übergeben werden. Definieren Sie daher für jede Operation den SOAP-Action Namen. Verwenden Sie dazu die Annotation @WebMethod wie z.B.: @WebMethod(action = "getCustomer").
Schritt 3: Bestimmen Sie den Namen für alle Input-Parameter.
In Java kann bei einer kompilierten Klasse der Name eines Parameters aus der Signatur einer Methode nicht bestimmt werden. So ist es zwar möglich den Datentyp und die Reihenfolge der Parameter auszulesen, nicht jedoch den Namen des Parameters. Bei der Generierung der WSDL-Datei verwendet Apache CXF daher Parameter-Namen wie z.B. “arg0″. Für die Methode “public Customer getCustomer(String customerNumber)” im Service-Interface CustomerService wird z.B. folgende Schema-Definition generiert:
<xs:complexType name="getCustomer">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
Um die SOAP-Nachricht besser lesbar zu machen, sollten daher alle Input-Parameter mit einem Namen versehen werden. Dies kann mit der Annotation @WebParam erfolgen. Hier ein Beispiel:
@WebParam(targetNamespace="", name="customerNummber")
Schritt 4: Legen Sie den Namen für alle Output-Parameter fest.
Das Problem bei Input-Parametern (siehe Schritt 3) gilt natürlich auch für Output-Parameter. Der Name des Parameters lässt sich nicht aus der Java Klasse ermitteln. Apache CXF generiert in diesem Fall bei Response-Nachricht ein Element mit dem Namen “return”. Hier ein Auszug aus der WSDL-Datei:
<xs:complexType name="getCustomerResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="tns:customer"/>
</xs:sequence>
</xs:complexType>
Die SOAP-Nachrichten sind in diesem Fall natürlich nicht besondern gut lesbar. Verbessern kann man dies mit der Annotation @WebResult. Dies könnte beispielweise so aussehen:
@WebResult(targetNamespace="", name="customers")
Schritt 5: Bestimmen Sie die Pflichtfelder.
Apache CXF definiert standardmäßig alle Elemente in der WSDL-Datei als optional, also z.B.:
<xs:element minOccurs=”0″ name=”lastname” type=”xs:string”/>
Ein guter Webservice Contract sollte jedoch unbedingt notwendige Datenelemente wie z.B. den Nachnamen bei einer Person als Pflichtfeld definieren. Dies kann über die Annotation @XmlElement beeinflusst werden. Ein Beispiel:
@XmlElement(required=true)
Schritt 6: Legen Sie fest, welche Datenelemente “nullable” sind.
In manchen Fällen ist es notwendig Null-Werte für bestimmte Elemente zu übermitteln. Wenn für den Vorname einer Person auch ein Null-Wert übermittelt werden darf, so könnte dies in der SOAP-Nachricht so aussehen:
<firstname xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
Standardmäßig generiert Apache CXF die WSDL-Datei, dass Null-Werte nicht erlaubt sind. Für Datenelemente für die auch Null-Werte zulässig sind, muss dies daher expliziet definiert werden. Dies kann ebenfalls über die Annotation @XmlElement definiert werden. Hier ein Beispiel:
@XmlElement(required=true, nillable=true)
Schritt 7: Definieren Sie den Datentyp für das XML-Schema.
Bei der Generierung der WSDL-Datei werden Java Datentypen auf bestimmte XML-Schema Datentypen gemappt. Der Java-Datentyp java.lang.String wird beispielweise auf “xsd:string” gemappt. In manchen Fällen ist dieses Mapping jedoch nicht ganz sinnvoll. Der Nachname einer Person sollte beispielweise kein “Carriage Return” oder “Tab” als Zeichen enthalten. Hier wäre der XML-Schema Datentyp “token” die bessere Wahl.
Der Java-Datentyp java.util.Calendar wird standardmäßig auf den XML-Schema Datentyp “xsd:dateTime” gemappt. Beim Geburtsdatum einer Person ist jedoch in der Regel nur das Datum und nicht die Zeit von Bedeutung. Hier wäre der XML-Schema Datentyp “xsd:date” sinnvoller. Der XML-Schema Datentyp kann über die Annotation @XMLSchemaType festgelegt werden. Für das Geburtsdatum einer Person könnte dies folgendermaßen aussehen:
@XmlSchemaType(name="date")
Schritt 8: Legen Sie den Type-Adapter fest.
In manchen Fällen ist es notwendig den Marshalling-Prozess von Datenstrukturen zu beeinflussen. Dies ist mit der Annotation @XmlJavaTypeAdapter und mit der Implementierung eines XMLAdapters möglich. Siehe dazu auch den Artikel in Kohsuke Kawaguchi’s Blog.
Hat man sich beispielsweise entschieden den XML-Schema Datentyp “xsd:token” für ein Datenelement zu verwenden, so sollte der CollapsedStringAdapter für die Deserialisierung des Strings verwendet werden. Hier ein Beispiel dazu:
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
Schritt 9: Legen Sie die ID bei referenzierten Objekten fest.
Datenelemente über die auf bestimmte Datenstrukturen referenziert werden kann wie z.B. die Kundennummer eines Kunden sollte den XML-Schema Datentyp “xsd:ID” erhalten. Verwenden Sie zu diesem Zweck die Annotation @XmlID.
Schritt 10: Bestimmen Sie die Reihenfolge der Elemente.
Apache CXF definiert standardmäßig die Elemente im XML-Schema sortiert in alphabetischer Reihenfolge. Für die Datenstruktur “Adresse” mit den Datenelementen “line1″, “line2″, “postalCode”, “city” und “country” würde dies bedeuten, dass das Element “city” in der SOAP-Nachricht als erstes Element vor allen anderen Elementen übertragen wird. Die SOAP-Nachricht ist somit schlechter lesbar.
Die Reihenfolge der Datenelemente kann über die Annotation @XmlType beeinflusst werden. Für die Datenstruktur einer Adresse könnte dies beispielweise so aussehen:
@XmlType(name = "Address", propOrder = {
"line1", "line2", postalCode", city", country")
Abschließende Gedanken
Wenn Sie alle 10 Schritte auf Ihren Webservice angewandt haben, so werden Sie mit Apache CXF eine relativ qualitativ hochwertige WSDL-Datei generieren können. Wünschenswert wäre es die XML-Schema Definition für die Datenstrukturen noch detaillierter festlegen zu können und z.B. die minimale oder maximale Länge von Texten mit “minLength” und “maxLength” anzugeben. Nach meinem aktuellen Wissensstand ist dies jedoch mit Annotations momentan noch nicht möglich. Auch die Einschränkung eines String mit Hilfe von Regulären Ausdrücken funktioniert momentan noch nicht über Annotations. Dies spricht gegen die Implementierung von Webservices nach dem “Java Code First”-Ansatz. Ich bin jedoch zuversichtlich dass diese Funktionen bald über den JSR-Prozess spezifiziert und demnächst auch von Apache CXF implementiert werden.
Haben Sie Feedback oder weitere Vorschläge? Ich freue mich über Ihre Kommentare.



Letzte Kommentare