GroovyWS

Module Overview

GroovyWS is taking over GroovySOAP as CXF replaces XFire. The major difference here is that GroovyWS is using Java5 so if you need to stick to 1.4 please continue to use GroovySOAP.

Warning: Most of the documentation is adapted from the former GroovySOAP documentation and will improve in the future. I tried to make it as accurate as possible but feel free to report any error.

Warning: GroovyWS is Java5 dependent (due to CXF) and has been tested using groovy-1.5.

Warning: In order to use GroovyWS, you must ensure that GroovySOAP is not in your classpath (~/.groovy/lib) 

Download

Distributions

GroovyWS is available in two packages:

  • as a full jar containing all CXF JARs and dependencies. It is located here
  • as a jar containing all except the jetty server and servlet files. It aimed at being deployed into third party containers. It is located here

Installing

You just need to place the above mentioned JAR file in your ${user.home}/.groovy/lib directory.

Pre-requisites

As the pre-requisites are quite tricky to handle, an all-in-one JAR file is distributed  

Documentation

Getting Started

The Server

You can develop your web service using a groovy script and/or a groovy class. The following two groovy files are valid for building a web-service.

  1. MathService.groovy
    class MathService {
        double add(double arg0, double arg1) {
          return (arg0 + arg1)
        }
        double square(double arg0) {
          return (arg0 * arg0)
        }
    }
  2. Then the easy part ... no need for comments
    import groovyx.net.ws.WSServer
    
    def server = new WSServer()
    
    server.setNode("MathService", "http://localhost:6980/MathService")

    That's all !

The Client

  1. Oh ... you want to test it ... two more lines.
    import groovyx.net.ws.WSClient
    
    def proxy = new WSClient("http://localhost:6980/MathService?wsdl", this.class.classLoader)
    
    def result = proxy.add(1.0 as double, 2.0 as double)
    assert (result == 3.0)
    
    result = proxy.square(3.0 as double)
    assert (result == 9.0)
  2. You're done!

Complex types

The Server

Let say we have a server that manage a library in which you can add a book, find a book and get all the books. The server code will probably look like this

  1. BookService.groovy
    class BookService {
    
      private List allBooks = new ArrayList()
    
      Book findBook(String isbn) {
        for (book in allBooks) {
          if (book.isbn == isbn) return book
        }
        return null
      }
    
      void addBook(Book book) {
        allBooks.add(book)
      }
    
      Book[] getBooks() {
        return (Book[])allBooks.toArray(new Book[allBooks.size()])
      }
    
    }

  2. with the class Book being something like that.
    Book.groovy
    class Book {
      String author
      String title
      String isbn
    }

    To ignore the metaClass property a custom type mapping must be defined (for details refer to Aegis Binding).
    Book.aegis.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <mappings xmlns:sample="http://DefaultNamespace">
      <mapping name="sample:Book">
        <property name="metaClass" ignore="true"/>
      </mapping>
    </mappings>

    However, if you compile custom data types from Java the bytecode won't contain a metaClass property and, hence, there is no need to define a custom mapping.

The Client

The good think here is that the client does not have to know about the Book class. It is automatically generated during the proxy creation time and can be used by your client. Here it is located in the defaultnamespace package since no package was used on the server side.

Here is how the client looks like now:

import groovyx.net.ws.WSClient

def proxy = new WSClient("http://localhost:6981/BookService?wsdl", this.class.classLoader)

def books =  proxy.getBooks()

for (book in books) println book

def book = proxy.create("defaultnamespace.Book")

book.title = "Groovy in Action"
book.author = "Dierk"
book.isbn = "123"

proxy.addBook(book)

def bks =  proxy.getBooks()

println bks.books[0].isbn

Iterating over the books is slightly more complicated since SOAP wrap the arrays in an element (in our case ArrayOfBook). Therefore you have to extract a field from that element. In our case:

def aob =  proxy.getBooks()

for (book in aob.books) println book.name

More Information

Using proxies

If you are using a proxy for accessing internet, you can use the following environment variables to get rid of it:

  • proxyHost
  • proxyPort
  • proxy.user
  • proxy.password

Using basic authentication

