Friday, October 3, 2008

Using the Thread's context class loader in a multi-module environment

When you working in a multi-module environment, a problem you bump up against is how to handle the thread's context class loader.

In case you haven't seen this before, you can access the thread's context class loader using the code

Thread.currentThread().getContextClassLoader();

and you can set it using

Thread.currentThread().setContextClassLoader(classLoader);

Why it exists

So why does the context class loader exist, and how is it used? The thread context class loader was introduced in Java 1.2 to enable frameworks running in application servers to access the "right" application class loader for loading application classes.

Lets take a framework like Struts, which uses the thread's context class loader (as do many other similar frameworks). Struts allow you to declare action classes in an XML configuration file. Once the framework has read the file and figured out what class to load (at this point only identified name), the framework needs a classloader to load the class.

One naive approach to this problem would be to use the code

this.getClass().getClassLoader()

This would return the the class loader responsible for loading the framework class. There is an obvious limitation here. The framework jar would need to be packaged in a location visible only to the application class loader, and not to the system class loader or to some shared or server class loader. Again, coming back to the example of Struts, say running in Tomcat, the framework jar would need to be placed in WEB-INF/lib and not on the system class path or in a server class path such as TOMCAT_HOME/common/lib. This restriction might hold in many cases, but is not one which can be relied upon.

To solve this problem, the context class loader was invented, to give framework code a mechanism to find the "correct" class loader to load application classes. In the case of the web application, the server typically applies the web application class loader as the context class loader.

So what's the problem?

This mechanism has worked well enough until now, but is a bit of a hack, something which is certainly exposed by multi-module frameworks such as OSGi.

There is an interesting article on the Equinox wiki page on this problem and how it addresses. To save some typing (and a bit of thinking), I've picked out a quote:

Many existing java libraries are designed to run inside a container (J2EE container, Applet container etc). Such containers explicitly define execution boundaries between the various components running within the container. The container controls the execution boundaries and knows when a boundary is being crossed from one component to the next.

This level of boundary control allows a container to switch the context of a thread when a component boundary is crossed. Typically when a container detects a context switch it will set the context class loader on the thread to a class loader associated with the component which is being entered. When the component is exited then the container will switch the context class loader back to the previous context class loader.

The OSGi Framework specification does not define what the context class loader should be set to and does not define when it should be switched. Part of the problem is the Framework is not always aware of when a component boundary is crossed.


Some Solutions

Eventually, the OSGi specification will presumably come up with a standard way of addressing this problem. Until then, it is up to vendors to implement their own solutions. Equinox does so through Buddy Class Loader and Context Finder mechanisms, which you can also read about here.

Rob Harrop describes his solution here for the Spring Application Platform:
Each bundle in OSGi has it's own ClassLoader, so therefore, only one bundle can be exposed as the thread context ClassLoader at any time. This means that if a third-party library needs to see types that are distributed across multiple bundles, it isn't going to work as expected.

The Platform fixes this by creating a ClassLoader that imports all the exported packages of every module in your application. This ClassLoader is then exposed as the thread context ClassLoader, enabling third-party libraries to see all the exported types in your application.


So this will work as long as you export the types that the third party libraries needs to load.

Impala's Solution

Impala uses proxies to call across module boundaries. The proxy interceptor has access to a service reference for the target object being invoked. This service reference, as well as containing a reference to the target instance, also has a reference to the class loader responsible for loading the module which exported the target bean. Before invoking the instance, it sets the thread context loader to this class loader.

That's quite a mouthful - if code makes more sense that this English then see this souce.

Additionallly, Impala Servlets - either extensions of the Spring DispatcherServlet or those responsible for integrating with other servlets - perform a similar operation.

There are one or two gotchas. It is possible to call beans defined in parent modules without going through a proxy. In this case, the described mechanism will not be applied. I'm pretty confident though that this particular scenario won't cause any serious problems.

I'd certainly be interested in finding out over the course of time whether there are other strategies that Impala's solution won't cover.

1 comment:

viagra online said...

This type of problem has happened in the office and at various expositions but we decided we didn't stop as quickly as possible to continue our work. Thanks for mentioning this and share it with us!