Thursday 21 November 2019

Compiling JAXB and JAX-WS classes with Maven in Java 11

Due to the deprecation of EE modules from SE, migrating projects to Java 11 can be somewhat trickier when you rely on Maven to compile your JAXB or JAX-WS classes. See https://jaxenter.com/jdk-11-java-ee-modules-140674.html for more information.

Here's how I got round the issues:

Dependencies


These were either the latest at time of writing, or the version that happened to work. Always check Maven Repository before throwing in dependencies - there may be a better version out there.

<javax.activation.version>1.1.1</javax.activation.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<jaxb-runtime.version>2.3.2</jaxb-runtime.version>
<jaxb2.maven.version>2.5.0</jaxb2.maven.version>
<jaxws.tools.version>2.3.2</jaxws.tools.version>

<dependency>
<groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>${javax.activation.version}</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>${jaxb-api.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>${jaxb-runtime.version}</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-tools</artifactId>
    <version>${jaxws.tools.version}</version>
    <scope>compile</scope>
</dependency>

JAXB


Our legacy application was using the following dependencies from org.jvnet.jaxb2 to generate the classes based on the xsds/xjc:

<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.12.2</version>

<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.6.4</version>

I won't go into detail as to how we configured those as it's not relevant here. Importantly, even by upgrading the versions, at time of writing, these simply do not work in Java 11! Here's the working solution which uses an xjc file to create the files. If you don't have one, they are easily created. There are many xjc guides out there.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>${jaxb2.maven.version}</version>
    <executions>
        <execution>
            <id>mystuff</id>
            <goals>
                <goal>xjc</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/main/resources/xsd/</source>
                </sources>
                <packageName>my.package.name</packageName>
            </configuration>
        </execution>
    </executions>
</plugin>

JAX-WS


This is a bit different because we need the endpoints as well as the JAXB objects. Essentially we need to call wsimport differently than we would have using the fairly straightforward jaxws-maven-plugin.

<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.4.1</version>

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>${maven.exec.version}</version>
    <executions>
        <execution>
            <id>mkdir</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <executable>mkdir</executable>
                <arguments>
                    <argument>-pv</argument>
                    <argument>target/generated-sources/wsimport</argument>
                </arguments>
            </configuration>
        </execution>
        <execution>
            <id>mystuff</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <executable>java</executable>
                <arguments>
                    <argument>-classpath</argument>
                    <classpath/>
                    <argument>com.sun.tools.ws.WsImport</argument>
                    <argument>-extension</argument>
                    <argument>-Xnocompile</argument>
                    <argument>-wsdllocation</argument>
                    <argument>/xsd/file.wsdl</argument>
                    <argument>-s</argument>
                    <argument>target/generated-sources/wsimport</argument>
                    <argument>src/main/resources/xsd/file.wsdl</argument>
                </arguments>
            </configuration>
        </execution>
    </executions>
</plugin>

This plugin literally creates a directory and then calls wsimport from the com.sun.tools.ws.WsImport library.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <version>${maven.build.helper.version}</version>
    <executions>
        <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>${project.build.directory}/generated-sources/wsimport/</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

For the code to be picked up use build-helper-maven-plugin as you normally would.