Home > OS >  Augmenting the classpath of legacy OSGi bundles in Eclipse Equinox
Augmenting the classpath of legacy OSGi bundles in Eclipse Equinox

Time:04-05

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 NoClassDefFoundExceptions for the JAXB classes.

My research so far has ended up in two possibilities to solve this:

  1. 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.

  2. 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?

CodePudding user response:

The bundles should always have imported javax.* packages. Those bundles were always wrong.

Have you tried boot delegation along with adding JAXB to the Java classpath?

org.osgi.framework.bootdelegation=javax.xml.bind,javax.xml.bind.*

If there is already a bootdelegation value in place, you will need to add javax.xml.bind,javax.xml.bind.* to the existing value.

Since you also have bundles which import the javax.xml.bind packages, you will also need to make sure the system bundle exports them. So you will need to configure org.osgi.framework.system.packages.extra property with all the JAXB packages including versions if necessary for your imports.

org.osgi.framework.system.packages.extra=javax.xml.bind;version=1.2.3, ...

CodePudding user response:

After some searching around the keywords given in BJ Hargrave's answer, we ended up finding this blog entry by Lars Vogel. Variant 3 in that article describes the way we successfully applied.

In short,

  • create a fragment to system.bundle and set it to be a framework extension
  • put the libraries you need on the classpath in that fragment (or configure the POM to let Maven do it for you)
  • export the packages you need exported

MANIFEST.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:

  • Related