dagblog.
← Archive

The Singleton and the ClassLoader Problem


Do you use the Singleton design pattern in Java? If you're new to it, a singleton is an
object with only one instance in the entire application. Use it when you need to guarantee that
an object is unique, for example in a cache implementation.

I spent several days implementing XML configuration for our application server. For that I turned to
the Apache Jakarta project, which includes
Commons Digester, a component widely used in
Jakarta projects. Digester maps XML to Java objects.

Digester lets you specify an object to call as it processes the document, using SAX's event model.
That gave me the name of the element being processed, which I passed along to a second object
that worked with the element's contents. So I had two objects, one that processed the contents and one that was
hooked into document processing. The object working with the contents always needed the current element's
name to store them correctly, since the element name was the key in a map. The object that sniffed out the element names
was the singleton.

That arrangement worked fine in our application server. When I tried moving the application
to another servlet container, I ran into trouble. The object was no longer unique. Two instances were created,
and the application stopped behaving correctly.
The singleton was thread-safe, though. The method that returned the instance was synchronized.

public class RuleWatcher{
private static RuleWatcher watcher = null;

private RuleWatcher(){
// soukromý konstruktor
}

public static synchronized Rulewatcher getInstance(){
if(watcher == null){
watcher = new RuleWatcher();
}
return watcher;
}
.
.
.atd.
.
}
The code is fine, yet it may still behave differently from what you expect. Why? The ClassLoader. Both Digester
and most servlet containers use their own class loaders. If a RuleWatcher instance is created
in the application server's ClassLoader context and an object is then created in another ClassLoader's context, Digester's in our case,
a call to getInstance() evaluates the condition to true. The class variable watcher
belongs to a different context, so another instance is created.
It took me about a day to figure this out. The fix was straightforward. Fortunately, the Digester component, or more precisely the object,
lets you set its ClassLoader. All it took was one line to set the original ClassLoader, and the problem went away.
ClassLoader c = Thread.currentThread().getContextClassLoader();
digester.setClassLoader(c);

Medium version

Related articles

Related CZ Podcast episodes