Exposing the boot classpath in OSGi |
|

A fairly common question that I get from time to time is how to go about using JDK specific classes inside an OSGi environment. To some degree, this is equivalent to getting access to the bootstrapping classpath from OSGi, without bundling it. To express package dependencies, bundles use the OSGi directive inside their manifests – mainly Export-Package and Import-Package for providing and demanding, a respectively, a class package dependency. Defining a bundle wiring is a crucial step for creating a modular application; however there are cases, as the issue above, where the needed package is not available from a bundle.
NoClassDefFoundError: com.sun…
Notable examples of such packages would be the sun.* and com.sun.*, present in the JDK jars. Even though these are internal packages and are not guaranteed to be portable, some of them can be found even in non-Sun JDKs, due to their usage. Your application might not use them, but there are various libraries that do (in some cases due to performance, in others because it's the only way to achieve a certain functionality). If the using bundle declares an import on the com.sun package, it will fail to resolve since there are no providers for it. If the import is not declared, since the bundle doesn't contain the class definition, the loading process will usually fail. Clearly the packages above are not a corner case; generalizing the example, the packages available in the OSGi framework boot classpath are not visible to the OSGi environment. There are several solutions to this problem but first, let's take a closer look to see why it occurs.
Class Space
In OSGi, each module has its own class loader used for loading resources and classes. Based on the wiring directive, the platform creates a delegation network between the various modules. The network forms a class space which represents (quoting the OSGi spec) : "all classes reachable from a given bundle's class loader" or in layman terms, what a bundle can see, the bundle world view. The networks can intersect as the same package can be loaded by multiple bundles; however each space must be consistent, a requirement enforced by platform during the resolving phase of each bundle. One of the side effects (or aims) of the networking model is type isolation or class versioning: multiple version of the same class can coexist nicely inside the same VM since each one is loaded into its own network, its own space.
However, there are classes that need to be loaded differently such as java.* packages. These classes are part of, and thus implicitly required by, the Java runtime itself. Each Java object for example, is a subclass of java.lang.Object which practically means that each bundle uses at least one Java package (java.lang). While such a dependency could be expressed through a directive in the bundle manifest, due to its mandatory nature, it becomes undesirable. That's why the java.* packages are considered implied imports and can be loaded even though they are not declared, by each bundle. In fact, the OSGi spec forbids bundles to specify an import on java.* since class wiring always implies versioning which means running multiple Java versions inside the same VM which is not possible (at least not today).
For loading these fundamental types, the OSGi platform uses parent delegation instead of the networking model; that is, it uses the class loader that started the OSGi framework to load classes instead of an OSGi class space. As this may seem more complicated then it actually is, I've created a diagram using the dot language:
As can be seen above, this loading model is quite different from the traditional Java convention that relies on parent delegation to resolve classes for all packages not just java.* . The bundles communicate with each other based on their wiring, while delegating the loading of special types to the parent class loader (the green arrows in the image)
Solution A: System Packages
The careful reader might have noticed that only java.* packages have been mentioned – none of the other public packages available in the JDK such as the javax.net or javax.xml are parent delegated which means they have to be resolved within the class space. That is, bundles need to import the packages (which means there
needs to be a provider) since they are not implied. The OSGi spec allows the Framework (through its system bundle) to export any relevant packages from its parent class loader as system packages using the org.osgi.framework.system.packages property.
As repacking the hosting JDK as a bundle isn't a viable option, one can use this setting to have the system bundle (or the bundle with id 0) export these packages itself. Most of the OSGi implementations already use this property to export all the public JDK packages (based on the detected JDK version). Below is a snippet from an Equinox configuration file for Java 1.6:
org.osgi.framework.system.packages = \
javax.accessibility,\
javax.activity,\
javax.crypto,\
javax.crypto.interfaces,\
...
org.xml.sax.helpers
Using this property, one can add extra packages that will be loaded and provided by the framework and that can be wired to other bundles.
org.osgi.framework.system.packages = \
javax.accessibility,\
javax.activity,\
...
org.xml.sax.helpers, \
special.parent.package
as can be seen by asking the system bundle (below a snippet from the OSGi console in Equinox):
osgi> bundle 0
System Bundle [0]
Id=0, Status=ACTIVE
Registered Services
...
Exported packages
...
org.xml.sax.helpers; version="0.0.0"[exported]
special.parent.package; version="0.0.0"[exported]
...
The setting needs to be initialized before the OSGi framework starts so a common pattern is to set this as a system property. This approach will override the default configuration so the upcoming OSGi 4.2 defines another property, named org.osgi.framework.system.packages.extra which will append the defined system packages to the org.osgi.framework.system.packages configuration, making it easier to extend the configuration defined already by the OSGi implementation. Adding new packages can be
as simple as passing an argument to the VM starting the platform:
java -Dorg.osgi.framework.system.packages.extra=special.parent.package;version=1.0 ...
Let's check the package again from the OSGi console:
osgi> packages special.parent.package
special.parent.package; version="1.0.0" <org.eclipse.osgi_3.5.0.v20081201-1815 [0]>
Solution A': Extension Bundles
Another possible option is to enhance the system bundle through extension bundles. These act as fragments; they are not bundles of their own but rather are attached to a host. Once attached, the fragment content (including any permitted headers) is treated as part of the host. Extension bundles are a special kind of fragments that get attached only to the System bundle in order to deliver optional parts of the Framework (such as the Start Level service). One can use this mechanism to create an empty extension that just declares the needed packages, leaving the loading to its hosting bundle (in this case the Framework):
osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.5.0.v20081201-1815 Fragments=1 1 RESOLVED a.framework.extension_0.0.0 Master=0 osgi> bundle 1 a.framework.extension_0.0.0 [1] Id=1, Status=RESOLVED Data Root=... No registered services. No services in use. Exported packages <b>special.parent.package; version="0.0.0"[exported]</b> No imported packages Host bundles <b>org.eclipse.osgi_3.5.0.v20081201-1815 [0]</b> No named class spaces No required bundles osgi> headers 1 Bundle headers: Bundle-ManifestVersion = 2 Bundle-SymbolicName = a.framework.extension <b>Export-Package = special.parent.package</b> <b>Fragment-Host = system.bundle; extension:=framework</b> Manifest-Version = 1.0
Notice the special host symbolic name and extra attribute in the Fragment-Host header above. This tells the Framework that the bundle is not just an ordinary fragment but an extension bundle.
Once attached, the relevant extension manifest directives get merged with that of the system bundle, its host:
osgi> packages special.parent.package special.parent.package; version="0.0.0"<org.eclipse.osgi_3.5.0.v20081201-1815 [0]>
Solution A' is basically a variant of A (hence the name) – instead of using system properties, one can use fragment bundles to extend the system bundle which can be more convenient in some cases.
It's worth pointing out that the extension bundles might perform loading using the Java boot class path, an optional mechanism defined by the specification which is not required for compliant implementations. However, at the moment, none of the OSGi frameworks that I have tried, implement this feature.
The main advantage of both solutions is that the package is provided (and thus versioned) inside OSGi. The convention is to use the default version (0.0.0) for system packages, however, this is not mandatory (as can be seen above). A powerful side effect is the ability to provide a different, more up to date version of the package declared by the framework, through a different bundle. We used this to solve an issue with transaction data access caused by the fact that the JDK comes with an incomplete version of javax.transaction package which gets exported automatically, by the framework, inside the OSGi environment:
osgi> packages javax.transaction
javax.transaction; version="0.0.0"<org.eclipse.osgi_3.5.0.v20081201-1815 [0]>
The solution is to install a bundle that contains the full javax.transaction API with a higher version:
osgi> packages javax.transaction
javax.transaction; version="0.0.0"<org.eclipse.osgi_3.5.0.v20081201-1815 [0]>
javax.transaction; version="1.1.0"<com.springsource.javax.transaction_1.1.0 [1]>
so that consuming bundles can use it instead of the JDK bundled one:
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20081201-1815
1 ACTIVE com.springsource.javax.transaction_1.1.0
2 ACTIVE user.bundle_0.0.0
osgi> headers 2
Bundle headers:
Bundle-ManifestVersion = 2
Bundle-SymbolicName = user.bundle
Import-Package = javax.transaction;version=1.0
Manifest-Version = 1.0
osgi> packages javax.transaction
javax.transaction; version="0.0.0"<org.eclipse.osgi_3.5.0.v20081201-1815 [0]>
javax.transaction; version="1.1.0"<com.springsource.javax.transaction_1.1.0 [1]>
user.bundle_0.0.0 [2] imports
See the Spring DM FAQ section for more information.
Solution B: Boot Delegation
Another option supported by OSGi is boot delegation, which you already saw for java.* packages. This allows the user to create 'implied' packages that will always be loaded by the framework parent class loader, even if the bundles do not provide the proper imports:
This option is mainly created to accommodate various corner cases especially in the JDK classes that expect parent class loading delegation to always occur or that assume that every class loader on the system has full access to the entire boot path. Packages sun.* and com.sun.* are the two most common examples (as I've already mentioned) and for this reason some OSGi implementations (i.e. Equinox) enable them by default:
org.osgi.framework.bootdelegation=sun.*,com.sun.*
As a side note, Spring DM uses the same setting as well, by default, inside its integration testing framework (AbstractConfigurableOsgiTests#getBootDelegationPackages())
Which solution is better ?
Each of the solutions above should work for most cases; however, I strongly recommend the A/A' approaches: they clearly express the bundle wiring and allow extensibility. The wiring is easy to control, detect and diagnose. Solution B acts as a bit of black magic since the bundle cannot control its loading and pick a certain version or provider since there is no class wiring in place. Moreover, the setting affects all bundles which might not be always what you want.
Nevertheless, there are cases where boot delegation is quite handy; a good candidate being instrumentation, such as profiling or code coverage. The majority of tools use byte code weaving to add various counters or to intercept the execution flow. The 'instrumented' bundles cannot be loaded inside OSGi without updating their manifest since the newly added code refers to classes unknown to the bundle. Adding the custom packages to the boot delegation list offers a very quick way to instrument OSGi applications without having to change the packaging or the deployment process.
Note on the parent class loader
In this entry, I have referred to the parent class loader as the entity that loads and starts (or boots) the OSGi framework, following the terminology of the OSGi specification. It's worth noting that some OSGi implementations (specifically Equinox) allow customization of the parent class loader to different values (such as the application, boot or extension class loader).
Links
For more information on OSGi class loading, please see the links below:
- OSGi core specification, sections 3.8, 3.14 and 3.15
- ClassLoader API
- Eclipse Runtime Options (specifically osgi.parentClassLoader)
P.S. There is no code listing for this entry but code aficionados can grab the graph definitions here.
Similar Posts
- Using an OSGi Profile with Bundlor
- Deploying WARs to the OSGi Web Container is now even easier
- Understanding the OSGi
uses
Directive - Diagnosing OSGi uses conflicts
- The dm Shell







Oleg Zhurakousky (blog author) says:
Added on January 19th, 2009 at 4:00 pmNOTE on Bootdelegation and Eclipse PDE.
To avoid the confusion I wanted to make sure readers are made aware of the following:
When starting Equinox inside the Eclipse a special Launcher (org.eclipse.equinox.launcher) is used. It is started with some Equinox properties where some of them are set by default to their default values. One of such properties is: osgi.compatibility.bootdelegation which (if set to "true" – default) delegates to the parent CL by default if class can not be found regardless if bootdelegation option was enabled. This means that while starting Equinox inside of Eclispe all classes visible to boot class loader (javax.*, org.w3c.* etc…) will be found.
To ensure full compatibility with OSGi and to experiment with botdxelegation option properly set osgi.compatibility.bootdelegation to "false" (i.e., -Dosgi.compatibility.bootdelegation=false) when starting the Equinox inside of Eclipse.
Simon Kaegi says:
Added on January 19th, 2009 at 4:15 pmAnother fairly elegant solution is to use an empty framework extension bundle that just provides the various potentially versioned exports. The servletbridge uses this technique to expose the servlet api and potentially any other useful packages from the app server.
Costin Leau (blog author) says:
Added on January 19th, 2009 at 6:31 pm@Oleg: Thanks for the explanation on Equinox usage inside Eclipse
@Simon: Thanks Simon – I've updated the blog entry with an explanation on how extension bundles can be used (solution A' above).
Cheers and thanks again for the comments!
Senthil says:
Added on February 25th, 2009 at 1:30 amI am looking at OSGi web extender release version. It is using the Spring core, context etc of alpha version. Could u guide me to correct osgi web bundles depending on released versions of Spring core, context etc?
Thanks,
Senthil
Costin Leau (blog author) says:
Added on February 25th, 2009 at 1:39 am@Senthil, please use the dedicated forum (http://forum.springframework.org/forumdisplay.php?f=43)
Neil Bartlett says:
Added on September 16th, 2009 at 7:40 amI agree that approaches A/A' are far better than B. For the bundle using these packages it keeps things simple: if you use ANY package (other than java.*) then simply import it! It is up to the deployer or administrator to ensure the packages are supplied from somewhere, in this case from the system bundle.
A good use-case for this technique is when an OSGi Framework is embedded in a larger Java application, e.g. a JEE application server. We can expose classes and interfaces such as the EJB APIs to the OSGi bundles. Again, the bundles simply import package javax.ejb and the deployer sorts out the details.
Mani says:
Added on November 30th, 2010 at 5:13 amHi, While running the struts application with osgi in tomcat the following exception raised when particularly
When starting the bundle “32 INSTALLED com.springsource.javax.servlet.jsp.jstl_1.2.0″
Missing Constraint: Import-Package: org.w3c.dom.traversal; version=”0.0.0″
Thankful to you for any help….
Gergely Nagy says:
Added on January 24th, 2011 at 12:33 pmVery useful article, nice explanation – thank you very much.
What about compile time enforcement of these constraints? Given the above configuration is based on aruntime property, how can, say Eclipse PDE, pick up the visibility rules and complain when something isn't explicitly imported (rather than deferring to a runtime crash)?
Costin Leau (blog author) says:
Added on January 25th, 2011 at 11:52 am@Gergely
That's up to the tool really but it's not that easy since it would have to be aware not just of the OSGi environment but also about the 'outside OSGi' world. Here there are no bundles to interogate so it's hard to know whether the package is/will be available or not.
Note that I haven't used Eclipse PDE much but I believe it does not support yet boot classpath delegation but I might be wrong – it's best to raise the issue with them.
Robert Saenger says:
Added on January 27th, 2011 at 3:36 amWhat is the best way to extend the eclipse project classpath, to provide classes that shall be provided as system classes, e.g. where did you specify the javax.transaction-1.1.0.jar to be taken into account.
The only way I achieved it, was to declare it as VM argument -Xbootclass/a:/javax.transaction-1.1.0.jar in the OSGi Framework Run Configuration, since it was not sufficient to declare it as required PDE project build path library.
Gergely Nagy says:
Added on February 10th, 2011 at 7:47 amThanks Costin.
> it's not that easy since it would have to be aware not just of the OSGi environment
> but also about the 'outside OSGi' world. Here there are no bundles to interogate
> so it's hard to know whether the package is/will be available or not.
Right, but in case of Eclipse PDE, it seems to capture everything via Target Platform and Execution Environment(JRE and all that), so it should have a fairly precise idea of what comes from where; both at run- and compile-time. At least I thought so.
Anyhow, I tried to implement A' using PDE, but it's complaining about that JRE pkgs are not available for Export-Package in the (empty) framework extension fragment. Could you show me the manifest for that?
Another minor thing: would you be able to clean up the examples from those noisy -s? (or am I the only one seeing those?)
> it's best to raise the issue with them
Sure I will. Just your examples seem to use Equinox, so I thought you might have run into this. ( you seem to be more responsive than Eclipse newsgroups
Thank you very much, again.
Nikita says:
Added on September 29th, 2011 at 11:52 amThanks for a great article. But I have some problems putting this to work.
We develop some java agent, which uses Java Instrumentation API to instrument loaded classes, which then, in some point in the future, should call back some classes from our agent. Agent adds itself to bootclassloader using instrumentation.appendToBootstrapClassLoaderSearch .
When putting this agent to fresh new Eclipse Virgo installation and trying to start it, we get
org.osgi.framework.BundleException: Bundle org.eclipse.equinox.region 1.0.0.v20110503 failed to start.
….
Caused by: java.lang.ClassNotFoundException: XXX_OurClassHere_XXX
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
I have tried to provide
-Dorg.osgi.framework.bootdelegation=sun.*,com.sun.*,our.package.*
to JAVA_OPTS before starting Virgo, but to no avail. -Dorg.osgi.framework.system.packages.extra did not help either.
Can you point me to some way to make our agent's classes visible to OSGI container and applications inside it?
Hope to hear from you soon,
Nikita
Craig Foote says:
Added on July 16th, 2012 at 1:25 pmI have the same problem as described by Gergely Nagy:
Following the example above, I created a fragment with the magic host header, added my Export-Package and then got an error stating "Package does not exist in this plugin". How can I get around this? I looked at a project-specific setting to turn off validation for missing Export-Package but I think it's just a warning that you haven't exported all your packages; didn't make a difference.
What can I do to get rid of this MANIFEST.MF error?
Craig
Marry says:
Added on November 16th, 2012 at 8:37 pmGreetings! The site is great. Thank you for a great resource
Mohit says:
Added on May 6th, 2013 at 6:54 amDo we have method where we can expose a user defined class to Parent ClassLoader?
Actually I have a user defined class which I am passing to JNDI API as a parameter. THis user defined class is loaded in one of the classes of JDK. Since JDK classloader doesnot have access to userdefined class, it is not able to loas this new class. I guess this API works fine in now OSGI environment