Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to use Spring JAXB Marshaller with Java 9 without defining additional modules

To illustrate my issue, I created a small spring boot sample application. The purpose of the application is to create a Jaxb2Marshaller bean.

@SpringBootApplication
public class App implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public Jaxb2Marshaller jaxb2Marshaller() {
        Jaxb2Marshaller bean = new Jaxb2Marshaller();
        bean.setContextPath("ch.sahits.game.helloworld");
        return bean;
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Started up");
    }
}

This code fails to start up with the exception:

Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
    at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122)
    at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:174)
    ... 23 more

What I do not understand about this exception is why the com.sun.xml.internal.bind.v2.ContextFactory is tried to instantiate? It has been suggested, that I also need a dependency for a runtime, so I added:

<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.0</version>
</dependency>

But that only got me a different exception, trying to load a different class:

Caused by: java.lang.ClassNotFoundException: com.sun.xml.bind.v2.model.annotation.AnnotationReader
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
    ... 39 more

Here is the complete pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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>ch.sahits.game</groupId>
    <artifactId>AddModuleDependencies</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <cxf-xjc-plugin.version>2.3.0</cxf-xjc-plugin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>5.0.4.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-xjc-plugin</artifactId>
                <version>${cxf-xjc-plugin.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>xsdtojava</goal>
                        </goals>
                        <configuration>
                            <extensions>
                                <extension>org.apache.cxf.xjcplugins:cxf-xjc-dv:${cxf-xjc-plugin.version}</extension>
                            </extensions>
                            <xsdOptions>
                                <xsdOption>
                                    <xsd>src/main/resources/helloworld.xsd</xsd>
                                    <bindingFile>src/main/resources/jaxb-binding.xjb</bindingFile>
                                    <packagename>ch.sahits.game.helloworld</packagename>
                                </xsdOption>
                            </xsdOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

The simple solution of course is to add --add-modules java.xml.ws as a VM option, but that is exactly what I am trying to get rid off to make my application future proof. What dependency do I have to use to resolve this issue? Or do I have change the bean configuration, so that the proper classes are looked up (and not the ones from com.sun.xml... )?

The MCVE for download as zip archive.

like image 801
hotzst Avatar asked Sep 01 '25 02:09

hotzst


2 Answers

Try adding the following:

<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.activation</groupId>
    <artifactId>javax.activation</artifactId>
    <version>1.2.0</version>
</dependency>

jaxb-core contains com.sun.xml.bind.v2.model.annotation.AnnotationReader (and seems to be a required dependency of jaxb-runtime, at least in your case), while javax.activation is needed by jaxb-api due to the usage of DataHandler by the latter.

Also, there is no a single bean class, so the marshaller will fail initialization. I've added the following

@XmlRootElement
public class MyBean {
}

and replaced

bean.setContextPath("ch.sahits.game.helloworld");

with

bean.setClassesToBeBound(MyBean.class);

after which the application has started.

like image 117
Roman Puchkovskiy Avatar answered Sep 02 '25 16:09

Roman Puchkovskiy


The following has worked for me

<dependency>
  <groupId>org.glassfish.jaxb</groupId>
  <artifactId>jaxb-runtime</artifactId>
  <version>2.3.0</version>
</dependency>
like image 21
Philippe Marschall Avatar answered Sep 02 '25 17:09

Philippe Marschall