WSImport and the dreaded xs:date in a simpleType

(Daniel Pfeifer)
Every now and then you will have a Web Service that defines an operation's parameter as a complexType that contains a list of reusable simpleTypes which in turn have restrictions such as xs:int or xs:date. While this is a good idea for the sake of reusability and clearity to the business many of us encounter the problem that JAXB doesn't correctly infer the correct serialization for these simpleTypes and in particular for xs:date.
In the JAXB documentation it states that simpleTypes are handled as Strings first and that the restriction is checked after that. Now there shouldn't be a problem with this; however, in JAXB 2.1 this fails for xs:date as the resulting web service client will always fall back to xs:dateTime which gives us grief when the receiving end will check whether they get the incorrect "2012-02-02T00:00:00.000+01:00" or the correct "2012-02-02+01:00".
Now how do you solve this? We would of course prefer to do as little tinkering as necessary to get the desired result. On the internet you can often find suggestions such as implementing globalBindings for xs:date and have your own Adapter in Java which does this for you. I guess that's fine, except that the documentation of JAXB explicitly mentions that this should be done for types that Java doesn't natively support. Now JAXB does support xs:date natively and all we really want is that the marshalling and unmarshalling is done correctly and for JAXB to do this correctly there is one tiny annotation that resolves all our issues, it's @XmlSchemaType("date"). Now if you use wsimport or xjc to create our Java classes from a WSDL that we've received from the SOA-team we really want this to be added by wsimport and not by us each and every time we have to update the Java classes due to definition updates.
Unfortunately, adding annotations explicitly using a custom bindings file isn't supported by JAXB natively. What we need is a 3rd party JAXB plugin called Annotate (there are more nice plugins, such as Equals, Hashcode and ToString just to name a few), which in turn depends on the framework Annox. The tricky part is how make Annotate and JAXB work together to fix our generated Java files.
Let's first look at our hypothetical XML Schema ( I am skipping the WSDL-stuff since it's not what we're interested of, I guess you already know what a WSDL looks like ;) ):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="http://mindbug.org/ws/schema/1">
  <xs:complexType name="WebComment">
    <xs:sequence>
      <xs:element name="Id" ref="tns:Id"/>
      <xs:element name="Comment" ref="tns:Clob"/>
      <xs:element name="CreateDate" ref="tns:CreateDate"/>
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="Id">
    <xs:restriction base="xs:int"/>
  </xs:simpleType>
  <xs:simpleType name="Clob">
    <xs:restriction base="xs:string"/>
  </xs:simpleType>
  <xs:simpleType name="CreateDate">
    <xs:restriction base="xs:date"/>
  </xs:simpleType>
</xs:schema>
Now that we have a XML Schema to work with we need to know how to create our correctly annotated JAXB class and the first step is to create the JAXB bindings customization file that I previously mentioned.
Let's look at one that matches the above schema:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:annox="http://annox.dev.java.net"
          version="2.1">
  <bindings
    node="/xs:schema/xs:complexType[@name='WebComment']//xs:element[@name='CreateDate']">
    <annox:annotate target="field">
      <annox:annotate annox:class="javax.xml.bind.annotation.XmlSchemaType"
                      name="date"/>
    </annox:annotate>
  </bindings>
</bindings>
As you can see this file is fairly simple. The Schema will be treated mostly by JAXB's defaults; however, for the CreateDate we create our own binding. The important thing here is that you point to the CreateDate using a valid XPath-expression and more importantly, you will see that we use elements from the namespace annox, which is the 3rd party JAXB plugin which I talked about previously and this example will only work if you have this plugin on wsimport's (and subsequently xjc's) classpath.
Now how do we get wsimport (or xjc) to run the previous example? Well, in most development environments today you see Maven to perform builds and since I do this, too, I want to show how we use the jaxws-maven-plugin to create a fully working web service client with the properly annotated JAXB classes.
So let's look at our Maven pom.xml (don't hold your breath, it's fairly long):
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>wsimportdemo</groupId>
<artifactId>wsimportdemo</artifactId>
<version>1.0</version>
<build>
  <plugins>
    <plugin>
      <groupId>org.jvnet.jax-ws-commons</groupId>
      <artifactId>jaxws-maven-plugin</artifactId>
      <version>2.2</version>
      <executions>
        <execution>
          <id>do-wsimport</id>
          <goals>
            <goal>wsimport</goal>
          </goals>
          <configuration>
            <!-- You can customize a lot here,
like directories where your
                   wsdl/bindings/xsd-files are et cetara -->
              <target>2.1</target>
              <keep>true</keep>
              <extension>true</extension>
              <xjcArgs>
                <xjcArg>-Xannotate</xjcArg>
              </xjcArgs>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics-annotate</artifactId>
            <version>0.6.3</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>
Now if you use this pom-file in Maven and have the custom bindings file placed in the folder that jaxws-maven-plugin looks for by default (${basedir}/src/jaxws) you will see that the field createDate in your newly created WebComment.java file is annotated with @XmlSchemaType(name = "date") just as it should and both marshalling and unmarshalling will be done correctly.
I do realize that I skipped over a lot of stuff (like showing a complete WSDL) but I very much believe that if you're reading this post than you probably found it on Google after running into this exactly problem so you likely don't need fully comprehensive go-through.
Hope this post helped you and as always: Enjoy!