Web Services mit Groovy und JAX-WS

Die dynamische Skriptsprache Groovy bietet eine starke Integration in die Java Plattform. Java Bibliotheken und Server lassen sich dank der Integration auch in Groovy Skripten verwenden. Dieser Artikel beschreibt, wie man mit Groovy JAX-WS Web Services implementieren kann. Ausser der Groovy Installation und Java ab der Version 6 ist keine weitere Software mehr notwendig.

Das in diesem Artikel vorgestellte Skript implementiert einen Service, der eine Operation zum Anlegen von Büchern anbietet. Diese Operation nimmt als Parameter ein Java Bean von Typ Book entgegen. Listing 1 zeigt die Service Klasse.

@WebService (targetNamespace="http://predic8.com/groovy-jax/")
@SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.BARE)
class BookService{
	@WebMethod
	public void add(Book book){
		println "Name des Buchs: ${book.name}"
	}
}
Listing 1: Service Klasse

Die Klasse BookService ist mit der JWS @WebService Annotation versehen um die Klasse als Service Implementation Bean kenntlich zu machen. Der targetNamespace muss angegeben werden, falls die Klasse im Defaultnamespace, das heißt ohne package Deklaration erstellt wurde. Auf die @WebMethod Annotation kann nicht verzichtet werden. Ohne diese Annotation wird jede öffentliche Methode der Service Klasse als Operation angeboten. Da BookService wie jede andere Groovy Klasse von GroovyObject erbt und somit die öffentliche Methode invokeMethod beinhaltet, versucht JAX-WS auch diese Methode als Operation anzubieten, was jedoch mit der @WebMethod Annotation verhindert wird. So werden nur die markierten Methoden veröffentlicht. Die in Java ab der Version 6 enthaltene JAX-WS Implementierung erfordert, dass portable Artifakte für die in einer Web Service Schnittstelle enthaltenen DatenTypen vorhanden sind. Die portablen Artifakte können aus den annotierten Klassen mit dem Tool wsgen erzeugt werden. Für unser Beispiel haben wir uns jedoch für den Parameter Style BARE entschieden, der auf Wrapper Beans für die Parameter verzichtet. Daher wird für das Beispiel keinen Aufruf von wsgen und auch kein Buildskript benötigt. Beim Parameter Style BARE hat eine Operation maximal einen Parameter. Dieser eine Parameter muss zusätzlich ein Bean sein. Wird anstatt der JAX-WS Referenzimplementierung Apache CXF verwendet, so entfällt die Einschränkung auf dem Parameterstyle BARE. CXF benötigt keine portable Artifakte. Alles was CXF für eine beliebige Signatur einer Operation benötigt wird On the fly erzeugt. Listing 2 zeigt den Code des Groovy Beans Book.

@XmlAccessorType(XmlAccessType.FIELD)
class Book {
	String name
	String author
}
Listing 2: Groovy Bean Book

Wichtig ist die JAXB Annotation @XmlAcessorType(XmlAccessType.FIELD).
Die @XmlAccessorTyp Annotation veranlasst JAX-WS für die Serialisierung des Beans die Felder der Klasse und nicht die Properties zu verwenden. Ohne diese Annotation würde beim Starten des Web Services ein Fehler verursacht werden. In Groovy erbt jede Klasse von GroovyObject. GroovyObject besitzt eine Property metaClass, welche nicht serialisiert werden kann. Mit dem AccessType Field wird die Serialisierung auf Felder in unserem Beispiel auf name und author beschränkt. Jetzt kann der Service gestartet werden. Über die statische Methode publish der Klasse Endpoint geht das ohne Web Anwendung und Web Server. Im Hintergrund wird natürlich ein kleiner Standalone Server gestartet, der in Java SDK enthalten ist:


Endpoint.publish("http://localhost:9000/book", new BookService())


Fazit

Wie aus Listing 3 ersichtlich kann mit ein paar Zeilen Groovy Code ohne Code Generierung ein Web Service erstellt werden. Verwendet man anstatt der JAX-WS Implementierung des SDKs Apache CXF, indem man die CXF Bibliotheken in dem Classpath aufnimmt, so entfällt auch die Einschränkung auf dem BARE Style. Dem Skripting von Web Services steht jetzt nichts mehr im Wege!

import javax.jws.soap.*
import javax.jws.*
import javax.xml.ws.*
import javax.xml.bind.annotation.*

@XmlAccessorType(XmlAccessType.FIELD)
class Book {
	String name
	String author
}

@WebService (targetNamespace="http://predic8.com/groovy-jax/")
@SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.BARE)
class BookService{
	@WebMethod
	public void add(Book book){
		println "Name des Buchs: ${book.name}"
	}
}

Endpoint.publish("http://localhost:9000/book", new BookService())
Listing 3: Komplettes Beispiel