In this post an attempt is made to compare the class loaders of Java, J2EE and OSGi at a very high level.
Beginning with java, it has a hierarchical class loader.
Basically you ask your parent before you load anything yourself. If parent has it, you don't have a choice of using your own but to use the same. For example, your program is using java.lang.String. System class loader first checks with extension loader. As extension loader doesn't have it, it delegates the call to Bootstrap loader. Bootstrap loader returns with a success as it has loaded String class from rt.jar.
This kind of tree like class loader supports delegation, visibility and uniqueness principle inherently. Lets look at the short comings of this approach.
You have a program, running on JRE 1.6, it needs latest and greatest xerces 2.9 explicitly. You have bundled the xerces jar with your program, and its included in the classpath. But during runtime, older version of xerces classes are getting loaded irrespective of your settings. You guessed it right! Its because JRE 1.6 ships xerces and it is already loaded by extension class loader. This is the primary problem of hierarchical class loader, at runtime all jars become a long list of files which is searched in a sequential order in the order of their loading.
Explicit dependencies can not be defined
Lets say your program needs Apache commons-logging, xerces and a long list of popular open source components. When you ship your product, either you have to build the stack yourself and hand it over to the customer or communicate this through a user documentation. The so called logical unit of a java program, the jar, lacks the place holder in its meta data to include information about its dependency.
Version dependency problem
Problem gets complicated when you explicitly need commons-logging 1.1.1, xerces 2.9 and so on, and your program is supposed to be part of a larger system which already has these libraries and their versions are not same as yours. You will have sleep less night in recompiling your program with those libraries to co-exist. Phew !! I have been through this many times.
Entire jar is exposed
When you are building java libraries you want the user to use only few classes which are meant be used. But today Java doesn't have any mechanism to define access specifiers at a class level. Once the jar is in the classpath all core classes can be seen and instantiated.
Now lets see what J2EE has done to take it forward. J2EE has kept the basic design principle of Java as is and has defined enterprise application features like security, transactions and so on, on top of it. So class loader remains hierarchical but with support for isolation across multiple applications in the same container.
Application A, lets say an ear, has its own class loader and has loaded war and sar inside it. Another Application B, another ear, has its own class loader and can not see Application A's classes. Now if you have a need to share classes between these two applications, either you have to make them use the same class loader or push the shared classes up in the hierarchy to system class loader. By doing this the entire container will be able to see those shared classes even though other applications doesn't necessarily need them. JBoss has introduced unified class loader to address the same problem. But you have to be a pro to use that! Usually this is solved by duplicating jars.
Now we will see what OSGi framework has defined on top of Java which gives solutions to all of the problems discussed above.
OSGi is a module system for Java. These modules are called as bundles in OSGi. A bundle corresponds to a single Jar file. Bundles metadata is defined in jars MANIFEST.MF. Metadata contains
1. Name of the bundle.
2. Version of the bundle.
3. A list of export and import list. List of classes that bundle wants to expose and use respectively.
4. And miscellaneous information like vendor, copyright, contact information and so on.
Having defined the basic unit of OSGi, lets look at the OSGi class loader architecture. Class loader in OSGi is a graph, unlike a hierarchical tree structure of Java and J2EE. Each bundle has its own class loader. For example bundle B defines an export list and bundle A imports it. When A requests a class to be loaded which is present in B, immediately call will be delegated to the class loader of B. As there is no long list of classes organized in a hierarchical structure anymore, it proves to be very fast and efficient. The process of resolving dependency based on import and export metadata is called resolution process. Its a complex procedure taken care by the OSGi framework.
Inherent advantage of having this kind of class loader is
1. Export is defined with a version attribute, where as imports can specify a range of versions. This allows a bundle to say I need a library having a version between x and y.
2. All packages in a bundle need not stick to a version while exporting. Package org.gok can be exported under 0.9 and another package in the same bundle org.goh can be exported as 1.1.
3. Same library having different versions can be there in the application side by side
Apart from this, bundles in OSGi also has a well defined life cycle enabling them to be very dynamic in nature.