Java vs OSGi Class Loading
In this post I’m going to discuss about the difference between traditional Java and OSGi class loading.
Java Class Loaders
A ClassLoader in Java is merely another Java class which is used load class files. The code we write in Java are compiled by javac compiler which turns the .java files into .class files. These class files contain the bytecodes of the code you wrote and then the JVM executes the program using this bytecode in class files.
In this process, ClassLoader plays an important role in loading class files from either file system, network or any other source. Java class loaders follow the following basic principles.
Delegation: forward request of class loading to parent class loader and only loads the class, if parent is not able to find or load class.
Visibility: allows child class loader to see all the classes loaded by parent ClassLoader, but parent class loader can not see classes loaded by child.
Uniqueness: allows to load a class exactly once, which is basically achieved by delegation and ensures that child ClassLoader doesn’t reload the class already loaded by parent.
Furthermore, there are three default class loaders in Java namely Bootstrap, Extension and System/Application class loader.
Bootstrap: Responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java. (JRE/lib/rt.jar)
Extension: delegates class loading request to its parent, Bootstrap and if unsuccessful, loads class form jre/lib/ext directory or any other directory pointed by java.ext.dirs system property
System: responsible for loading application specific classes from $CLASSPATH environment variable
what is OSGi?
OSGI (Open Service Gateway Initiative) is a Java Framework which facilitates development and deployment of modular software programs. It specifies how to build larger applications from very loosely-coupled components. It improves the way Java classes interact within a single JVM while providing the following features.
- a modified Java classloader which provides fine-grained control over symbolic linking with other code in the same JVM
- a central service registry for decoupling callers of an interface from the interface implementation
- an enhanced version of the java.lang.SecurityManag9er (ConditionalPermissionAdmin)
- a large set of standardized optional services for things like loading configuration-files, publishing events, exposing Java servlets, etc.
The smallest unit of deployable code in OSGi is called a “bundle”. This is a jar file with some additional resources and a manifest file (MANIFEST.MF) which details out on all of the bundle’s contents.
OSGi bundles have the following mandatory properties (defined in the manifest file);
Bundle-SymbolicName: the FQDN (fully-qualified domain name) of the “primary” Java package in the jarfile
Bundle-Version: major.minor.patch version
In addition to the above mentioned mandatory properties, OSGi bundles have the following important optional properties;
Bundle-Activator: name of a class to be notified when bundle is “started”/”stopped”
Import-Package: list of (java-package-name, version-range) that classes in this jarfile expect other bundles to provide.
Export-Package: list of Java packages within this jarfile that external code is allowed to access.
Bundle Life Cycle
OSGi defines several possible states for a bundle:
INSTALLED: The bundle has been successfully installed.
RESOLVED: All Java classes that the bundle needs are available. This state indicates that the bundle is either ready to be started or has stopped.
STARTING: The bundle is being started, the
BundleActivator.start method has been called but the start method has not yet returned. When the bundle has an activation policy, the bundle will remain in the “starting” state until the bundle is activated according to its activation policy.
ACTIVE: The bundle has been successfully activated and is running. Its Bundle Activator start method has been called and returned.
STOPPING: The bundle is being stopped. The
BundleActivator.stop method has been called but the stop method has not yet returned.
UNINSTALLED: The bundle has been uninstalled. It cannot move into another state.
As I mentioned earlier, Java class loaders have only parent and child class loaders. And the parents expose all the classes they know to their child class loaders. Unlike those, OSGi classloaders follow a peer based procedure. They only expose the classes that the jar file wants to expose and only use classes exposed by peer jarfiles in the version their jarfile wants. Also multiple installed bundles can export the same Java package with different versions.
The jarfiles containing OSGi bundles must not be on the normal Java classpath. Instead they are loaded by creating an OSGi org.osgi.framework.launch.Framework object, and using Framework.getBundleContext().installBundle(path) to load bundle jarfiles. Each of the “installed” bundles is loaded via a separate instance of an OSGi-specific subclass of
Each OSGi bundle has its own mapping table which specifies which classloader is responsible for providing classes from a specific package to the current bundle. This gets populateed when the bundle is in resolved state (when it is first loaded. So when the jar file of a bundle is loaded, the Framework first creates a Bundle object to represent the jar file and that will create a classloader which is a child of an osgi environment classloader. The bundle’s classloaders pass requests for classes in different packages upto the parent classloader similar to traditional java. That means core java classes are available to all bundles and they are loaded from the Bootstrap class loader as usual. But for all other packages, requests are not passed up to the parent. The bundle’s classloader will resolve classes using the bundle-specific mapper table.
Because of this, classes in a new bundle by default cannot see any classes from the Java JDK or classes in normal Java application classpath($CLASSPATH). Hence, to see those the bundle should use the Import-Package declaration which tells the Framework to copy the entry from the shared map of all possible packages into the bundle-specific map of imported packages.
- Reduces the tight coupling between jarfiles compared to normal Java applications.
- Provides the ability for multiple versions of java libraries to coexist in the classpath at the same time
- Bundle-activator feature provides the capability to execute the code in a jar file as soon as the bundle is started. This is similar the static initializers in Java but these activators also have access to the OSGi environment as well.