Protecting Java in a Modular World – Context Classloaders

This post is about seemingly tricky class loading stuff in Java. It is actually not really tricky, there is just a lot of missing-the-point advice out there and it is one of those problems that you typically start worrying about when you already had the problem for a while and have built up a lot of wrong ideas about it.

In particular this post we look at the widely used but hardly noticed Thread Context Classloader mechanism in Java.

Java Classloading recap (skip if you think you’re a pro)

Java class loading is separating name spaces in the type system. That is actually not as complicated as it sounds. It is just saying that there are different groups of types at runtime and types in different groups (class loading scopes) may not be able to access each other or only one may access the other but not vice versa. What goes into a class loader scope, i.e. what is provided by one class loader, is defined by the environment – typically all types of a module or an application (in Java EE).

(Disclaimer: OSGi makes it all a little more complicated in its own way – but the effects described below still show.)

This has various important implications: On the one hand it allows you to isolate stuff from each other. E.g. you can run several web apps that have the same JAX-RS servlet (as an example) by name but in different implementation versions. This type of isolation is the underpinning of all hot deployment within one JVM process.

In fact, types in the JVM are considered identical if and only if they match in name and defining loader (i.e. the class loader that actually provided the class).

The standard class loading setup is that a class loader has one parent and when asked to load a class it will ask its parent before trying to check its resources (typically .jar, .class files)

That is very good. It assures consistency, which filtering or child-first policies (that many app servers offer as problem solvers ) do not. And losing consistency in class loading may give you some short term gain for a usually super awful midterm pain.

Modular systems like OSGi or Z2 for that matter (still need to check on Jigsaw) modify this pattern slightly by having a view from higher or by delegating to more than one loader. But the basic premise holds true. Simplified you end up in tree-style visibility relationships where B and C share the types of A, but A has no access to the types provided by B nor C.

Here, A may be the System class loader, the one that provides java.lang.String and B and C are some module loaders in some modular runtime. Now, the fact that B and C share the types of A is the reason they can instantiate, pass and use implementation of types of A to each other without trickery . On the other hand, B can be removed without requiring to unload C (if done badly however, C may still hold Objects from B without knowing even knowing of their type!).

OK. Let’s stop the general stuff here and let’s talk about context class loaders.

A Thread’s context classloader. What is that good for?

Context classloaders actually solve an important problem. Imagine you have a functionality F in A that is supposed to be used in B. As an example for F think about a Web framework like Stripes, Vaadin, or essentially any such toolkit. In non-trivial cases, you may want to extend F to use extension types and or configuration files that are supplied in B. Wouldn’t it be good if A could actually look into B for that? That is exactly what context class loaders are for. They transport a caller scope into the callee. This mechanism is used all over the place. It is used in JNDI to find initial context factories (and more), in Jetty (or any other Web container), BIRT, Hadoop, javax.mail… just to name a few.

You may never have noticed, because application containers set them for you whenever the control flow passes their access point. For example the Web container sets the context class loader to the one of the Web app for every request.

The general coding scheme used is

ClassLoader thatLoader = Thread.currentThread.getContextClassLoader();
Thread.currentThread.setContextClassLoader(thisLoader);
try {
  // do something
} finally {
  Thread.currentThread.setContextClassLoader(thatLoader);
}

And that is the main advantage of the context class loader approach: It’s really simple.

Unfortunately, that is also its biggest problem: Most coders are hardly aware of the mechanism and accidentially either do not set it correctly when it is advised to do so, or have a class loader passed on when it would be better not to do so.

In modular environments (e.g. OSGi as well as Z2) when the control flow passes through different class loading scopes, there may be no one automatically switching contexts for you as that is prohibitive due to performance and complexity considerations.

Furthermore, many frameworks like to hold on to context class loader they pick from some initial call and use these later on. This holds for example for Eclipse BIRT and for Apache Hadoop and essentially any other framework that manages its own threads.

In this case, when eventually passing on control, the context class loader may be set to some or some other Web app, for example, and the framework at hand may start looking into the Web app’s class loading scope which can not only lead to using implementations you didn’t expect, it may also mean that re-deployment leaves old implementations in use (because the old loader is still held on to).

That leads us to

Rule #1: Make sure you set the context class loader to a suitable scope when using some non-trivial framework in a modular environment.

Note that any new thread (or java.util.Timer) will take over the context class loader from the instantiating thread. I.e. there is not much required to make rule #1 a necessity.

A more constructive situation comes up when you intentionally integrate a framework that manages threads to call your code back at some later point in time. For example the Quartz Scheduler does so (see also Getting Work Done Asynchronously). When used in a Web app, Quartz’s threads will pick up the class loader from the web app and as long as the job’s implementation is in the Web app, things will work as “naively” expected.

In modular environments however, the job’s code may reside in some other scope than Quartz itself. Quartz is effectively used as an application container.

In that case you need some generic job wrapper that not only locates the actually job implementation and its module, but also sets the context class loader to the one of the module. Of course, Quartz falls into the category for which rule #1 applies. I.e. when starting the scheduler, make sure the context class loader has Quartz and your job wrapper. Not more.

So we have:

Rule #2: When integrating asynchonous callbacks in a modular environment, abstract callback retrieval and set the context class loader appropriately.

This concludes the context class loader piece. In a next installment (now available: Protecting Java in a Modular World – Isolating the Bad Guy), we will look at how to tame beasts like Eclipse BIRT by confining it into an isolated class loading scope.

In the meantime try searching for class loading problems around BIRT and enjoy (or feel sorry).

Advertisements