SpringWS and Jaxb

Well, truly say this small post is not about how to use SpringWS and Jaxb together. It's more about potential power of this combination. I know, that combination is in release for few years alredy and used by thousands of developers. But this article is more for guys in doubt if they really need SpringWS rather than for experienced Spring WS developers.

First of all, I want to say that Spring WS is very powerful framework. I've noticed it when version 1.0 beta was released. As everything in Spring it has good API, simple implementation model and as result it was very flexible. And this mean that you can easily introduce need changes and extensions without changing Spring WS source code.

From the beginning, the Spring WS team was saying that SpringWS should support not only low-level request processing, when you have some object that represents input XML document or build output XML document, but can use different mapping and translation libraries to make it for you. And here is a place for Jaxb.

To enable marshaling you need just to do few simple changes:

  1. Add marshaller bean of org.springframework.oxm.jaxb.Jaxb2Marshaller type:
    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="classesToBeBound">
    <list>
    <value>GetImageRequest</value>
    <value>GetImageResponse</value>
    <value>AddImageRequest</value>
    <value>AddImageResponse</value>
    </list>
    </property>
    </bean>

  2. Add Marshaling Method Endpoint Adapter to yours spring.xml:
    <bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
    <property name="marshaller" ref="marshaller"/>
    <property name="unmarshaller" ref="marshaller"/>
    </bean>

  3. Well, that's all :)

And then, after some testing, I found that it would be nice to trim input text fields and do other post-marshalling operations. And I was sure there was a normal way to do that with rather Spring WS or Jaxb. After a few minutes of searching, I have found the way and had completely working code that was doing what I was needed.

The catch is that Jaxb Unmarshaller supports listeners and Spring's Jaxb2Marshaller has a property unmarshallerListener, that points your listener to Unmarshaller's listeners.
With only 3 lines of configuration we have own listener called on each unmarshalling:
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
....
<property name="unmarshallerListener">
<bean class="TargetHandlerUnmarshallerListener"/>
</property>
</bean>

Here is TargetHandlerUnmarshallerListener code:
import javax.xml.bind.Unmarshaller;

public class TargetHandlerUnmarshallerListener extends Unmarshaller.Listener {

@Override public void afterUnmarshal(Object target, Object parent) {
super.afterUnmarshal(target, parent);

if (target instanceof PostPopulatedHandler) {
((PostPopulatedHandler) target).postPopulated();
}
}
}

In this class I override only afterUnmarshal() method, that is called after XML is transformed to the Java object. You can also override beforeUnmarshal() method if you need.

PostPopulatedHandler is my own interface that expose only postPopulated() method. Each class knows what to do after unmarshalling, and declares it in the postPopulated() method. Of course, there can be used some annotation, but interface option is the most simple, easier and obvious way.

So, some summary is next: Spring libraries are awesome, because they always provide very good API and you always can do what you need without changing library source code. Jaxb is awesome as well. I'm noticing this, because it's always hard to write good API, especially in multifunctional libraries.

No comments: