‘OSGi Demystified’ is a series of articles addressing common OSGi issues in CICS. We offer insight into OSGi, discuss best practices, and provide setup and configuration advice. This is the second in the series of section 2, to see the previous section discussing CICS bundles, Liberty bundles, and OSGi services, here’s the link to the first in the section. In this section of articles we address a number of common Java and OSGi class loader problems you might encounter in CICS OSGi and Liberty JVM servers. This follows on from OSGi Demystified: 2.1 – OSG-sigh. To catch up on this section, follow the link.
NoClassDefFoundErrorissues beyond class path clumsiness.
- Boot delegation – the act of delegating class loading to OSGi’s boot class loader.
- Boot delegation in CICS.
NoClassDefFoundError – beyond minor class path errors
Looking beyond class path clumsiness, a
NoClassDefFoundError will generally indicate that you failed to declare a dependency on another package. Your bundle does not go looking for classes from other bundles unless you tell it to with an Import-Package statement in your MANIFEST.MF. Maybe your class depends on a super class or has a member from another package that is not available within the same bundle? In which case, you might hope that your development tooling would spot the external dependency. Usually it does, but unfortunately not always. Some development environments such as Eclipse (and by inference CICS Explorer) can be too tolerant and donâ€™t always perform the strict OSGi compliance required to flag up missing dependencies.
For example, letâ€™s say your application uses Java Extensions. At coding time, if you use an object from an extension package, youâ€™ll want to add an import for that particular javax.* package to your source-code. Failure to add the import is obvious from the red-X. Clicking the â€śI donâ€™t know what Iâ€™m doing â€“ fix it for meâ€ť suggestion adds the import to your Java source. However, the same is not true for OSGi dependencies. Eclipse wonâ€™t notice if you fail to add the javax.*package to your bundleâ€™s Import-Package statement. That particular omission is mysteriously tolerated as explained next…
The mystery explained with boot delegation
This situation arises because the OSGi framework running in your Eclipse development environment has been configured, by default, to delegate all javax.* package requests to the parent class loader of the OSGi environment. In OSGi terms the parent class loader is called the boot class loader. Don’t confuse it with Java’s bootstrap class loader. The boot class loader is just the OSGi name for whichever class loader loaded the OSGi framework. The act of delegating class loading to OSGi’s boot class loader is referred to as boot delegation.
Now, because the parent class loader of the OSGi framework is usually Java’s application (or system) class loader, delegating class load requests to it will break out of OSGi’s network of peer (OSGi bundle) class loaders, and back to standard Java class loading rules. If you weren’t already aware, Java performs parent-first class loading. So the application class loader delegates to the extensions class loader and the extensions class loader delegates to the bootstrap class loader, and the bootstrap class loader is connected to the elbow…er, well you get the idea.
Typically, core JRE classes are on the class path of the bootstrap class loader and javax.* packages are on the class path of the extensions class loader. Each request has to go all the way up the hierarchy, before rippling back down until the load request can be fulfilled. Keep that parent-first search pattern in mind.
In a strict OSGi runtime, boot delegation of javax.* does not occur. That’s because the act of boot delegation precludes the existence of an alternate implementation or a newer version of a package. OSGi would look directly to the boot class loader hierarchy and due to the parent-first class loading, it would likely find the (original) class on the extensions class loader and ignore the bundle where your preferred and versioned implementation of an extension package resides. Boot delegation does this even if you are a good citizen and declare your bundle’s dependency on the newer version.
So in a stricter OSGi environment like CICS, we don’t boot delegate the javax.* packages. If you don’t declare your javax.* dependencies and your development environment doesn’t highlight the omissions, expect to be served with a
NoClassDefFoundError. Do yourself a favour and declare all your package dependencies on the Import-Package statement in the MANIFEST.MF of your OSGi bundle.
As a general guideline, only the core java.* packages and any packages internal to the bundle can be omitted from your Import-Package statement. The core java.* packages are always delegated to the bootstrap class loader of the JRE; for obvious security reasons.
By Ivan Hargreaves, Mark Cocker and Abigail Bettle-Shaffer.
Read the next in the section 2 of OSGi Demystified, defining class path and class loader and discovering the wonders of OSGi resolution.