jaxb-api
jaxb-api copied to clipboard
`ContextFinder` cannot find implementation when thread context class loader contains no JAXB jars
Relates to #99 (maybe even the same as that issue, but the issue still occurs with the latest jaxb-api version)
Version
-
jakarta.xml.bind:jakarta.xml.bind-api:4.0.2
-
org.glassfish.jaxb:jaxb-runtime:4.0.5
JDK 21
Description
JAXB's ContextFinder
is unable to find the JAXBContext
implementation when the thread context class loader contains none of the JAXB jars. JAXBContext.newInstance
fails with:
Exception in thread "main" jakarta.xml.bind.JAXBException: Implementation of Jakarta XML Binding-API has not been found on module path or classpath.
- with linked exception:
[java.lang.ClassNotFoundException: org.glassfish.jaxb.runtime.v2.ContextFactory]
at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:250)
at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:238)
at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:386)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:605)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:546)
at org.example.Main.main(Main.java:17)
Caused by: java.lang.ClassNotFoundException: org.glassfish.jaxb.runtime.v2.ContextFactory
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at jakarta.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:113)
at jakarta.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:146)
at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
... 5 more
The problem might be that jakarta.xml.bind.ServiceLoaderUtil
uses only ServiceLoader.load(Class)
, which implicitly uses the thread context class loader. Maybe it should fall back to using ServiceLoader.load(Class, ClassLoader)
(and use ServiceLoaderUtil.class.getClassLoader()
)? But I am not sure if that could have any undesired implications.
Reproduction steps
Create a small Maven project:
-
/pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>temp-jaxb-test</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>4.0.5</version> </dependency> </dependencies> </project>
-
/src/main/java/org/example/Main.java
package org.example; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.annotation.XmlRootElement; import java.net.URL; import java.net.URLClassLoader; public class Main { @XmlRootElement static class DummyClass {} public static void main(String[] args) throws Exception { ClassLoader parentClassLoader = null; Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0], parentClassLoader)); var context = JAXBContext.newInstance(DummyClass.class); var marshaller = context.createMarshaller(); marshaller.marshal(new DummyClass(), System.out); } }
Then run Main.main
; it will fail with the JAXBException
mentioned above.
(this is a bit contrived example, but I hope it matches the actual use case described in the "Additional context" section below close enough)
Additional context
I am currently experiencing this while working on a plugin for Jenkins. As described in the Jenkins documentation the context class loader normally does not contain the plugin classes (and therefore not the JAXB classes used by the plugin).
It seems many Jenkins plugins have worked around this by temporarily changing the context class loader, see for example https://issues.jenkins.io/browse/JENKINS-68514. Most of them have done that for the old javax.xml.bind
package, but it probably applies to the new jakarta.xml.bind
package name as well. At least for the plugin I am working on that seems to be the case.
@maloewe-ona @lukasj Maybe related with #243 ??
Regards, Antonio.
Yes, being able to specify the ClassLoader
to use would probably help (unless JAXBContext
is used in third-party code you cannot edit). But it would of course be even better if it just worked automatically without having to explicitly specify a ClassLoader
, in case that is possible.