It starts off really easy: java.* packages are always obtained from the boot class loader.
But then, the OSGi specification (section 3.8, "Runtime Class Loading") gets more interesting: For other packages in the JRE, e.g. javax.xml, bundles need to specify a package import in order to be able to use these packages. This was surprising to me because I had used javax.* packages in bundles without declaring the corresponding package imports. Through tests I could verify that this is in fact possible in an Equinox runtime. But obviously the Equinox guys did not accidentally deviate from the OSGi specification, but chose to do so for compatibility reasons: If a class could not be found by any class loader (as specified by OSGi), Equinox asks the boot class loader as last resort. This compatibility option can be disabled by setting the property osgi.compatibility.bootdelegation to false. Nevertheless, for OSGi-compliance, bundles shall declare imports for all used non-java.* packages.
This led me to the question, how imports of JRE packages are resolved. I learned from the OSGi specification that packages from the parent class loader are exported by the system bundle. The list of exported packages is computed by the OSGi framework (unless that list is explicitly specified via the property org.osgi.framework.system.packages). In Equinox, there is a static list of packages for each execution environment. These lists can be found as *.profile files in the root of the org.eclipse.osgi bundle. So if Equinox is running in a Java 1.6 JRE, the system bundle will, amongst others, export the javax.xml package, and a bundle with an "Import-Package: javax.xml" manifest header can load the class javax.xml.XMLConstants via the system bundle from the boot class loader.
I would have stopped exploring here, if I had not come across a bundle that declares an import of a JRE package with version constraint: "Import-Package: javax.xml.stream;version="[1.0.1, 2.0.0)"". Unfortunately, this import will not be resolved against the system bundle because in Equinox all JRE packages are exported in version 0.0.0 only. Version 0.0.0 is definitely wrong, but so would be 1.6.0 (think of the packages org.w3c.dom or javax.xml.ws included in JavaSE-1.6). I can understand that Equinox refuses to do Oracle's homework and to add version information for the packages from the Java runtime. It's quite sad that Sun has never come around to apply their own package versioning mechanism to the JRE packages: java.lang.Package.getPackage("javax.xml") returns null in a 1.6 Sun JDK. This leaves us OSGi users in quite a mess: In order to use JRE packages from a bundle, we shall declare imports for them, but we may not place any (reasonable) version constraints on them.
But what if someone else's bundle does a versioned import of a package that is (nowadays) part of the JRE? I could think of several options:
- Just install a bundle that exports the package in a properly declared version (and hope that the duplicate classes never cross their path...).
- Alternatively, it should also be possible to make the system bundle export JRE packages with a correct version (given that you know the correct version...), by setting the (standard OSGi) property org.osgi.framework.system.packages.extra or by installing a fragment to the system bundle that just adds a package export.