Spring WS and JAXB without a Code Generator

2010-04-28

Note: although this post mostly talks about implementing web services, the manual object-XML mapping technique described here can also be applied to WS clients.

I’d like to start by confessing that I’m very picky about programming techniques that rely on source code generators. I’m also sceptical about other kinds of compile-time tools that generate artifacts like XML files. I had my share of bad experiences with these sorts of tools, not because they are buggy or badly designed, but simply because they are not perfect.

Well, nobody’s perfect, you say? True enough. IDE auto-formatters are not perfect either. The style of code that they produce often still requires manual formatting. But it’s not a bad thing, since as a programmer I always have the final word: if I don’t like the place where that curly brace was put, or the way that lengthy statement was wrapped, I go ahead and fix it manually.

Enter the Evil Code Generators

Now, what if I don’t like some artifact produced by a generator? It can be ugly, it can be slow, it can miss the point – oh, well, there’s nothing I can do about it, because all my efforts will be overwritten during the next generation. It may be suitable for 95% cases, but what about the remaining 5%? When these tools do the work for me, they do it their way, and they won’t let me have that small customization that I need. You see, that’s one problem with code generators. They assume too much.

In my book, generators are only suitable for one-shot spawning of artifacts, or for quickly creating some throw-away demos. In practice, however, these tools are often integrated into project build lifecycle, which is bad because:

  • Generated output cannot be manually edited,
  • Generated output cannot be version controlled (at least in a meaningful way),
  • You become dependent on the generator – without it, the source won’t even compile!
  • The thought that you are not in control of your code makes you feel dizzy.

When it comes to Web Services…

…developers choose to go either the “WSDL to Java” or “Java to WSDL” route. The “to” part is usually done with some sort of generator utility, which means losing control of either the WSDL contract or the Java code. What I’d like to show in this post is a simple setup of Spring Web Services and JAXB that does not involve compile-time generation of artifacts, so that when defining your service interface you can feel completely in charge of everything: the Java, the XML, and the binding between the two.

I chose Spring WS mostly because it promotes contract-first Web Services, and JAXB2 because it’s a popular (some would say “official”) XML data binding specification. I was tempted to skip the binding part entirely and just parse/build the darn XML messages with JDOM, but then I saw that it required too much effort, especially in prospect of growing the WS interfaces.

Although I do believe that JAXB was designed to be primarily used alongside code generators like wsimport (WSDL to Java), it also works without them. What we’re going to do is:

  1. Write XML Schema for our web service
  2. Write Java classes for XML binding, with JAXB annotations
  3. Implement the endpoint
  4. Assemble everything with Spring WS configuration

The XML Schema

Create a /WEB-INF/personService.xsd file where each request and response message is expressed by an <element> and each complex type (if we need deeper structures) is represented by a <complexType>. Let’s say that our service accepts some kind of person-ID and returns the person’s name and age, where name is a complex type that consists of firstName and lastName.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="http://bar.foo"
    targetNamespace="http://bar.foo" elementFormDefault="qualified">

    <element name="GetPersonRequest">
        <complexType>
            <sequence>
                <element name="id" type="string"/>
            </sequence>
        </complexType>
    </element>

    <element name="GetPersonResponse">
        <complexType>
            <sequence>
                <element name="name" type="foo:FullName"/>
                <element name="age" type="int"/>
            </sequence>
        </complexType>
    </element>

    <complexType name="FullName">
        <sequence>
            <element name="firstName" type="string"/>
            <element name="lastName" type="string"/>
        </sequence>
    </complexType>

</schema>

The Java classes for XML binding

Now for each XML request, XML response and XML complex type we create a corresponding Java class and put JAXB annotations to bind it to XML. The Java classes that map to personService.xsd schema look like this:

@XmlRootElement(name="GetPersonRequest", namespace="http://bar.foo")
@XmlType(name="", propOrder={"id"})
@XmlAccessorType(XmlAccessType.FIELD)
public class GetPersonRequest {

    @XmlElement(name="id", namespace="http://bar.foo")
    private String id;

    /* normal getters and setters */
}

////////////////////////////////////////////////

@XmlRootElement(name="GetPersonResponse", namespace="http://bar.foo")
@XmlType(name="", propOrder={"name", "age"})
@XmlAccessorType(XmlAccessType.FIELD)
public class GetPersonResponse {

    @XmlElement(name="name", namespace="http://bar.foo")
    private FullName name;

    @XmlElement(name="age", namespace="http://bar.foo")
    private int age;

    /* normal getters and setters */
}

////////////////////////////////////////////////

@XmlType(name="FullName", namespace="http://bar.foo", propOrder={"firstName", "lastName"})
@XmlAccessorType(XmlAccessType.FIELD)
public class FullName {

    @XmlElement(name="firstName", namespace="http://bar.foo")
    private String firstName;

    @XmlElement(name="lastName", namespace="http://bar.foo")
    private String lastName;

    /* normal getters and setters */
}

Some explanation:

  • each request and response element maps to @XmlRootElement class
  • each inner element maps to @XmlElement field
  • each complex type maps to @XmlType class, where propOrder sets the order of inner elements
  • since requests and responses are also anonymous complex types, they are annotated with a nameless @XmlType
  • @XmlAccessorType(XmlAccessType.FIELD) annotation tells JAXB to access field values directly

The endpoint implementation

It’s really up to you how you’re going to implement this part. Here’s just an example of using the @PayloadRoot annotation to map incoming XML request to a handler method:

@Endpoint
public class PersonEndpoint {

    @PayloadRoot(localPart = "GetPersonRequest", namespace = "http://bar.foo")
    public GetPersonResponse getPerson(GetPersonRequest request) {
        /* implementation code */
    }
}

The assembly

Define a Jaxb2Marshaller bean and set the classesToBeBound property to an array of all @XmlRootElement classes, so that they can be recognized by the marshaller.

DefaultWsdl11Definition can be used to publish WSDL definition based on personService.xsd, as long as we follow the convention of Request/Response suffixes in XML element names. And the rest are standard Spring WS affairs. A more or less complete setup may look like this:

<!-- the endpoint implementation -->
<bean class="foo.bar.PersonEndpoint"/>

<!-- publishes WSDL contract based on the given XML schema -->
<bean id="personService" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
    <property name="schema" ref="schema">
        <bean class="org.springframework.xml.xsd.SimpleXsdSchema">
            <property name="xsd" value="/WEB-INF/personService.xsd"/>
        </bean>
    </property>
    <property name="portTypeName" value="personInterface"/>
    <property name="locationUri" value="/ws/personService"/>
    <property name="targetNamespace" value="http://bar.foo"/>
</bean>

<!-- uses annotations to map XML requests to endpoint methods -->
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>

<!-- passes endpoint method arguments and returned objects to JAXB marshaller -->
<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
    <property name="marshaller" ref="jaxbMarshaller"/>
    <property name="unmarshaller" ref="jaxbMarshaller"/>
</bean>

<!-- uses JAXB annotations to marshall between XML messages and Java objects -->
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="classesToBeBound">
        <list>
            <value>foo.bar.GetPersonRequest</value>
            <value>foo.bar.GetPersonResponse</value>
        </list>
    </property>
</bean>

<!-- parses and builds SOAP messages -->
<bean class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory"/>

The dependencies

For the sake of completeness, here's a listing of relevant Maven dependencies that I used.

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-core-tiger</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-xml</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-oxm-tiger</artifactId>
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
</dependency>
<dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
</dependency>
<dependency>
    <groupId>stax</groupId>
    <artifactId>stax-api</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.ws.commons.axiom</groupId>
    <artifactId>axiom-api</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.ws.commons.axiom</groupId>
    <artifactId>axiom-impl</artifactId>
</dependency>
<dependency>
    <groupId>xml-apis</groupId>
    <artifactId>xml-apis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.ws.commons.schema</groupId>
    <artifactId>XmlSchema</artifactId>
</dependency>

For a similar no-generated-code technique applied to JAX-WS services, check out this post.

  1. camilo casadiego - July 26th, 2010 at 22:02

    Hi there, i follwed closely all the setup, but im getting an java.lang.ClassCastException: com.ctc.wstx.stax.WstxInputFactory cannot be cast to javax.xml.stream.XMLInputFactory, my setup is jboss 5.1 and the app is build using spring framewrok, ibatis, and jboss richfaces….i already check many posts and different setups but still the same…you got any idea on how to solve this?

    Thanks a lot!!!

  2. Osvaldas Grigas - July 27th, 2010 at 08:58

    @camilo
    That may be caused by a clash of the WS/XML jars used by your project versus the WS libraries bundled in your application server. Experiment on excluding one or the other – the exact procedure may require app server specific configuration, tweaking the classpath or maybe even the classloaders. (I’m not a JBoss guy so I can’t know for sure.)

    For example, switching between different implementations of StAX (Streaming API for XML) might help. It looks like the one you’re currently using is Woodstox.

    Another thing to try is to remove stax-api from your project and from all transitive dependencies. If you use Maven, execute this command to see where stax-api is referenced:
    mvn dependency:tree -Dincludes=*:stax-api

  3. camilo casadiego - July 27th, 2010 at 15:02

    HI there, i triyed many configurations, and changed the stax api from the app server, this was the worst because it dont even managed to start, at the end i decided to move to jax-rpc web services in order to have a easier integration betwen the app and the J2ME app, finally this was what the webservice was intended for.

    I think i’ll get back to this in a couple weeks and let you know which is the rigth configration for jboss.

    thans a lot!

  4. Shameer - September 29th, 2010 at 20:02

    There is a similar post, with bit more details here:

    http://justcompiled.blogspot.com/2010/09/building-web-service-with-spring-ws.html

  5. Kristiaan - November 8th, 2010 at 21:39

    Thanks for this explanation, Osvaldas. I’m just starting to use spring-ws and since I’m not too keen on generated code myself, I’ll probably try your approach for writing a ws client.

  6. Ziletka - July 26th, 2011 at 22:30

    Hi,
    thanks for your post.
    I’m curious if is possible map a few XMLs to one java object by JAXB? Let’s say we have two web services:
    1) CreatePersonRequest
    2) UpdatePersonRequest
    with the same structure. Is it possible map those two requests to one object?

    By Castor it is possible, because there are special xml files for mapping given XML to object, so we can have a lot of “the same object” mappings. But JAXB uses annotation, that is connected with one java object.

  7. Osvaldas Grigas - July 27th, 2011 at 09:11

    @Ziletka
    You can create two Java objects without any fields of their own, but make them inherit from the same superclass which in turn should contain all common fields.

    Another way is to setup Spring’s PayloadTransformingInterceptor (http://static.springsource.org/spring-ws/sites/2.0/reference/html/server.html#d4e1440) to transform from one XML message to another using XSLT, before it gets mapped by JAXB.

  8. Ziletka - July 27th, 2011 at 12:51

    Upps inheritance of course. It is nice and clear solutions.