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.
- MathService.groovy
class MathService { double add(double arg0, double arg1) { return (arg0 + arg1) } double square(double arg0) { return (arg0 * arg0) } } - 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
- 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)
- 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 ![]()
- 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()]) } } - with the class Book being something like that.
Book.groovyclass 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
Comments (6)
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.
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...
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.
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)Jun 24
Danilo Araya says:
Hello World... When i set up a client to the following WSDLHello 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?
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.