Search code examples
reflectionclassloader

How to use class loader to hack the Singleton Pattern (create multiple instances of Singleton with different classloaders)


I read this :Breaking of sinlgleton by two different class loaders But I can't create more than one instance of a Singleton with more than one class loader. Can some one help me provide an example of how you can break the Singleton pattern with multiple class loaders please?

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    
    public static  void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

        ClassLoader cl1 = new CustomClassLoader();
        ClassLoader cl2 = new CustomClassLoader();
        Class<?> singClass1 = cl1.loadClass(SingletonObject.class.getName());
        Class<?> singClass2 = cl2.loadClass(SingletonObject.class.getName());
        
        Method getInstance1 = singClass1.getDeclaredMethod("getSingletonObject");
        Method getInstance2 = singClass2.getDeclaredMethod("getSingletonObject");
        Object singleton1 = getInstance1.invoke(null);
        Object singleton2 = getInstance2.invoke(null);

        System.out.println("ok");
        
    }
}
public class SingletonObject {
    private static SingletonObject ref;
    private SingletonObject () //private constructor
    { }

    public  static  SingletonObject getSingletonObject()
    {
        if (ref == null){
            ref = new SingletonObject();
            System.out.println("create new");
        }else{
            System.out.println("already have it");
        }
        return ref;
    }


    public Object clone() throws CloneNotSupportedException
    {throw new CloneNotSupportedException ();
    }
}
public class CustomClassLoader extends ClassLoader{
}


Solution

  • Your CustomClassLoader has no effect. It doesn’t know any way to resolve classes, so all it does, is delegating load requests to its parent loader, which is the ordinary application class loader.

    You have to create a class loader capable of loading the intended class and not delegating to the application class loader.

    import java.net.URL;
    import java.net.URLClassLoader;
    
    public class Main {
        
        public static  void main(String[] args) throws ReflectiveOperationException {
            URL[] url = { SingletonObject.class.getProtectionDomain()
                                               .getCodeSource().getLocation() };
    
            ClassLoader cl1 = new URLClassLoader(url, null);
            ClassLoader cl2 = new URLClassLoader(url, null);
            Class<?> singClass1 = cl1.loadClass(SingletonObject.class.getName());
            Class<?> singClass2 = cl2.loadClass(SingletonObject.class.getName());
    
            Object singleton1 =
                singClass1.getDeclaredMethod("getSingletonObject").invoke(null);
            Object singleton2 =
                singClass2.getDeclaredMethod("getSingletonObject").invoke(null);
            Object singleton3 = SingletonObject.getSingletonObject();
    
            System.out.println((singleton1 != singleton2) + ", "
                + (singleton2 != singleton3) + ", " + (singleton1 != singleton3));
            System.out.println("ok");
        }
    }
    

    Here, the URLClassLoader gets the URL to resolve the class SingletonObject and a null parent class loader which indicates the bootstrap loader rather than the application class loader which it would use when using the constructor without a ClassLoader parameter.