Flexible marshalling and unmarshalling using Spring WS

Talking about WebServices from a clients perspective, Spring WS provides some true simplifications when it comes to configuring and calling arbitrary Services. While emphasizing a contract-first view (with a clear focus on the schema of the request resp. response message payload), it supports a couple of ways to map an object graph to the stipulated message structure of the contracted request schema (marshalling) and of course vice versa, mapping the received response payload to an appropriate object structure (unmarshalling).
The following sections will give some demonstration on how to leverage the various options for marshalling and unmarshalling within Spring WS, especially by showing how to apply a strategy for marshalling and a different one for unmarshalling, all within the same WebService call.

WebServiceGatewaySupport

Spring WS encourages a client model that’s based on the Gateway ‘pattern’: A (Message-) Gateway encapsulates the technical API code that is necessarily used to access an external resource (in our case a WebService), so a client of that Service is shielded from the technical complexity behind. For that purpose you can simply extend org.springframework.ws.client.core.support. WebServiceGatewaySupport and pick one of the provided send…() methods:

public class FundRatingGateway extends WebServiceGatewaySupport {
  ...
  public Rating rate( Fund fund) throws IOException{
    ...
    ... getWebServiceTemplate().sendSourceAndReceive( ... );
    ...
  }
}

As you have seen, we can rely on a org.springframework.ws.client.core.WebServiceTemplate that’s provided automatically by WebServiceGatewaySupport, assumed that it’s configured properly in Springs appclication context.
Most important, you have to provide the URI of the targeted WebService. Since you don’t need to modify or change the URI during runtime, you can provide a static URI simply by injecting it to a property called defaultUri:

<bean id="fundRatingGateway" class="demo.webservice.FundRatingGateway">
    <property name="defaultUri" value="http://www.xxx.org/demo/ws/fundrating/"/>
    ...
</bean>

Further on, you could also inject a certain message factory (to a property of the same name), which is responsible for creating the message (by selecting the underlying kind of message representation, i.e. generating a SOAP envelope if using SOAP messages). So if you for example want to produce SOAP messages using SAAJ (SOAP with Attachements API), nothing further is to do since this is the default message factory that’s being used by WebServiceGatewaySupport.

Automated Marshalling and Unmarshalling

Assumed, that we initially don’t want to create and parse XML manually in order to convert a Fund into XML and the received XML of the calculated Rating back into a Rating instance (although possible with Spring WS), we can endue WebServiceGatewaySupport with an arbitrary instance of a Marshaller and Unmarshaller, as long as they implement org.springframework.oxm.Marshaller resp. org.springframework.oxm.Unmarshaller. That said, it’s quite possible to assign a CastorMarshaller to transform the request object(s) to XML and a JAXB Unmarshaller for the way back (there are some Implementations right at hand, i.e. also basing on JIBX or XStream, although you could come up with your own implementation of a Marshaller or Unmarshaller), it’s only a question of configuration:

    <bean id="fundRatingGateway" class="demo.webservice.fundrating.FundRatingGateway">
        <property name="defaultUri" value="http://www.xxx.org/demo/ws/fundrating/"/>
        <property name="marshaller" ref="castorFundMarshaller" />
        <property name="unmarshaller" ref="jaxbRatingUnmarshaller" />
    </bean>

    <bean id="castorFundMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
	<property name="mappingLocation" value="classpath:demo//webservice/fundrating/FundRequestMapping.xml" />
    </bean>
    ...

Now all you have to do is call marshalSendAndReceive() on WebServiceTemplate – it will use the injected instances of marshaller and unmarshaller for conversion between the objects view and the messages view. All the boilerplate work of conversion and message construction will going on behind the scenes:

public class FundRatingGateway extends WebServiceGatewaySupport {
  ...
  public Rating rate( Fund fund) throws IOException{
    return (Rating) getWebServiceTemplate().marshalSendSourceAndReceive( Fund );
  }
}

In the Mix

Now suppose another scenario: The mesage structure of the returned response (the calculated Rating) might be too complex to be handled easily by an Unmarshaller (i.e. would the resulting mapping file be way to complex and uncomprehensable or there may be some rather dynamic parts within the responses schema that’s better handled programmatically and therefore with greater flexibility). In such cases it may be for example better to parse the resulting DOM structure of the response message (while we want to continue with marshalling the rather easy structured Fund using a Marshaller). We can’t use marshalSendSourceAndReceive() any longer since it requires both – a Marshaller and an Unmarshaller, so we have to look for an alternative.

Let’s take a closer look at WebServiceTemplates ‘standard’ method for calling a WebService:
sendSourceAndReceive( javax.xml.transform.Source requestPayload, org.springframework.ws.client.core.SourceExtractor responseExtractor )

