DevOps Zone is brought to you in partnership with:

I am an entrepreneur which currently bootstrapping my 2nd start-up. Uri has posted 3 posts at DZone. You can read more from them at their website. View Full User Profile

Java Classloader - Handling Multiple Versions of The Same Class

02.24.2014
| 11925 views |
  • submit to reddit

This article deals with different approaches to load multiple versions of the same class.

The problem

Many times during the development you will need to work with different libraries versions which not always backward compatible or from some reason you need to support multiple versions of the same library. 

Using standard Java classloaders will not solve this issue since they all use "loadClass" method to load a specific class only once. After that "loadClass" will return the reference of the existing Class instance.


The solution:

Using another classloader to load the library (or multiple classloaders).

There are several approaches available to achieve that:


  1. Use a URLClassLoader:

    This class loader will allow you to load your jars via URLs, or specify a directory for your classes location. 

    Here is an example:

URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/C://Test/test.jar")});
Class cls = clsLoader.loadClass("test.Main");
Method method = cls.getMethod("main", String[].class);
String[]params = new String[2];	
method.invoke(null, (Object) params);


   2.   Write your own custom class loader.

         Since Java class loaders (including URLClassLoader) first ask to load classes from their
         parent class loader, you can encounter a situation where you will need your custom
         classloader to load the classes from your specified location first.

         In this case here is a sample of a custom class loader which do exactly that:


import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

public class CustomClassLoader extends ClassLoader 
{
    private ChildClassLoader childClassLoader;
    
    public CustomClassLoader(List<URL> classpath)
    {
        super(Thread.currentThread().getContextClassLoader());
        URL[] urls = classpath.toArray(new URL[classpath.size()]);
        childClassLoader = new ChildClassLoader( urls, new DetectClass(this.getParent()) );
    }

    @Override
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
    {
        try
        {
            return childClassLoader.findClass(name);
        }
        catch( ClassNotFoundException e )
        {
            return super.loadClass(name, resolve);
        }
    }

    private static class ChildClassLoader extends URLClassLoader
    {
        private DetectClass realParent;

        public ChildClassLoader( URL[] urls, DetectClass realParent )
        {
            super(urls, null);
            this.realParent = realParent;
        }

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException
        {
            try
            {
            	Class<?> loaded = super.findLoadedClass(name);
                if( loaded != null )
                    return loaded;
                return super.findClass(name);
            }
            catch( ClassNotFoundException e )
            {
                return realParent.loadClass(name);
            }
        }
    }

    private static class DetectClass extends ClassLoader
    {
        public DetectClass(ClassLoader parent)
        {
            super(parent);
        }

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException
        {
            return super.findClass(name);
        }
    }
}
Published at DZone with permission of its author, Uri Lukach.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Peter Verhas replied on Mon, 2014/02/24 - 7:25am

Never write a class loader. Possible exceptions may be to learn, experience and practice what class loader is and how it works in Java or if you are developing a framework. This, however, has to be the understanding of your customer, who is financing your effort.

In all other cases invent into research to find an appropriate framework, like OSGi, servlet environment or Spring, JEE.

Having said that I see no explanation or reason why you are using the thread context class loader in your code.

Uri Lukach replied on Mon, 2014/02/24 - 9:29am in response to: Peter Verhas

Recently I had to deal with a scenario in which I had to support multiple jar versions of the same device. I had a few network devices from the same type with different versions which needed to be provisioned. 

Unfortunately the new versions were not backward compatible, so I chosen this solution which works well. 

I hope other people can find this solution helpful and save their valuable time.


Kind Regards

Uri


Peter Verhas replied on Mon, 2014/02/24 - 10:39am in response to: Uri Lukach

I am still interested in your use and reason of thread context class loader in the super constructor call. The documentation of the thread context class loader on the net is sparse and contradicting at times. Your use is actually not inline with what I know about the thread context class loader. Who sets in your use the thread context class loader?

It may well be the case that my knowledge is faulty since I never dared to write a class loader in production code.

Uri Lukach replied on Mon, 2014/02/24 - 2:44pm

This is an excellent question! 

Using the Thread context classloader assures that the classes will use their own classloader to load other classes. For instance, if class 'DeviceA' uses 'DeviceB' then 'DeviceB' needs to be on the classpath of the classloader of 'DeviceA', or its parent.

So basically, Thread context classloader provide a back door around the classloading delegation scheme. 

Deepu Roy replied on Mon, 2014/02/24 - 9:31pm

Interesting solution. I would have first checked to see if I could repackage the different versions, which I assume was not possible in this case. Custom ClassLoaders would have been my last resort in a production application. Was this in a Java EE environment? If yes, did you see any issue with app reloads?

Uri Lukach replied on Tue, 2014/02/25 - 4:04am

I am using Tomcat with Spring, hence I do have multiple classloaders (before adding my own custom ones). Web application reloads are working fine.





Oleksandr Alesinskyy replied on Wed, 2014/02/26 - 5:51am

Do not do this unless you absolutely need it.

This type of solution shall be your last resort, if all other failed.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.