How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
I have some code that uses JAXB API classes which have been provided as a part of the JDK in Java 6/7/8. When I run the same code with Java 9, at runtime I get errors indicating that JAXB classes can not be found.
The JAXB classes have been provided as a part of the JDK since Java 6, so why can Java 9 no longer find these classes?
The JAXB APIs are considered to be Java EE APIs and therefore are no longer contained on the default classpath in Java SE 9. In Java 11, they are completely removed from the JDK.
Java 9 introduces the concepts of modules, and by default, the java.se
aggregate module is available on the classpath (or rather, module-path). As the name implies, the java.se
aggregate module does not include the Java EE APIs that have been traditionally bundled with Java 6/7/8.
Fortunately, these Java EE APIs that were provided in JDK 6/7/8 are still in the JDK, but they just aren't on the classpath by default. The extra Java EE APIs are provided in the following modules:
java.activation
java.corba
java.transaction
java.xml.bind << This one contains the JAXB APIs
java.xml.ws
java.xml.ws.annotation
Quick and dirty solution: (JDK 9/10 only)
To make the JAXB APIs available at runtime, specify the following command-line option:
--add-modules java.xml.bind
But I still need this to work with Java 8!!!
If you try specifying --add-modules
with an older JDK, it will blow up because it's an unrecognized option. I suggest one of two options:
- You can set any Java 9+ only options using the
JDK_JAVA_OPTIONS
environment variable. This environment variable is automatically read by thejava
launcher for Java 9+. - You can add the
-XX:+IgnoreUnrecognizedVMOptions
to make the JVM silently ignore unrecognized options, instead of blowing up. But beware! Any other command-line arguments you use will no longer be validated for you by the JVM. This option works with Oracle/OpenJDK as well as IBM JDK (as of JDK 8sr4).
Alternate quick solution: (JDK 9/10 only)
Note that you can make all of the above Java EE modules available at run time by specifying the --add-modules java.se.ee
option. The java.se.ee
module is an aggregate module that includes java.se.ee
as well as the above Java EE API modules. Note, this doesn't work on Java 11 because java.se.ee
was removed in Java 11.
Proper long-term solution: (JDK 9 and beyond)
The Java EE API modules listed above are all marked @Deprecated(forRemoval=true)
because they are scheduled for removal in Java 11. So the --add-module
approach will no longer work in Java 11 out-of-the-box.
What you will need to do in Java 11 and forward is include your own copy of the Java EE APIs on the classpath or module path. For example, you can add the JAX-B APIs as a Maven dependency like this:
<!-- API, java.xml.bind module -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
See the JAXB Reference Implementation page for more details on JAXB.
For full details on Java modularity, see JEP 261: Module System
For Gradle or Android Studio developer: (JDK 9 and beyond)
Add the following dependencies to your build.gradle
file:
dependencies {
// JAX-B dependencies for JDK 9+
implementation "jakarta.xml.bind:jakarta.xml.bind-api:2.3.2"
implementation "org.glassfish.jaxb:jaxb-runtime:2.3.2"
}
In my case (spring boot fat jar) I just add the following to pom.xml.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
None of these solutions worked fine for me in the recent JDK 9.0.1.
I found that this list of dependencies is enough for a proper functioning, so you don't need to explicitly specify --add-module
(though it is specified within these dependencies's pom's). The only you need is to specify this list of dependencies:
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
Clean solution for all JDKs >= 9
You need to add two dependencies to your build
- the jaxb-api
- a jaxb implementation
As an implementation I chose to use the reference implementation by glassfish to get rid of old com.sun classes / libraries. So as a result I added in my maven build
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
Note that from version 2.3.1 you don't need to add the javax.activation any longer. (see https://github.com/eclipse-ee4j/jaxb-ri/issues/1222)
This worked for me:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.7.0</version>
</dependency>
Update
As @Jasper suggested, in order to avoid depending on the entire EclipseLink library, you can also just depend on EclipseLink MOXy:
Maven
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.7.3</version>
</dependency>
Gradle
compile group: 'org.eclipse.persistence', name: 'org.eclipse.persistence.moxy', version: '2.7.3'
As dependencies for my Java 8 app, which produces a *.jar which can be run by both JRE 8 or JRE 9 with no additional arguments.
In addition, this needs to be executed somewhere before JAXB API will be used:
System.setProperty("javax.xml.bind.JAXBContextFactory", "org.eclipse.persistence.jaxb.JAXBContextFactory");
Works great so far, as a workaround. Doesn't look like a perfect solution though...