How single can your singleton instance be?

The singleton pattern must rank one of the most-widely used. We all know the pattern and its benefits. Joshua Bloch taught us how to ensure single-instance when the class is Serializable. However, it's quite easy to make any number of fully-functional instances of your singleton class using a simple ClassLoader trick.

We are all familiar with the following straightforward implementation of lazy-loaded Singleton pattern:

The constructor is private, and a single public function is exposed. This function (which is also synchronized) tests whether there is already an instance and if not, it creates one. Item 57 in Joshua Bloch's "Effective Java" instructs us to add readResolve() function in case the singleton is also Serializable. Another version, which employs eager-loading is:

It's even simpler than the previous one. You pay a one-time penalty of creating an object (even if you never use it), but the getInstance() is no longer synchronized.

The constructor, is of course, private in both cases to prevent accessing it from the outside code. However, as shown before[2], you can use combination of getDeclaredConstructors and setAccessible to make the private constructor accessible via reflection. You can then create any number of instances of that singleton class. However, the author of the above failed to notice that the created instance may not be fully functional. The publically-accessible getInstance() function can perform additional manipulations with the created object, setting additional properties and attributes. In this case, the object you have may not function properly. Here, you need to call the getInstance() function and have it return a different instance (properly and fully initialized) each time you call it.

The answer lies in Chapter 2 of excellent Component Development for the Java Platform[3] book (which came out in 2001). This chapter describes the way the ClassLoaders work, and how you can have any number of Class object instances for the same class. Let's start with a small (and not real-life) example to show how it works, and then go to a real-life example.

As mentioned before, you can use getDeclaredConstructors() and setAccessible() on Constructor to make it accessible, but then you will miss the additional logic (empty in our sample case) in getInstance().

In order to create a second instance of TestSingleton class, we need to create additional ClassLoader that does not have our first ClassLoader as its ascendant. As described in the book, the ClassLoader first checks its cache to see if it already loaded the specified class, then asks its parent for the class (recursively until there is no parent), and only then (if parent tells that it doesn't have this class) it loads the class (from the URL in our case). Here, we need to copy the compiled class to another directory and create a second URLClassLoader. Both class loaders will have the same parent (system class loader), but this parent doesn't have the TestSingleton class, hence the second class loader will have its own copy:

So, now we have two instances of (almost) the same class. It is true that the Class objects are different, but the functionality is the same.

Now that we have seen the technique, it can be trivially applied to already existing classes. In order to do this, we scan the class path looking for either the .class on local computer or for jar file that contains the required class. After we find it, we copy the corresponding class to some directory, and load it using the same technique as above (passing null parent). Afterwards we create a second copy in another directory.

Now we start to scan the classpath. Note that here we show only Windows version. For cross-platform, you need to use ":;" pattern to break the classpath and assemble drive names back when you are under Windows:

For each classpath component, we check whether it is a jar file or a directory. The handling of jar files is left as a (simple) exercise:

for (String pathComp : pathComps) { if (wasLocated) break; if (pathComp.endsWith(".jar")) { // scan jar and look for the desired class. // Use JarInputStream for reading jar file and // JarEntry for checking the current entry. // The, getInputStream() on the matching entry // and copy it to the temp directory as below. }

In case it's a directory, we scan it for the desired .class file (not that if the class belongs to some package, you'll need to adjust the scan code correspondingly):

As shown above, when the class is found, it is copied to a temporary directory. After we are done copying, we create the first URLClassLoader with null parent. This is crucial - the default parent of a new class loader is the system class loader. In this case, the parent class loader has access to the original class (since it's in the classpath), and the copied class will be simply ignored.