It doesn’t seem to be a good candidate for our scenario at first sight, since there seems to be no way to introduce our Marshaller (though there seems to be a good chance for parsing the DOM of the response as we only have to act as a SourceExtractor – but one step after another).

Marshalling … engage

First we have to bring the Marshaller back into the game. We need the Marshaller to produce a javax.xml.transform.Source that we could pass as first argument. Inspecting org.springframework.oxm.Marshaller, we can see that there’s only one method responsible for the transformation:

public void marshal( java.lang.Object, javax.xml.transform.Result )

The second argument seems to be of type in-out, so the problem shifts to the question on how to convert Result to Source. Luckily there’s only a short distance between those two – we’ll using org.springframework.xml.transform.StringResult that gets wrapped (indirectly via org.springframework.core.io.ByteArrayResource) into a org.springframework.xml.transform.ResourceSource. It reads more complex than it’s actually is – take a short look at the source and you’ll understand immediately:

public class FundRatingGateway extends WebServiceGatewaySupport {
...
	private Source marshall( Fund fund ) throws IOException{

		StringResult result = new StringResult();

		getMarshaller().marshal( fund, result );

		return new ResourceSource( new ByteArrayResource( result.toString().getBytes() ) );
	}
}

Unmarshalling … extract

Now, the only thing left is to come up with an implementation of org.springframework.ws.client.core.SourceExtractor that is capable of transforming the response message back into a Rating instance. We have to implement
public Object extractData( javax.xml.transform.Source ) and see that again an instance of Source has a finger in the pie, this time representing the response.
Since the delivered Source is an instance of javax.xml.transform.dom.DOMSource, we could easily parse the Document by relying on the DOM API:

public class RatingResponseExtractor implements SourceExtractor{

	public Object extractData( Source src ) throws IOException, TransformerException {

		DOMSource dom = (DOMSource) src;

		return toRating( dom.getNode() );
	}

        private Rating toRating( Node ratingRootNode ){
            ...
        }
        ...

Putting the pieces together

Having all essential ingredients on board, it’s easy to adapt our Gateway. We simply use the converted Source that’s generated by our Marshaller and an instance of RatingResponseExtractor in order to please WebServiceTemplate and that’s it.
Note, that we have to cast the result of RatingResponseExtractor since the interface of SourceExtractor forced us to return an Object.

public class FundRatingGateway extends WebServiceGatewaySupport {
    private SourceExtractor ratingResponseExtractor = new RatingResponseExtractor();
    ...
    public Rating rate( Fund fund) throws IOException{
        return
            getWebServiceTemplate()
                .sendSourceAndReceive( marshal( fund ), ratingResponseExtractor  );
    }
    ...
}

Conclusion

As you may have seen, marshalling and unmarshalling isn’t very painful within Spring WS, especially if using WebServiceGatewaySupport in conjunction with its provided WebServiceTemplate.
It’s easy to use any kind of Marshaller or Unmarshaller as long as they appear as org.springframework.oxm.Marshaller resp. Unmarshaller.
Even mixing different transformation strategies for request and response messages is possible – of course also possible the other way round: providing a Source instance as response payload from a constructed DOM Document should just as well be possible as using an Unmarshaller for the response message at the same time. Since org.springframework.oxm.Unmarshaller.unmarshal() accepts directly an instance of Source, there’s even no conversion required.

Have a nice and straightforward time on consuming WebServices in a contract first centric manner …

Advertisements

8 Responses to “Flexible marshalling and unmarshalling using Spring WS”

  1. Carl Says:

    Good article,

    very informative and exactly what i was looking for!

    Thanks

    Carl

  2. Markus Says:

    Hi,
    Spring-WS is surely a very good choice for WS realisation. But I heard Apache CXF is even more flexible, but I didn’t try it out myself. Just for your consideration.

    Cheers, Markus

  3. Karthik Says:

    Just what I was looking for. Very good article.

  4. Arjan Says:

    Nice article!

    Instead of extending WebServiceGatewaySupport, one could also declare

    private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();

    and supply a setter for the uri:

    public void setDefaultUri(String defaultUri) {
    webServiceTemplate.setDefaultUri(defaultUri);
    }

  5. Arjan Says:

    Note that Castor’s setIgnoreExtraAttributes defaults to true in Spring WS 1.5. This will make Castor skip all remaining attributes as soon as it finds one that has not been mapped.

    See http://jira.codehaus.org/browse/CASTOR-1046

  6. Fayaz Shaik Says:

    Nice Article!
    Cheers Spring- ws.

  7. Anonymous Says:

    it gives uri exception do not forget to set uri in getway class as

    setDefaultUri(webServiceTemplate.getDefaultUri());

  8. Anonymous Says:

    good explanation and example !


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: