JAXB not available on Tomcat 9 and Java 9/10
Try the following and its dependencies. See a Maven repository for latest version.
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0.1</version>
</dependency>
It also contains the Java Service Loader descriptors. See Using JAXB in Java 9+
Analysis
First some random facts:
- if not given a class loader,
JAXBContext::newInstance
will use the thread's context class loader when looking for the JAXB implementation - this is the case even if you callnewInstance(Class...)
(one might mistakenly think it uses the provided class instances' loader) - Tomcat builds a small class loader hierarchy 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 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
Solution
The solution is to make sure JAXB uses the right class loader. We know of three ways:
- call
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
but that's not really a good idea - create a context resolver, but that requires JAX-WS and that feels like replacing one evil with another
- use the package-accepting variant of
JAXBContext::newInstance
(Javadoc from Java EE 7) that also takes a class loader and pass the correct loader, although that requires some refactoring
We used the third option and refactored towards the package-accepting variant of JAXBContext::newInstance
. Menial work, but fixed the problem.
Note
User curlals provided the critical piece of information, but deleted their answer. I hope it was not because I asked for a few edits. All credit/karma should go to them! @curlals: If you restore and edit your answer, I will accept and upvote it.