27

In our application we sometimes get the following exception:

javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]

We already found out that this happens only if we use Collection.parallelStream() but not if we use Collection.stream().

We saw that JAXB uses Thread.currentThread().getContextClassLoader() for loading classes. We also saw, that when using parallelStream(), the threads for executing our command are using different class loaders. Sometimes it's a org.apache.catalina.loader.WebappClassLoader, and sometimes it's a jdk.internal.loader.ClassLoaders.AppClassLoader.

And it now seems, that the AppClassLoader does not know about the JAXB dependencies, whereas the WebappClassLoader does.

We are using Java 11 and the following Maven dependencies:

<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>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

Any idea what could be wrong? How can it be, that the AppClassLoader does not know about our dependencies?

Ethan Leroy
  • 15,804
  • 9
  • 41
  • 63

5 Answers5

9

We had a different experience. The issue was that tomcat has a different classloader than each war (I think we're seeing something similar with spring boot apps; void-main was different than that of application-runner)..

Either way, the issue is that the "bootstrap" classloader doesn't have access to the jars in your application - and thus NO jaxb. So if you ever launch a thread (like ForkJoinPoolThread from StreamXX.parallel() or ForkJoinPool.commonThreadPool()) then THOSE threads will be from the bootstrap classloader, NOT your application's classloader.. So if the FIRST time your background task loads JAXB, they will run getClass().getContextClassLoader().getResourceAsStream("xxxx") and will not find the resource.

Our solution was to launch all background tasks in an explicit thread-pool and have an explicit thread-pool factory.. The thread-pool-factory captures the classloader from the invoking thread (the one initialized by the war or the spring-boot context). This classloader WILL have jaxb and friends.. So now each thread launched from this thread-pool factory has an explicit thr.setContextClassLoader(globalCL);....

Problem solved (via a hack)

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
M. Maraist
  • 432
  • 4
  • 7
0

I had similar issue where I was getting ClassNotFoundException for org.apache.xerces.parsers.SAXParser. I had removed/excluded dependencies for xercesImpl,xml-api,xmlbeans,xmlschema-core and the issue is resolved, though I have no idea why and how it worked. What prompted me to remove those jars was XMLReaderFactory which was present in JDK 8 and xml-api.

Sachin Kumar
  • 808
  • 3
  • 11
  • 29
0

You are loading an older version of JAXB from somewhere. In Java 11 the context factory should be : com.sun.xml.bind.v2.ContextFactory

and not

com.sun.xml.internal.bind.v2.ContextFactory

https://github.com/eclipse-ee4j/jaxb-api/issues/78

Try doing the following things: upgrade your activation package to 1.2 . Not sure it has anything to do with it, but since you are using java 11 I would suggest using activation 1.2

Instead of plugging in glassfish implementation of jaxB. Use following 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-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.0</version>
</dependency>

OR alternativly

<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>2.3.3</version>
</dependency>

If this still does not work fallback to the first thing I wrote you:

correct context factory is:

com.sun.xml.bind.v2.ContextFactory

There is an old vesion somewhere there loaded.

Alexander Petrov
  • 9,204
  • 31
  • 70
-1

This class com.sun.xml.internal.bind.v2.ContextFactory was previously available in the file rt.jar till java 8, and then was trimmed out from jdk.


Possible cause: Your application uses some library, which uses some old library, which expects java 8, which contains xml.bind, which is trimmed out from jdk 11.


Workaround: You can simply copy file /usr/lib/jvm/jre-1.8.0-openjdk/lib/rt.jar from jdk 8, trim up a little its contents, saving only com.sun.xml.internal.bind.* packages. Then put this jar into your tomcat/lib folder.