Wednesday, November 6, 2013

Typical issue of thread context class loader in OSGi


Recently got an opportunity to look at a problem that one teammate was facing in thread context class loader with embedded Felix container. The issue was with thread context class loader that was working in one bundle and was failing in another bundle. This happens during the start up of web application which has embedded Felix container. After start up, if the failed bundle is restarted alone, then it works fine. Thought there was this workaround to start the bundle alone after application starts up, automation required more work. So worked for couple of hours to fix this issue. 

To explain in more detail of this problem, here is the illustration:

There are two bundles in the OSGi container:

Bundle A
Bundle B depends on A

Bundle A has below one line code and it was not setting back to the original class loader after changing it:

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());


Same thread which invokes A was calling B while OSGi container start up and as B also needs to use class loader logic as A, it was failing to get the class loader object.

This was breaking B from achieving its functionality. Debugging showed the class loader gets just the reference of A bundle and returns nothing other than only the bundle ID. So had to take a look at bundle A to fix the issue in B.


Solution
The simple solution in this context is like below which makes sure to set the original context class loader back in bundle A

ClassLoader orignalCL = Thread.currentThread().getContextClassLoader();
try{
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
//some logic
}finally{
logger.info("Back to original class loader");
Thread.currentThread().setContextClassLoader(orignalCL);
}

Output Before
CL.toString() : 91.0 -> which is nothing but ID of the A bundle.

Output after fix :
CL.toString()  :
com.ibm.ws.classloader.CompoundClassLoader@790079[war:ear_name/war_name] Local ClassPath: ....


The learning from this is that never forget to re-set to original context class loader when you done with logic. If the same thread later calls some other code that relies on the context class loader and that code is not written to properly set the context class loader, then that code will fail majestically. The debugging such errors is of course time consuming.