Make JAXB great again!

Setup JAXB to generate a fluent builder API and use java.time classes

Author: Maciej Papież

Tue May 29 2018
2
java
1
builder pattern
1
java.time
1
jaxb
1
jdk8
1
maven

TL;DR

You don’t need to suffer from headache when working with JAXB-generated classes. Add a java.time adapter and amend the DTOs with fluent builder API instead.

Intro

Our team is creating a bi-directional integration with a third party company (which is a partner of our customer). One of the interfaces is defined by an XSD schema - the files will be transferred daily via SFTP.

JAXB: 1st attempt

We decided to create a new JAR artifact that will use cxf-xjc-plugin maven plugin to generate Java classes from XSD schema files. A bit of configuration and here it is - a ready dependency to be included wherever we need it.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-xjc-plugin</artifactId>
      <version>3.2.1</version>
      <configuration>
        <extensions>
          <extension>org.apache.cxf.xjcplugins:cxf-xjc-dv:3.2.1</extension>
        </extensions>
      </configuration>
      <executions>
        <execution>
          <id>generate-sources</id>
          <phase>generate-sources</phase>
          <goals>
            <goal>xsdtojava</goal>
          </goals>
          <configuration>
            <sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
            <xsdOptions>
              <xsdOption>
                <xsd>${basedir}/src/main/resources/xsd/integration.xsd</xsd>
                <packagename>software.xsolve.example</packagename>
                <extensionArgs>
                  <extensionArg>-Xdv</extensionArg>
                </extensionArgs>
              </xsdOption>
            </xsdOptions>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

I’ve started the implementation and quite soon it turned out to be cumbersome. Although the data structure is only 4 levels deep, building the tree using constructors + setters was not pleasant at all. There’s no indentation that keeps everything organized, there’s a lot of boilerplate code… let’s try to improve it!

Maybe JAXB could generate a builder?

Well, yes! A quick google search of “jaxb builder” will guide you to this StackOverflow post, where Mirko Klemm promotes a set of JAXB plugins that he created by himself.

When you jump to the repository, there’s a nice documentation available. Two plugins seemed interesting for me:

  • fluent-builder: Generates a builder class for every class generated,
  • immutable: Will make generated classes immutable.

All right, let’s plug them in! ;)

  1. Add an appropriate extension to your pom.xml file to grab the dependency (kind of):

    <extension>net.codesup.util:jaxb2-rich-contract-plugin:1.18.0</extension>

    This goes into build.plugins.configuration.extensions.

  2. Add two extensionArg elements in order to enable chosen plugins (consult the docs in order to find the required flags for other plugins):

    <extensionArg>-Xfluent-builder</extensionArg>
    <extensionArg>-Ximmutable</extensionArg>

    This, on the other hand, goes into build.plugins.executions.configuration.xsdOptions.extensionArgs.

So far so good! After a quick mvn clean install we can exploit a great, fluent builder API for object construction. A quick demo:

final Region region = Region.builder()
    .withCompany(EntityIdentifier.builder()
        .withIdentifier("Facebook LTD")
        .withType(EntityTypeCode.LTD)
        .withName("Facebook")
        .build())
    .addEmployee(EntityIdentifier.builder()
        .withIdentifier("123456")
        .withType(EntityTypeCode.CONTRACTOR)
        .withName("John Doe")
        .build())
    .build();

Sweet & fun to use, don’t you think?

Go away, XMLGregorianCalendar! Come here, java.time!

After a while, I noticed that by default, xs:dateTime are converted into XMLGregorianCalendar objects. Nobody likes to deal with old Java date/time APIs… unless we convert them to JodaTime or java.time ;)

Can it we done with JAXB?

Again, a bit of googling and I stumbled upon a blog post by @gdpotter, entitled Using Java 8 Time within JAXB with JXC. Hey, that’s exactly what I need! In his artice, he performs a short demo of jaxb-java-time-adapters library, created by @migesok. The blog post is based on Gradle, let’s port it to Maven world.

This small lib is a set of adapters that you can ask JAXB to use while generating your classes. How?

  1. Add a dependency to your pom.xml.

    <dependency>
      <groupId>com.migesok</groupId>
      <artifactId>jaxb-java-time-adapters</artifactId>
      <version>1.1.3</version>
    </dependency>
  2. Add a src/main/resources/binding.xml file that defines which adapters should be used.

    <bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"
      xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
    
      <globalBindings>
        <xjc:javaType name="java.time.OffsetDateTime" xmlType="xs:dateTime"
          adapter="com.migesok.jaxb.adapter.javatime.OffsetDateTimeXmlAdapter"/>
      </globalBindings>
    
    </bindings>
  3. Setup the bindings file in pom.xml and make sure that JAXB extensions are enabled. In our scenarion, two lines have to be added to the xsdOption section:

    <bindingFile>${basedir}/src/main/resources/binding.xml</bindingFile>
    <extension>true</extension>

The end.

I hope you’ll benefit from these two JAXB-related improvements that I just showed you. Working with JAXB doesn’t have to be a pain, we can make it more enjoyable and up-to-date!

Don’t hesitate to reach out! ;)

Loading...
Boldare