Hi,

with the help of an answer on StackOverflow we have solved this. In
case anybody was watching this, here's what happened...

First some random facts:

* if not given a class loader
  (https://javaee.github.io/jaxb-v2/doc/user-guide/ch06.html#d0e6919),
  `JAXBContext::newInstance` will use [the thread's context class
  loader
(https://docs.oracle.com/javase/10/docs/api/java/lang/Thread.html#getContextClassLoader())
  when looking for the JAXB implementation - this is the case even if
  you call `newInstance(Class...)` (one might mistakenly think it uses
  the provided class instances' loader)
* Tomcat builds a small class loader hierarchy
  (https://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html)
  to separate web applications from one another
* by not relying on the module _java.xml.bind_, in Java 9, JAXB
  classes are not loaded by the bootstrap or system class loader

So here's what happened on Java 8:

* we don't pass a class loader to JAXB (oops), so it uses the
  thread's context class loader
* our conjecture is that Tomcat does not explicitly set the context
  class loader and so it will end up being the same one that loaded
  Tomcat: the system class loader
* that's dandy because the system class loader sees the entire JDK
  and hence the JAXB implementation included therein

Java 9 enters - the piano stops playing and everybody puts down their
scotch:

* we added JAXB as a regular dependency
  (https://stackoverflow.com/a/48204154/2525313) and so it is
  loaded by the web app's class loader
* just as on Java 8, JAXB searches the system class loader, though,
  and that one can't see the app's loader (only the other way around)
* JAXB fails to find the implementation and goes belly up

The solution is to make sure JAXB uses the right class loader. We know
of three ways:

* call

`Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader());`
  but that's not really a good idea
* create a context resolver

(https://docs.oracle.com/javaee/7/api/javax/ws/rs/ext/ContextResolver.html),
  but that requires JAX-WS and that feels like replacing one evil with
  another
* use the package-accepting variant of `JAXBContext::newInstance` that
  also takes a class loader and pass the correct loader, although that
  requires some refactoring

 so long ... Nicolai



On 26.07.2018 12:41, Nicolai Parlog wrote:
> Hi!
> 
> TL;DR: On Java 10.0.1, a web app in Tomcat 9.0.8.0 has no access to
> JAXB even though its reference implementation is present on the
> class path.
> 
> (This looks like a bug to me, but "Before you report a bug" urged
> me to ask here first. :) )
> 
> NOTE: I already asked [on 
> StackOverflow](https://stackoverflow.com/q/51518781/2525313), where
> the question is a little more readable.
> 
> On to the details...
> 
> # The Situation
> 
> We have a web app that runs on Tomcat and depends on JAXB. During
> our migration to Java 9 we opted for adding [the JAXB reference 
> implementation as a regular 
> dependency](https://stackoverflow.com/a/48204154/2525313).
> 
> Everything worked when launching the app from the IDE [with
> embedded 
> Tomcat](https://tomcat.apache.org/tomcat-9.0-doc/api/org/apache/catalina/startup/Tomcat.html),
>
> 
but when running it on a real Tomcat instance, I get this error:
> 
> Caused by: java.lang.RuntimeException:
> 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] at [... our-code ...] 
> Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API
> has not been found on module path or classpath. at
> javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.ContextFinder.find(ContextFinder.java:421) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at [... our-code ...] Caused by:
> java.lang.ClassNotFoundException: 
> com.sun.xml.internal.bind.v2.ContextFactory at 
> jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
>
> 
~[?:?]
> at 
> jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
>
> 
~[?:?]
> at java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[?:?] at 
> javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122)
>
> 
~[jaxb-api-2.3.0.jar:2.3.0]
> at 
> javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155)
>
> 
~[jaxb-api-2.3.0.jar:2.3.0]
> at
> javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.ContextFinder.find(ContextFinder.java:421) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at [... our-code ...]
> 
> Note:
> 
>> Implementation of JAXB-API has not been found on module path or
>> classpath.
> 
> These are the relevant files in `webapps/$app/WEB-INF/lib`:
> 
> jaxb-api-2.3.0.jar jaxb-core-2.3.0.jar jaxb-impl-2.3.0.jar
> 
> What is going on here?
> 
> # What I tried
> 
> ## Adding JARs
> 
> Maybe it helps to add the JARs to Tomcat's class path in
> `setenv.sh`?
> 
> CLASSPATH= .../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar: 
> .../webapps/$app/WEB-INF/lib/jaxb-impl-2.3.0.jar: 
> .../webapps/$app/WEB-INF/lib/jaxb-core-2.3.0.jar: 
> .../webapps/$app/WEB-INF/lib/javax.activation-1.2.0.jar
> 
> Nope:
> 
> Caused by: javax.xml.bind.JAXBException: ClassCastException:
> attempting to cast 
> jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class
> to 
> jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class.
>
> 
Please make sure that you are specifying the proper ClassLoader.
> at 
> javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:157)
>
> 
~[jaxb-api-2.3.0.jar:2.3.0]
> at
> javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:300) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:286) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.ContextFinder.find(ContextFinder.java:409) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at
> javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) 
> ~[jaxb-api-2.3.0.jar:2.3.0] at 
> de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.initializeCommandExtractor(DefaultWmsRequestFactory.java:103)
>
> 
~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
> at 
> de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.lambda$new$0(DefaultWmsRequestFactory.java:87)
>
> 
~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
> 
> That's clearly the same class, so apparently it has been loaded by
> two class loaders. I suspect [the system class loader and the app's
> class 
> loader](https://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html#Class_Loader_Definitions),
>
> 
but why would loading `JAXBContext` be delegated to the system class
> loader once but not always? It almost looks as if the delegation 
> behavior of the app's class loader changes while the program runs.
> 
> ## Adding the module
> 
> I don't really want to add _java.xml.bind_, but I tried it anyways
> by adding this to `catalina.sh`:
> 
> JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-modules=java.xml.bind"
> 
> Doesn't work either, though:
> 
> Caused by: java.lang.ClassCastException: 
> java.xml.bind/com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl 
> cannot be cast to com.sun.xml.bind.v2.runtime.JAXBContextImpl at
> [... our-code ...]
> 
> Apart from the different class and stack trace, this is in line
> with what happened earlier: The class `JAXBContextImpl` was loaded
> twice, once from _java.xml.bind_ (must have been the system class
> loader) and one other time (I assume by the app's loader from the
> JAR).
> 
> ## Searching for bugs
> 
> [Searching Tomcat's bug 
> database](https://bz.apache.org/bugzilla/query.cgi) I found 
> [#62559](https://bz.apache.org/bugzilla/show_bug.cgi?id=62559).
> Could that be the same error?
> 
> 
> 

-- 

PGP Key:
    http://keys.gnupg.net/pks/lookup?op=vindex&search=0xCA3BAD2E9CCCD509

Web:
    http://blog.codefx.org
        a blog about software development
    https://courses.codefx.org
        high-quality Java courses

Social:
    https://twitter.com/nipafx
    http://youtube.com/c/codefx

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to