If your server use basic authentication, you need to set up the following properties:

  • http.user
  • http.password

TODO

 If you have ideas how the API should look, do not hesitate to post on the user list ;-

Known problems (and work around)

You will not be able to create a proxy directly for a service that requires a username and password to fetch the WSDL. One alternate way is to download the WSDL (using wget or curl with the username and password set) and to create the proxy using the local file.

Demos with public web services

Currency rate calculator

There exist a lot of web-services available for testing. One which is pretty easy to evaluate is the currency rate calculator from webservicex.net.
Here is a small swing sample that demonstrate the use of the service. Enjoy !

import groovy.swing.SwingBuilder
import groovyx.net.ws.WSClient

proxy = new WSClient("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL", this.class.classLoader)

def currency = ['USD', 'EUR', 'CAD', 'GBP', 'AUD', 'SGD']
def rate = 0.0

swing = new SwingBuilder()

refresh = swing.action(
  name:'Refresh',
  closure:this.&refreshText,
  mnemonic:'R'
)

frame = swing.frame(title:'Currency Demo') {
  panel {
    label 'Currency rate from '
    comboBox(id:'from', items:currency)
    label ' to '
    comboBox(id:'to', items:currency)
    label ' is '
    textField(id:'currency', columns:10, rate.toString())
    button(text:'Go !', action:refresh)
  }
}
frame.pack()
frame.show()

def refreshText(event) {
  rate = proxy.ConversionRate(swing.from.getSelectedItem(), swing.to.getSelectedItem())
  swing.currency.text = rate
}

TerraServer-USA by Microsoft


TerraServer supports a Tiling Web Service that enables you to build applications that integrate with USGS imagery found on their site. Here is a sample of what you can achieve.

import groovyx.net.ws.WSClient;
def proxy = new WSClient("http://terraservice.net/TerraService.asmx?WSDL", this.class.classLoader)

// Create the Place object
def place = proxy.create("com.terraserver_usa.terraserver.Place")

// Initialize the Place object
place.city = "mountain view"
place.state = "ca"
place.country = "us"

// Geocode the place
def result = proxy.ConvertPlaceToLonLatPt(place)

println "Longitude: ${result.lon}"
println "Latitude: ${result.lat}"

will give:

Longitude: -122.08000183105469
Latitude: 37.400001525878906

Developers

Guillaume Alleon

Source Control

http://svn.codehaus.org/groovy-contrib/groovyws

Building

Contributing

Community

Mailing List(s)

use user@groovy.codehaus.org

Issue tracker

There is a GroovyWS category in JIRA

Articles

A nice article from Geertjan's blog with several examples: http://blogs.sun.com/geertjan/entry/groovy_web_service

Labels

 
(None)
  1. Feb 26, 2008

    lizardu says:

    I'm having problems running the simple server example. The Groovy SOAP server wo...

    I'm having problems running the simple server example. The Groovy SOAP server works fine (BTW the SOAP jar link in the GroovySOAP module is broken), but the GroovyWS isn't working for me:

    This line is throwing the exception:

    server.setNode("MathService", "http://localhost:6980/MathService")

    Feb 26, 2008 12:28:47 AM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
    INFO: Creating Service {http://DefaultNamespace}MathService from class MathService
    Caught: java.lang.NoSuchMethodError: org.apache.ws.commons.schema.XmlSchemaCollection.getExtReg()Lorg/apache/ws/commons/schema/extensions/ExtensionRegistry;
            at MathService.run(MathService.groovy:11)
            at MathService.main(MathService.groovy)

    Any suggestions?

    thanks

    -----------------------------------------------------------

    UPDATE - 02/26/2008

    I ran GWhich ( a modified groovy version of JWhich) on org.apache.ws.commons.schema.XmlSchemaCollection and found that the class was coming from the old groovysoap-all-1.0-0.3-snapshot_jdk1.5.0.jar. Oops. After I removed the SOAP jar from the groovy/lib folder, the server example now works.

    JWhich (and now GWhich) have proven to be extremely useful over the years.

  2. Mar 03, 2008

    Jason Dyer says:

    If anyone is getting the "<x> doesnt contain ObjectFactory.class or jaxb.index" ...

    If anyone is getting the "<x> doesnt contain ObjectFactory.class or jaxb.index" on Linux, make sure you don't have 'ecj' installed.  If you do, run 'apt-get remove ecj' (or your package manager remove command) and try again. 

    (I discovered this after a lot of frustration and trial-and-error, so I thought it might be worth documenting.

    Note that I have no idea what removing ecj (eclipse java compiler?) might break...

  3. Apr 21

    Roger Hosier says:

    I'm neither a Java developer nor a Groovy developer really, but I need some poin...

    I'm neither a Java developer nor a Groovy developer really, but I need some pointers to understand where to look or perhaps even to stop looking for some things that I'd like to do related to GroovyWS. Thanks in advance for any advice or links that you have for me!

    What I want to go is have the user be able to enter in the URL of the WSDL. The process will then create the proxy, which appears to create all of the classes necessary for the client. I want to be able to display a list of the operations to the user for selection of the action they are interested in. Once the operation is selected the user will be shown the appropriate request variable and they can select the ones that they need to fill in and designate where the data will come from or literals if appropriate. The mapping of the response will be done similiarly to the request. Once that is done whenever the action is to be taken the operation will be invoked and the data mapping will take place as necessary.

    Are there any methods that I can use to extract the list of operations from the proxy once it's created or do I need to inspect the WSDL itself in an XML parser like XMLSlurper and get this information myself? It seems like a waste for me to have to interrogate the WSDL when it's pretty obvious that CXF has already done that.

    One of the head scratchers that I have still is with the namespace handling. What I don't want to do is show com.terraserver_usa.terraserver.Place.city to the end user for the mapping, I think that Place.city would be perfect, but I don't see any way of being able to get the actual name of the class for the proxy.create call. I've seen a few posting where folks have complained about getting the generated package.class name from the WSDL, but I would expect that at least the CXF layer would allow me to get this information easily if I had the approriate CXF "client" object and knew the correct methods to call.

  4. May 13

    Dan OBrien says:

    The simple MathService works for me, but the complex BookService doesn't. The cl...

    The simple MathService works for me, but the complex BookService doesn't. The client gets this error:

    groovy bookClient.groovy
    May 9, 2008 8:14:05 AM org.apache.cxf.endpoint.dynamic.DynamicClientFactory outputDebug
    INFO: Created classes: defaultnamespace.AddBook, defaultnamespace.AddBookResponse, defaultnamespace.ArrayOfBook, defaultnamespace.Book, defaultnamespace.FindBook, defaultnamespace.FindBookResponse, defaultnamespace.GetBooks, defaultnamespace.GetBooksResponse, defaultnamespace.ObjectFactory
    BindingInfo = org.apache.cxf.binding.soap.model.SoapBindingInfo@13829d5
    o = SOAPBinding ({http://schemas.xmlsoap.org/wsdl/soap/} binding):
    required=null
    transportURI=[http://schemas.xmlsoap.org/soap/http]
    style=document
    defaultnamespace.ArrayOfBook@baa31b
    Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Groovy in Action' with class 'java.lang.String' to class 'javax.xml.bind.JAXBElement'
    at bookClient.run(bookClient.groovy:11)
    at bookClient.main(bookClient.groovy)
  5. Jun 24

    Danilo Araya says:

    Hello World... When i set up a client to the following WSDL

    Hello World...

    When i set up a client to the following WSDL

    http://siagfqa.paperless.cl:8083/axis2/services/ExtincionReconocimiento?wsdl

    using

    def extincion = new WSClient("http://siagfqa.paperless.cl:8083/axis2/services/ExtincionReconocimiento?wsdl", this.class.classLoader)

    i'm getting the following error

    Caused by: org.xml.sax.SAXParseException: A class/interface with the same name "
    cl.paperless.siagf.ws.Exception" is already in use. Use a class customization to
     resolve this conflict.

     Does anybody knows how to fix it?

  6. Jul 17

    Kirk Brocas says:

    This all looks great. One question though: will it support SMTP as a transport m...

    This all looks great. One question though: will it support SMTP as a transport mechanism like SOAP? CXF doesn't appear to support SMTP as transport.