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