I was trying to use javax.xml.bind
in a plugin I’m developing. Everything was working fine in my functional tests. When I tried building a project that applies the plugin, it failed due to missing classes.
It was working for the tests because I have java.toolchain.languageVersion
set to 8 in my plugin build, thus running with Java 8, where javax.xml.bind
is still part of the standard library. It was failing for the project applying the plugin because I actually have Java 11 installed on the machine, where javax.xml.bind
was removed from the standard library and has to be added as a an external dependency. (Why compile works with the installed Java 11, but languageVersion
set to 8 is not really clear to me.)
I added an implementation dependency to my plugin:
dependencies {
implementation("com.sun.xml.bind:jaxb-ri:2.3.9") {
because("javax.xml.bind is not part of the JDK anymore starring with JDK9")
}
}
I have two places in my plugin where I use JAXB. One place is in a task:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class WriteCompileSpecFile extends DefaultTask {
// ...
@TaskAction
protected void generate() {
DefaultHDVLCompileSpec compileSpec = new DefaultHDVLCompileSpec(getSvSource().getFiles());
try {
JAXBContext jaxbContext = JAXBContext.newInstance(DefaultHDVLCompileSpec.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
FileAdapter fileAdapter = new FileAdapter(getProject().getProjectDir());
marshaller.setAdapter(fileAdapter);
marshaller.marshal(compileSpec, destination.get().getAsFile());
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
I can execute this task without any problems.
The other place where I use JAXB is in an artifact transform:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public abstract class WriteXrunArgsFile implements TransformAction<TransformParameters.None> {
// ...
private static DefaultHDVLCompileSpec getCompileSpec(File input) {
File compileSpec = new File(input, ".gradle-hdvl/compile-spec.xml");
try {
JAXBContext jaxbContext = JAXBContext.newInstance(DefaultHDVLCompileSpec.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
FileAdapter fileAdapter = new FileAdapter(input);
unmarshaller.setAdapter(fileAdapter);
DefaultHDVLCompileSpec result = (DefaultHDVLCompileSpec) unmarshaller.unmarshal(compileSpec);
for (File svSourceFile : result.getSvSourceFiles()) {
assert svSourceFile.isAbsolute() : "not absolute: " + svSourceFile;
assert svSourceFile.exists() : "doesn't exist: " + svSourceFile;
}
return result;
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
When the artifact transform gets executed, I get:
> Execution failed for WriteXrunArgsFile: /home/tudor/.gradle/caches/transforms-4/2f8f369cd18dbcfb90e34fd250b46eaa/transformed/some-published-dependency-0.1.0.zip.
> 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.bind.v2.ContextFactory]
I don’t understand why it works in one case (the task), but not in the other (the artifact transform). I even had a look at the JAR file pulled in by the dependency and I can find ContextFactory
in there. This is the build scan: Build Scan® | Develocity.
To get this to work I just switched to com.sun.xml.bind:jaxb-ri:3.0.2
, though this also required changing the namespace, so I’m not blocked by this issue.
I would just like to figure out what is going on, to maybe learn a bit more about how Gradle works. I was also a bit surprised by the entire Java EE/Jakarta situation and the backward incompatibility and the dependency hell. I always had the impression that Java highly values backward compatibility.