Saturday, August 8, 2009

Thread Context ClassLoader / Buddy ClassLoading in OSGi.

Bundles like Struts,hibernate,Spring need to instantiate classes in custom application bundle.In normal cases this can be directly done because there is a single class space(single class loader).

Consider a case where there are more than one class loaders .Lets take a simple case in which the struts/spring jar is kept under tomcat common lib and your application classes are under the web-inf lib. These two are jars now are loaded by two different classloaders.
Tomcat common lib class loader being the parent of tomcat web-inf/lib class loader.Also classloader of struts jar cannot see application classes in web-inf lib.

Now how can struts/spring see application classes ?

To solve such problems java Java came up with a solution called Thread Context Class Loader(TCCL). Thread context class loader was introduced in Java 2 to enable frameworks running in application servers to access the "right" application class loader for loading application classes.

This is accessed using Thread.getCurentThread.getContextClassloader().Its as simple as, It is the classloader associated with each thread.There are setter methods which can be called to set this TCCL.

By the definition in tomcat the TCCL is set to the context class loader from where the thread begins.So TCCL has the classloader visibity of web-inf lib,solving our problem.

Now since the thread started from that application ,these third party jars can use TCCL to instantiate application classes. The control starts from our application bundle and then flow moves to some third party bundle like struts or hibernate,which internally in their
source code use TCCL to instantiate our classes.

TCCL becomes slightly more complicated in the case of OSGI.

I have a small POC for the Reflection based Class loading using TCCL.

Source Code :

Bundle 3(Core bundle) – has reflection code to instantiate application classes. Uses TCCL to instantiate classes.

Bundle 2 (Service) –Using the core bundle to instantiate its classes (imports bundle 3 classes directly)

Bundle 1 (Application) – uses the OSGi service provided by bundle 2.

Control Flow:

Bundle 1 makes a OSGi service call to bundle 2.

Bundle 2 now uses core to instantiate its(bundle 2) classes.

Result: ClassNotFoundException


Bundle 2 is able to instantiate classes only in Bundle 3 and not in Bundle.

This is because TCCL has visibility only to class loader from where the thread started.


1. PAR(Spring DM solution)

Spring Dm comes with a concept of PAR,which is grouping of bundles.All bundles inside the PAR will contribute together to form a synthetic context.And hence bundles inside it have visibility to all others exisiting inside it.

2.Buddy Class Loading (this is equinox specific solution and not part of OSGi spec)

Now bundle 3, that is the core bundle can be a buddy to bundle 2 (service bundle).


In bundle 3 manifest we need to mention Buddy-Policy :registered

In bundle 2 manifest we mention Eclipse-RegisterBuddy : bundle symbolic name

Now at run time we can add new slots(Bundle 4) which registers itself as a buddy to bundle 2(core) ,without affecting any other bundles.

3. We can set the TCCL in bundle 2 to bundle class loader when the deligation goes through it.I guess TCCL may not be the right approach(not good to tamper the class loader prior to making calls across bundles).

We can choose a solution based on the requirement.

Cyclic Dependency in OSGi & Dynamic Imports

Since in OSGi we have multiple class loaders as compared to single Class Loader case ,there may be occur the problem of cyclic dependency.

This cyclic dependency may not be compile type instead most of the cases its runtime. Some piece of code may try to instantiate classes in another bundle using reflection.

Consider such a example

Bundle A requires Bundle B at runtime and vice versa.

Bundle A has to mention import in its manifest for bundle B classes and Bundle B has to do the same for Bundle A classes.

Now in such case when Bundle A is deployed it says class not found for Bundle B classes,hence to solve this we need to write

Import Package : package-from-BundleB;resolution=optional

Now Bundle A doesn't give error . Follow with deployment of Bundle B. Later when bundle A tries to access classes in Bundle B it says ClassNotFoundException.

This is because when we mentioned resolution=optional , it doesn't try to link again when class is requested. And the linking happens only initial time.

To solve this we need to use Dynamic imports in such cases.

This cyclic dependency in most of the cases can be solved using Thread Context Class Loader or Eclipse Buddy Class Loading(refer next blog for details),and there will rarely occur a case to use dynamic imports.

Free Domain Names @!