4

I was designing the below singleton class, and I am aware of that where my singleton can be broken

public class SingletonObject {
    private static SingletonObject ref;
    private SingletonObject () //private constructor
    { }

    public  static synchronized   SingletonObject getSingletonObject()
    {
        if (ref == null)
            ref = new SingletonObject();
                return ref;
        }


    public Object clone() throws CloneNotSupportedException
    {throw new CloneNotSupportedException ();
    }   
}

the following url already suggest where singleton can be broken cracking singleton with other ways but my query is that as suggested in this url that singleton can be broken by class loader also same class could be loaded by two different class loaders, as such, you could create two instances of your singleton class by simply invoking its getInstance() method in a class loaded by two different class loaders. This approach would work without having to resort to violating the private constructor.

ClassLoader cl1 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
ClassLoader cl2 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
Class<?> singClass1 = cl1.loadClass("hacking.Singleton");
Class<?> singClass2 = cl2.loadClass("hacking.Singleton");
//...
Method getInstance1 = singClass1.getDeclaredMethod("getInstance", ...);
Method getInstance2 = singClass2.getDeclaredMethod("getInstance", ...);
//...
Object singleton1 = getInstance1.invoke(null);
Object singleton2 = getInstance2.invoke(null);

Please advise what measure should be taken to avoid this also.

Community
  • 1
  • 1
Tuntun Dffhf
  • 57
  • 1
  • 3
  • 2
    Why are you so worried about people going out of their way to hack your singleton? If they want to they will... Dealing with your specific issues; you could use `ServerSockets` to obtain an OS level lock. – Boris the Spider Mar 10 '13 at 12:18

4 Answers4

5

If you want to create true singleton, you should avoid using custom classloaders - all singletons should be loaded by common parent classloader.

Example of resolving your issue from question, linked below:

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

Similar questions:

Community
  • 1
  • 1
bsiamionau
  • 8,099
  • 4
  • 46
  • 73
  • +1 clever, nice hint. – Boris the Spider Mar 10 '13 at 12:19
  • zvzdhk so I should include this getclass method inside my above class itself or from the class where I am creating the instance of singleton please specify with detail example – Tuntun Dffhf Mar 10 '13 at 12:32
  • @TuntunDffhf it is method of your custom classloader, that insures, that singleton class would not be loaded with different classloaders, to get more, [follow the link](http://snehaprashant.blogspot.co.uk/2009/01/singleton-pattern-in-java.html) – bsiamionau Mar 10 '13 at 12:42
1

This is the scenario you are worried about:

  1. Classloader A does not have Singleton on its classpath.
  2. Classloader B is a child classloader of A and has Singleton on its classpath.
  3. Classloader C is a child classloader of A and has Singleton on its classpath.
  4. Both B and C load Singleton.

When this happens you end up with two distinct types called Singleton ... according to the JLS. So when you create the respective instances, they will be instances different types. (Arguably, the "one of a type" invariant has not been broken because the types are not the same. However, if the purpose of the singleton is to hold state that must be held only once, that technical argument is beside the point.)

And in fact, if you do end up with that situation, then another class loaded by B won't be able to use A's instance of Singleton ... or vice versa ... because of the different types. Effectively B's and C's sets of classes will only "know" about one of the singleton instances ... respectively.

Anyway, if you really do need B's and C's classes to share one instance of Singleton, the way to achieve it is to put Singleton onto the classpath of a common parent/ancestor classloader; e.g. A in this case.


Please advise what measure should be taken to avoid this also.

On re-reading this, it seems that you are looking for a way stop someone else from writing code that accidentally or deliberately breaks the invariant using a classloader.

I don't think there is one ... unless you treat other people's code as untrusted and run it inside a sandbox that prevents them creating classloaders, using abstraction-breaking reflection, and so on.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

I don't think it should be a concern, since classes loaded by different loaders are incompatible, and you will not be able to cast both instances to SingletonObject.

Javier
  • 12,100
  • 5
  • 46
  • 57
  • It might do some other work like create a massive, heavy weight, expensive object. – Boris the Spider Mar 10 '13 at 12:20
  • Right, but even so there are not "two instances of a singleton class", but "two singleton classes". – Javier Mar 10 '13 at 12:23
  • Well, I guess there are two instances of `Singleton.class` rather than two instances of `Singleton`. – Boris the Spider Mar 10 '13 at 12:27
  • I got your point, but `Singleton.class` evaluates to the `Class` object as defined by the defining class loader [(JSL)](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.8.2). – Javier Mar 10 '13 at 12:34
0

To avoid such situation Singleton's class should be loaded by the top most class loader. In such complex case this should be done at application start up, before any other class loader appear. Invoke getSingletonObject() method first to load class and instatantiate singleton. Child class loaders (home made, for exmple) in tern should not break class loading policy: first look up class at parent class loader and only then try to load itself. If the break, multiple Singletons can appear.

Mikhail
  • 4,175
  • 15
  • 31