Singleton Class with Several Different Classloaders

Singleton class with several different classloaders

If you want a true Singleton across classloaders, then you need a common parent to load the class in question, or you need to specify the classloader yourself.

Update: From the comment from @Pshemo below a fair bit of the content in the blog below might come directly from a JavaWorld Article. I've left the blog entry in as it may still help someone, but its worth knowing where the content originally came from.

Original:
There is a blog entry that gives you a way to do this" (although I havent tried it!), and it looks fairly reasonable

As requested below here a code snippet from my link above - I do suggest you visit the blog though for the full context:

private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}

ClassLoader in Singleton Pattern

Multiple ClassLoaders are commonly encountered in Java EE. The Java EE application servers loads classes from deployed War/EAR by a tree of classloaders. The reason why they do it in this way is to isolate one application from other applications but still sharing classes between deployed modules. If you want your class to be truly singleton then you need to make sure that same classloader loads your singleton. You can achieve it like this

private static Class getClass(String clazz) throws ClassNotFoundException {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if(loader == null)
loader = YourSingleton.class.getClassLoader();
return (loader.loadClass(clazz));
}
}

Note Enum in java already implement singleton pattern.

Updated Can you please explain what is multiple class Loader and how it resolve the issue.

Let say you have a library Foo. Some part of your project requires Foo_version1.jar and some other part requires Foo_version2.jar. So in your classpath you have Foo_version1.jar and Foo_version2.jar. Now the class loader need to load a class Bar from Foo, it will load it from first Foo_versionX that it finds on the classpath. In order to resolve this issue you need another class loader because remember that some part of your project require Bar class from different jar then the one loaded by classloader.

By using the code mentioned above you are making sure that if more than 1 classloader try to load your class then always the same instance will be used.

Impact of changes on singleton class loaded by different classloaders

Singleton is per classloader. so changes wont impact other instances loaded using other classloaders.

there are couple of good references:

  • on Singleton and different classloaders
  • on Static members and different classloaders

Isolating a static singleton class using class loaders

Does plain simple reflection work at specific points in time, where you want the output of getValue to change?

Field f = LegacySingleton.class.getDeclaredField("value");
f.setAccessible(true);
f.set(null, true|false);

If not, For the Classloader approach, you can follow a Plugin architecture. But as others have noted, this may boil down to the whole dependencies being loaded on 2 different Classloader hierarchies. Also, you may face LinkageError issues, depending on how the dependencies work in your codebase.

Inspired by this post:

  • Isolate the codebase, primarily the jar having LegacyRequestHandler class and do not include in the application/main classpath.
  • Have a custom classloader that looks inwards first and then checks parent. A complete example of such a classloader is here.
  • Have a wrapper invoker that will initialise the class loader with the jar path providing LegacySingleton class, e.g.

    new ParentLastURLClassLoader(Arrays.asList(new URL[] {new URL("path/to/jar")}));

  • Post that, you can load the singleton in it's class loader space and obtain copies.


//2 different classloaders
ClassLoader cl1 = new ParentLastURLClassLoader(urls);
ClassLoader cl2 = new ParentLastURLClassLoader(urls);
//LegacySingleton with value = true in Classloader space of cl1
cl1.loadClass("LegacySingleton").getMethod("setup", boolean.class).invoke(null, true);
//LegacySingleton with value = false in Classloader space of cl1
cl2.loadClass("LegacySingleton").getMethod("setup", boolean.class).invoke(null, false);


  • Next, you may obtain the driver class for your legacy code using reflection(through cl1/2) and trigger execution.

NOTE that you shouldn't refer to the classes in the Legacy code directly in the main class, as then they will be loaded using Java primordial/application class loader.



Related Topics



Leave a reply



Submit