Wednesday, April 05, 2006

Spring and Axis - How to beat the XML-Object impedance mismatch

Spring has a whole suite of handy helper objects that help with SOAP, JAX-RPC and Web Services, and in my opinion they are much more powerful than the terse but information-rich user manual gives them credit for.

In particular, during a recent project I stumbled across the part of the Spring manual that described how to set up a JAX-RPC proxy using CGIlib and a thin spring wrapper (the JaxRpcPortProxyFactoryBean class). I thought "hey I will give it a go" and in typical Spring fashion in perhaps 5 minutes I had a small test working that hit a very simple web service.

Adventures in Shirking My Work

With that encouraging result I turned my attention to the web service I was building at the time. The XML schema for this web service ran to several hundred lines, with a couple of dozen types including polymorphism and enumerations. The Spring JAX-RPC proxy had a simple method for specifying the XML-java mappings and I mentally cringed but started grunting my way through the glue code I needed to generate. I didn't get far - I just have never been good at menial tasks (some people would say that about my ability to do just about any task, but lets not go there, shall we?)

In an effort to avoid real work I started poking around in Spring source code, and it wasn't too long before I discovered that Spring uses the Axis JAX-RPC client to provide client-side web service connectivity. Hmmm.... I thought. I was already using Axis to serve my web service,
and I had managed to completely describe the Object-XML mappings in the Axis deployment description (WSDD file). It made me start to wonder - could I use the same type mappings for the Spring JAX-RPC client?


Working Hard to avoid Working Hard

There was a post-processing method on the JaxRpcPortProxyFactoryBean where the type mapping information could be loaded, and after a little experimentation I finally came up with:

protected void postProcessJaxRpcService(javax.xml.rpc.Service service){

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setValidating(false);

factory.setNamespaceAware(true);

try

{

DocumentBuilder builder = factory.newDocumentBuilder();

Document document = builder.parse(

new ClassPathResource(classpathAccessibleWSDDFile).getInputStream());

WSDDService wsddService =

new WSDDDocument(document).getDeployment().

getWSDDService(new QName(wsddServiceName));

if ( wsddService == null )

throw new RuntimeException("No WSDD Service named \""+wsddServiceName+"\" was available");

TypeMapping serviceTypeMappings = wsddService.getTypeMapping(SOAP_ENCODING_STYLE);

TypeMappingRegistry registry = service.getTypeMappingRegistry();

registry.register(SOAP_ENCODING_STYLE, serviceTypeMappings);

}

catch (Exception e)

{

throw new RuntimeException("failed", e);

}

}



In the above code, I simply used a couple of Axis calls to read the WSDD file, extract the TypeRegistry information and then hand it off to the Axis JAX-RPC client. It worked first time I managed to properly load the TypeRegistry, flawlessly. Again, typical Spring.

Waiting for the Flash to Hit Me

I was feeling pretty cheeky for the rest of the day, having managed to add a sophisticated JAG-RPC client to my arsenal with only a couple of lines of code. It wasn't till a couple of weeks later when I was about to add a second client (to a different web service) that I really realized how quickly I could put together just about any client from a valid WSDL file and a WSDD deployment descriptor.

I have 4 or 5 working clients now, including both RPC style and Document style web services. And all as a result of feeling too lazy one day to do my real job.

Is there a lesson there? Not one I would tell my kids.

1 comment:

Vaddadi K Naidu said...

Thank you. It really helped.