John D'Emic's blog about programming, integration, system administration, etc...

Friday, April 10, 2009

Mule SAAJ Transport

I've recently been working on a couple of projects that use complex SOAP API's. One of these API's is specified with a 1.5 megabyte WSDL along with something like 50 megabytes worth of Axis generated stub classes. Since I only needed to use a small subset of the WSDL's methods, I wasn't thrilled with dealing with the WSDL or the Axis stubs directly. As we're using Mule, this would have involved using either a CXF WSDL outbound-endpoint or maybe using the stub classes in a component.

Since I knew what the SOAP payloads look like (the SOAP body content), what I really wanted to do was just build this XML and pass it to an endpoint. It would also be nice if this endpoint dynamically set the SOAP message headers, extracted the SOAP body from the response and applied the response transformers (and perhaps got me a beer.)

I didn't see an obvious way to do with the CXF transport, so I took a stab at implementing such a transport myself. I had used SAAJ in a web-services proxy project I worked on last year and it seemed like a good fit. As such, I present the SAAJ-Transport. You can currently use it to pass arbitrary XML that is used as the SOAP body in messages sent to a SOAP endpoint. The endpoint handles constructing the SOAP message for you, adding the headers and extracting the SOAP body from the response (it won't get you a beer...yet.) Here's an example using a chaining-router to send a SOAP message and send the response to a VM endpoint.

<saaj:outbound-endpoint address="${service.url}" synchronous="true">
<transformer ref="templateToRequest"/>
<mulexml:xml-to-dom-transformer returnClass="org.w3c.dom.Document"/>
<saaj:mime-header-transformer key="Cookie" value="#[header:SERVICE_SOAP_SESSION_ID]"/>
<vm:outbound-endpoint path="service.dispatcher">
<mulexml:dom-to-xml-transformer returnClass="java.lang.String"/>

The "document-to-soap-message-transformer" takes an org.w3c.dom.Document, transforms it to a SAAJ SOAPMessage and uses SAAJ to invoke the web-service. The mime-header-transformer adds a MIME header to the message (in this case a Cookie). Existing properties on the MuleMessage will be added the the SOAP header. When the response is received, the transport will extract out the SOAPBody and return it as the synchronous response, as well as set any SOAP headers as properties on the MuleMessage. In this case, the response is transformed back to a Document, then to a String, then finally passed out on the VM endpoint.

I'm hoping next week to get full documentation and examples up on the MuleForge page. I'm also planning to work on receiver functionality. This would allow you to receive SOAP messages on an inbound-endpoint and have their bodies extracted, headers set as Mule properties, etc. I'm still working on getting the distribution together. For now you'll need to checkout the source and use "mvn clean package" to build the jar or "mvn clean install" to get them into your local repository.


Dan Diephouse said...

Couldn't this be done with a CXF outbound endpoint?

<cxf:outbound-endpoint address="..." proxy="true"/>

That will take whatever messsage you send it and embed it in a soap envelope. Just make sure it's XML of some sort.

I don't think this will add all message properties as soap headers, but it does add the correlation properties in as headers. You could always write a CXF interceptor to do that too.

johndemic said...

Ah, good call Dan...I missed proxying on outbound CXF endpoints. Will this extract the XML payload from the response as well, or is that still wrapped in the SOAP envelope?