November 14, 2010

Groovy Goodness: Use GroovyWS to Access SOAP Web Services (Part 2)

In a previous post we learned how to create a SOAP web service with the XFire plugin in Grails. The generated WSDL defined that the properties of our objects could be null by default and the minimum occurence is 0. And because of this we must work with JAXBElement objects to get the real object values. In this post we change the property mapping so the properties cannot be null and have a minimum occurence of 1. And then we can change our client code and deal with the object values directly instead of via a JAXBElement object.

Grails SOAP web service

We can change the default mapping of our objects that is generated by the XFire plugin. We must add an Aegis mapping XML file to our classpath and in this file we can define the custom mapping we want to apply. The name of the file is <ClassName>.aegis.xml and must be in the same package as the class we are writing the custom mapping for. If we add the XML files to the src/java directory of our Grails application then the files will be copied to the classpath automatically.

After we have created the XML mapping files we must add an extra runtime dependency. So we change the grails-app/conf/BuildConfig.groovy file and add a dependency to Jaxen, because this is needed to make the custom mapping work.

<!-- File: src/java/com/mrhaki/groovyws/server/Author.aegis.xml -->
        <property name="name" minOccurs="1" nillable="false"/>
        <property name="blogItems" minOccurs="1" componentType="com.mrhaki.groovyws.server.BlogItem"/>
<!-- File: src/java/com/mrhaki/groovyws/server/BlogItem.aegis.xml -->
        <property name="title" minOccurs="1" nillable="false"/>
        <property name="text" minOccurs="1" nillable="false"/>
<!-- File: src/java/com/mrhaki/groovyws/server/SearchRequest.aegis.xml -->
        <property name="authorName" minOccurs="1" nillable="false"/>
// File: grails-app/conf/BuildConfig.groovy
grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
grails.project.dependency.resolution = {
    inherits("global") {
    log "warn"
    repositories {
    dependencies {
        runtime('jaxen:jaxen:1.1.1') {
            transitive = false

We can run our Grails application with $ grails run-app and open the URL http://localhost:8080/server/services/blog?wsdl to see the changes in the generated WSDL file.

GroovyWS Client

Because the definition of our SOAP web service is changed we can also change the client code. We now no longer have use JAXBElement objects, so our code is much cleaner. We can access the object types directly. For example for the dynamic SearchRequest object we can set the authorName property directly. In our old client code we had to create a JAXBElement object to set the value.

package com.mrhaki.groovyws.client

import groovyx.net.ws.WSClient

class BlogWSClient {

    String wsdlUrl

    def proxy

    def findAuthor(String name) {
        def searchRequest = createSearchRequest(name)
        proxy.findAuthor searchRequest

    private def createSearchRequest(String name) {
        def searchRequest = proxy.create('com.mrhaki.groovyws.server.SearchRequest')
        searchRequest.authorName = name

    private void createProxy() {
        if (!proxy) {
            proxy = new WSClient(wsdlUrl, this.class.classLoader)

package com.mrhaki.groovyws.client

import spock.lang.Specification

class BlogWSClientSpec extends Specification {

    def client = new BlogWSClient(wsdlUrl: 'http://localhost:8080/server/services/blog?wsdl')

    def "get author with name mrhaki and two blog items"() {
        def author = client.findAuthor('mrhaki')

        'mrhaki' == author.name
        def arrayOfBlogItems = author.blogItems
        def blogItems = arrayOfBlogItems.blogItem
        2 == blogItems.size()
        'Title1' == blogItems[0].title
        'Title2' == blogItems[1].title
        'Sample blogitem one.' == blogItems[0].text
        'Sample blogitem two.' == blogItems[1].text


In the next post we see how we can add logging interceptors to our client so we can see the input and output from the invocation of the SOAP web service.