Download NetBeans!

20071103 Saturday November 03, 2007

Groovy Web Service

Long cherished dream of mine, reverberating through the darker corners of my innermost thoughts... figuring out how to consume a web service in Groovy. "A web service? In Groovy? That must mean you use the same standard Java libraries for JAX-WS, or JAX-RPC, generate client stubs and then use them to connect to the web service, right?" Wrong. Forget stubs. Groovy provides its own library for web services. Just to simplify the life of developers, since it is incredibly lightweight and gets the job done painlessly. And there are no stubs.

Everything, though slightly out of date, is described here:

Groovy Web Services

I tried the final example, with success. Here's my Groovy script:

import groovyx.net.ws.WSClient

class TryIt {
    
    groovy.swing.SwingBuilder swing = new groovy.swing.SwingBuilder()
    
    def proxy = new WSClient("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL", TryIt.class.classLoader)
    def currency = ['USD', 'EUR', 'CAD', 'GBP', 'AUD', 'SGD']
    def rate = 0.0

    void main() {

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

        def 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
    }
    
}

It's incredible that this is literally all the code that you need. Nothing more in any shape or form. No configuration files, no stubs, no XML, no anything else. The above is almost the same as in the original document referred to above, but slightly tweaked (e.g., the 'def' keyword had been omitted in a few places). When I call the above class from a Java class (since NetBeans IDE doesn't support the running of Groovy classes, just Groovy scripts), the following Swing form appears, created from the SwingBuilder code above:

Then I enter a number (in the above case, I typed '500.00') and press Refresh. A number that doesn't make much sense to me returns, but that's how things go with web services over which you have no control. They're just black boxes, spewing something back to you upon request:

To set this up in NetBeans IDE, apart from creating the Groovy class in a Groovy file and calling it from a Java class, you need to be aware of the following:

  • Make sure to include the groovyws JAR when you compile, otherwise compilation will fail, since you're using Groovy's WSClient class:

    <target name="groovyc" description="groovyc">
        <taskdef name="groovyc" 
                 classpath="lib/groovy-all-1.1-rc-2-SNAPSHOT.jar" 
                 classname="org.codehaus.groovy.ant.Groovyc"/>
        <groovyc srcdir="${src.dir}" destdir="groovy">
            <classpath path="lib/groovy-all-1.1-rc-2-SNAPSHOT.jar"/>
            <classpath path="lib/groovyws-all-0.1.jar"/>
        </groovyc>
    </target>

    I can't remember where I got that JAR from. I googled a lot and found it referred to somewhere, after groovyws-standalone.jar turned out to not include everything I needed. (But maybe I'm wrong, I'll check this.) Now that you know it is called groovyws-all-0.1.jar, you should be able to find it. (Would be cool if it were bundled with the standard Groovy distribution.)

  • If you're using JDK 6, you need to set the endorsed dir, in the Run panel, in the Project Properties dialog box:

    -Djava.endorsed.dirs=copy the value of jaxws.endorsed.dir from nbproject/private.properties

  • You need ant-1.7.0.jar and the Groovy 1.1 JAR (or some other version of the Groovy 1.1 JAR) in your application's Libraries node.

And, I think, that's it. One thing thing I'm going to try is to get the Daily Dilbert web service to return comics via Groovy, as above, although I suspect I may end up in trouble with the images. But, before beginning that, I did a bit of tweaking, and not much later I now have the world's simplest web service client:

import groovyx.net.ws.WSClient

class TryIt {
    
    groovy.swing.SwingBuilder swing = new groovy.swing.SwingBuilder()
    
    def proxy = new WSClient("http://saintbook.org/MightyMaxims/MightyMaxims.asmx?WSDL", TryIt.class.classLoader)
        
    void main() {

        def frame = swing.frame(title:'Thought for the Day') {
          panel {
            label(proxy.ForToday())
          }
        }
        
        frame.pack()
        frame.show()
        
    }
        
}

The above (which is NOT a snippet, it is the entire web service client), results in this when I run it:

And here's one that's a bit more interactive, similar to the first one, but this time sending snippets of Shakespeare to a web service in order to retrieve full speeches, which has been referred to several times before in this blog:

import groovyx.net.ws.WSClient
import java.awt.BorderLayout

class TryIt {
    
    groovy.swing.SwingBuilder swing = new groovy.swing.SwingBuilder()
    
    def proxy = new WSClient("http://www.xmlme.com/WSShakespeare.asmx?WSDL", TryIt.class.classLoader)
        
    void main() {

        def frame = swing.frame(title:'Shakespeare',size:[300,300]) {
          panel(layout: new BorderLayout()) {
            textField(id:'quote',constraints: BorderLayout.CENTER, "fair is foul")
            textArea (id:'area',constraints: BorderLayout.NORTH, proxy.GetSpeech(swing.quote.text).replaceAll("><",">\n   <"))
            button(constraints: BorderLayout.SOUTH,"Search",action:refresh)
          }
        }
        
        frame.pack()
        frame.show()
        
    }
    
    def refresh = swing.action(
          name:'Refresh',
          closure:this.&refreshText,
          mnemonic:'R'
    )
    
    def refreshText(event) {
          def newQuote = proxy.GetSpeech(swing.quote.text)
          swing.area.text = newQuote.replaceAll("><",">\n   <")
    }
        
}

Clearly, it's all really cool and lightweight. I can imagine it can be very useful for doing quick tests as part of a larger process. But the above would make sense in a production environment too, I reckon. Why consume web services the hard way if you can do it the easy way? For all details on this, see the aforementioned page, which seems to be the only one that describes this cool Groovy feature.

In other news. Put 13949712720901ForOSX in your blog to let Apple know that you want Java 6 support in Mac OS, as described here! And then go here to see all the other people who have already done so...

Nov 03 2007, 12:41:45 PM PDT Permalink