We are trying to migrate an existing Eclipse IDE Product from Java 8 to Java 11.
Everything worked fine with our own code; but, unfortunately, the product also includes some (quite old) bundles from a third party. Those bundles have been built and are working under Java 8 and use javax.xml.bind
(JAXB) and we don't have access to the source code, so we cannot rebuild them for Java 11.
Now the problem is that those bundles assume that javax.xml.bind
is on the (Java Runtime Library) classpath (which was correct for Java 8), so they don't have an Import-Package
for JAXB in their manifests. When executed in our new Java 11 product, of course, this ends up in NoClassDefFoundException
s for the JAXB classes.
My research so far has ended up in two possibilities to solve this:
Add JAXB to the Java Classpath by providing it as a module and add a corresponding --add-modules
argument as VM argument in the eclipse.ini
.
For each legacy bundle, create a fragment with additional Import-Package
headers to add the JAXB import to each of the affected bundles.
The first solution does not work well for us, because our own bundles already use an Import-Package
header in the manifest to make use of JAXB which is available in our target platform as an OSGi bundle. If we added a JAXB module to Java, we would end up with problems that classes/packages are available in two modules (the unnamed one from the OSGi bundle, and the JAXB that we added via command line).
The second solution works, but the disadvantage is that we need to create N fragments for N legacy bundles, which adds quite some clutter to our codebase.
So my question is: Is there any other mechanism in Equinox that I can use to make some packages (JAXB) known to the BundleClassloader of certain bundles (our legacy bundles) without having to modify or rebuild these legacy bundles?
After some searching around the keywords given in BJ Hargrave's answer, we ended up finding this blog entry on vogella by Dirk Fauth. Variant 3 in that article describes the way we successfully applied.
In short,
system.bundle
and set it to be a framework
extensionMANIFEST.MF
of the fragment (excerpt):
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: JAXB framework extension
Bundle-SymbolicName: net.winklerweb.jaxbfix
Bundle-Version: 1.0.0.qualifier
Fragment-Host: system.bundle; extension:=framework
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-ClassPath: lib/jakarta.activation-api-1.2.2.jar,
lib/jakarta.xml.bind-api-2.3.3.jar,
lib/jaxb-impl-2.2_1.jar,
.
Export-Package: com.sun.istack,
com.sun.istack.localization,
com.sun.istack.logging,
pom.xml
of the fragment (excerpt):
<modelVersion>4.0.0</modelVersion>
<groupId>net.winklerweb</groupId>
<artifactId>net.winklerweb.jaxbfix</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>initialize</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<excludeTransitive>false</excludeTransitive>
<includeArtifactIds>jakarta.activation-api,jakarta.xml.bind-api,jaxb-impl</includeArtifactIds>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<silent>false</silent>
<outputDirectory>lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>true</runOnIncremental>
<runOnConfiguration>true</runOnConfiguration>
</execute>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2_1</version>
</dependency>
</dependencies>
Finally, a few useful links explaining OSGi/Equinox class loading which I have come across during my research: