In the previous episode from this series, I showed you how to use XML-RPC to call Java code from Ruby over the network. In this article, we will do the same, but using SOAP instead of XML-RPC. One advantage of SOAP over XML-RPC is the fact that SOAP endpoints can be described using WSDL and, by using Ruby’s extensibility, new methods can be created on the fly to mirror the methods declared in the WSDL, therefore giving us a lot of nice loose coupling.
Moreover, toolkits exist to automatically create WSDL descriptions from Java classes, so you won’t have to do it by hand. One of these toolkits, and the one we’ll be using in the examples here, is XFire. The most recent version of XFire available at the time I’m writing this is 1.1-RC1. I suspect this example will work just as well when 1.1 final is released. You’ll also need Java5 and Maven 2.
Maven is handy for automatically downloading all the necessary dependencies and running the demo inside a servlet container, using the Jetty6 plugin, without writing much code or configuration. Of course, you can always download all the necessary JARs by hand and drop them, together with the demo code, into the servlet container of your liking.
You can follow XFire’s Quick Start tutorial to set up a simple SOAP service, or you can simply grab a copy of this archive, which adds some more data to the tutorial sample, unzip it and run mvn jetty6:run from its root directory, once you have Maven installed. Go grab a cup of coffee while Maven downloads all the dependencies. Finally test that everything is OK by requesting a copy of the WSDL file from http://localhost:8080/services/BookService?wsdl.
On Ruby’s side, all that is needed is already provided by Ruby’s own core soap library, in particular SOAP::WSDLDriverFactory.
Here’s how you’d call your service from Ruby:
require 'soap/wsdlDriver'
WSDL_URL = 'http://localhost:8080/services/BookService?wsdl'
driver = SOAP::WSDLDriverFactory.new(WSDL_URL).create_rpc_driver
books = driver.getBooks(nil)
p books.out.book[0].title
book = driver.findBook(:isbn => '222')
p book.out.title
A couple of minor nits:
- The BookService#getBooks method in Java takes no arguments, but if you try to call driver.getBooks without arguments, Ruby complains about a missing argument. Apparently, passing a ‘nil’ value fixes it, but I’d like to know why this is so.
- XFire add this extra ‘out’ element to its generated schema. This is mildly annoying as you always have to use it in order to access the real values you are interested in.
On the pro side, you can set up all of this with very little configuration. The only configuration that is going to change from one application to another is XFire’s services.xml file which maps service names to service classes, and even this is pretty straightforward:
<beans xmlns="http://xfire.codehaus.org/config/1.0">
<service>
<name>BookService</name>
<namespace>http://sourcesense.com/BookService</namespace>
<serviceClass>com.sourcesense.xfire.demo.BookService</serviceClass>
</service>
</beans>
Technorati Tags: ruby, xfire, soap, maven, jetty.