Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Excerpt
hiddentrue

create a SOAP server and make calls to remote SOAP servers using Groovy

Info
titleDeprecated Module

Before using GroovySOAP, make sure to check GroovyWS

Introduction


Wiki Markup
{link:SOAP|http://www.w3.org/TR/soap/}{link}
is a lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. Groovy has a SOAP implementation based on
Wiki Markup
{link:Xfire |http://xfire.codehaus.org}{link}
which allows you to create a SOAP server and/or make calls to remote SOAP servers using Groovy.

Installation

 You just need to download this jar file in your ${user.home}/.groovy/lib directory. This jar file embeds all the dependencies.

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
    Code Block
    java
    java
    public class MathService {
        double add(double arg0, double arg1) {
          return (arg0 + arg1)
        }
        double square(double arg0) {
          return (arg0 * arg0)
        }
    }
    

  2. You can also using something more Groovy
    Code Block
    java
    java
    double add(double arg0, double arg1) {
      return (arg0 + arg1)
    }
    double square(double arg0) {
      return (arg0 * arg0)
    }
    

  3. Then the easy part ... no need for comments
    Code Block
    java
    java
    import groovy.net.soap.SoapServer
    
    def server = new SoapServer("localhost", 6980)
    
    server.setNode("MathService")
    
    server.start()
    

    That's all !

The Client

  1. Oh ... you want to test it ... two more lines.
    Code Block
    java
    java
    import groovy.net.soap.SoapClient
    
    def proxy = new SoapClient("http://localhost:6980/MathServiceInterface?wsdl")
    
    def result = proxy.add(1.0, 2.0)
    assert (result == 3.0)
    
    result = proxy.square(3.0)
    assert (result == 9.0)
    
  2. You're done!

Custom Data Types

This example shows how to use custom data types with Groovy SOAP. The code can be downloaded from here.

The Server

The PersonService.groovy script contains the service implementation and the custom data type (Person).

Code Block
titlePersonService.groovy
class Person {
  int id
  String firstname
  String lastname
}

Person findPerson(int id) {
  return new Person(id:id, firstname:'First', lastname:'Last')


Server.groovy is equivalent to the previous example.

Code Block
titleServer.groovy
import groovy.net.soap.SoapServer;

def server = new SoapServer("localhost", 6980);
server.setNode("PersonService");
server.start();


For each class compiled by the groovy compiler a metaClass property is added to the bytecode. This property must be excluded from being mapped by XFire, otherwise an error will be reported when trying to obtain the WSDL document from http://localhost:6980/PersonServiceInterface?wsdl. The reason is that XFire cannot map groovy.lang.MetaClass. To ignore the metaClass property a custom type mapping must be defined (for details refer to Aegis Binding).

Code Block
xml
xml
titlePerson.aegis.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns:sample="http://DefaultNamespace">
  <mapping name="sample:Person">
    <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


Code Block
titleClient.groovy
import groovy.net.soap.SoapClient

def proxy = new SoapClient('http://localhost:6980/PersonServiceInterface?wsdl')
def person = proxy.findPerson(12)
println 'Person (' + person.id + ') = ' +
  person.firstname + ' ' + person.lastname



More Information

Current limitations (and workaround)

  1. No authentication (see JIRA issue 1457)
  2. No proxy support (see JIRA issue 1458)
  3. Numeric values are represented as strings in custom data types and arrays.
  4. Custom data types cannot be processed on client side when using the Groovy SOAP module with the current groovy-1.0 release.
  5. It looks like the XFire dynamic client does not support complex datatypes. This may be a concern if you need for example to transfer an Image as a byte array. The workaround I use is to transform this in a String an transfer that String - As this is a bit painful I am investigating moving to the regular XFire client. Here is a little program demo-ing this (look at this "disco age" image - Is Groovy that old ?

The client (ImageClient.groovy)

Code Block
java
java
import groovy.swing.SwingBuilder
import groovy.net.soap.SoapClient
import javax.swing.ImageIcon
import org.apache.commons.codec.binary.Base64

proxy = new SoapClient("http://localhost:6980/ImageServiceInterface?WSDL")

// get the string, transform it to a byte array and decode this array
b64 = new Base64()
bbytes = b64.decode(proxy.getImage().getBytes())


swing = new groovy.swing.SwingBuilder()

// this is regular SwingBuilder stuff
i1 = swing.label(icon:new ImageIcon(bbytes))
frame = swing.frame(title:'Groovy logo', defaultCloseOperation:javax.swing.WindowConstants.DISPOSE_ON_CLOSE) {
  panel(){
    widget(i1)
  }
}
frame.pack()
frame.show()


The (ugly) server part embedding the image which is Base64 encoded (ImageServer.groovy):

Code Block
java
java
import groovy.net.soap.SoapServer

def server = new SoapServer("localhost", 6980)
server.setNode("ImageService")
server.start()


and the missing and secred part is here.

Demos with public web services

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 !

Code Block
java
java
import groovy.swing.SwingBuilder
import groovy.net.soap.SoapClient

proxy = new SoapClient("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL")

def currency = ['USD', 'EUR', 'CAD', 'GBP', 'AUD']
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
}


And here is the result: