A trick with JAXB

Last week I found a neat trick with JAXB2.

The project I am working on at the moment is a mix of web services and database, and most of the things I store on the database are also things I need to include in my web service messages. This is often the case.

To define my domain objects I like to use an XSD file, pass it through JAXB2 with the HyperJAXB3 plugin, and that generates Java objects complete with JPA and JAXB annotations. That means I can easily serialize them to XML for my web services as well as use them with JPA to get them in and out of the database. I could write the classes by hand but I get another thing as well. The XSD file feeds into Spring’s web services framework to generate my WSDL file. So from one XSD file I get quite a lot.

Unfortunately HyperJAXB3 is no longer maintained, but it seems to work quite well and the ex-developer says he will accept pull requests. So far the only problem I have had is that it doesn’t like Java8 time objects, but neither does JPA at the moment so I’m avoiding that problem for now by using the old ones.

The XSD file, in case you haven’t seen one, is just an XML file that defines objects. You use the complex-type tag to define a domain objects and that contains element tags which define fields. There’s a little more to it, of course. You normally define relationships between objects (a customer has a one-to-many relationship with transactions), named queries and other database specific things. In my case I also have some custom annotations I add to the definitions. This is easy to organise. So some of my elements look like this:

<element name="id" type="string">
    <annotation>
        <appinfo>
            <hj:id>
                <orm:generated-value strategy="AUTO" />
            </hj:id>
            <annox:annotate>
                <ims:SalesforceName fieldName="Id" />
            </annox:annotate>
        </appinfo>
    </annotation>
</element>

The annox tag specifies my custom annotation. In this project I’m pulling data from Salesforce which has rather horrible field names so I change them to something I prefer but keep the old name in the annotation. The hj and orm tags generate the JPA annotations.

This works just fine but that extra information also ends up in the WSDL file, which means I’m rendering too much internal information to the outside world. It isn’t a privacy thing in this case, but when you start giving away information someone else might build something that relies on it, then suddenly I can’t change it any more.

The way you’re supposed handle that is to use a binding file, an XJC file. This is another XML file which takes a simple XSD file and uses the binding file to dynamically edit the extra bits into it during the JAXB phase. So you get a simple WSDL file (derived from the simple XSD file), and you still get the complexities you need for the Java generations. If you have several XSD files you can use the same binding file for all of them, which can help with consistency. The trouble is with a binding file is that it has rather tortuous syntax, the re-use is not as valuable as it seems because you generally have just one XSD file, and you often have extra tags to edit into every single element tag. That last point means your field definition and its modifying tags are separated which is inconvenient.

So I really wanted to avoid using a binding file, but I also wanted to expose a simpler WSDL. It occurred to me that if I defined the extra bits in the main XSD file (as in the above example) then all the tags I want to hide are inside annotation tags. That means a fairly simple XSL transform could strip those out, rather like doing the reverse of the binding file in fact. I can’t do this dynamically (although maybe I can but not without editing some 3rd party code) but I can simply generate a smaller XSD file from my larger one and use that smaller one to generate the WSDL file.

Here are the details:

My project has a schema directory which holds my main XSD file. A little further down you can see an ims-core.xsl file which is my XSL transform file. In my pom.xml I added a plugin reference:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>xml-maven-plugin</artifactId>
    <version>1.0.1</version>
    <executions>
        <execution>
            <goals>
                <goal>transform</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <transformationSets>
            <transformationSet>
                <dir>${basedir}/schema</dir>
                <stylesheet>file://${basedir}/ims-core.xsl</stylesheet>
                <outputDir>${project.build.directory}/generated-sources/xjc</outputDir>
            </transformationSet>
        </transformationSets>
    </configuration>
</plugin>

This runs the XSL transform on every XSD in the schema directory, and writes the output into the output dir. This output dir is the same one I output the generated Java file to because I want the generated (smaller) XSD file on the classpath.

The transform is quite simple, a lot of variations on this:

<xsl:template match="xsd:complexType">
<complexType>
<xsl:copy-of select="@*|b/@*" />
<xsl:apply-templates />
</complexType>
</xsl:template>

That one does the complexType tag, I have similar templates for schema, sequence, extension, element, simpleType, enumeration, totalDigits and fractionDigit. There is also this for annotation:

<xsl:template match="xsd:annotation" />

Which does nothing, ie leaves out annotation tags.
Finally, to remove certain objects altogether from the WSDL I have one of these:

<xsl:template match="xsd:complexType[@name='shedlock']">
<!-- ignore schedlock -->
</xsl:template>

I won’t go into the details of using HyperJAXB3 or even the Spring web services stuff here because they are covered elsewhere, but the trick, ie generating the XSD you really want to expose over web services using an XSL transform of your main XSD is worth knowing.

Leave a Reply

Your email address will not be published. Required fields are marked *