Thursday, 25 July 2019

How to prevent Singleton Pattern from Reflection, Deserialization and Cloning ?

        In earlier posts, we discussed how to create the Singleton Design Pattern and what are the different approaches to create Singleton design pattern in Java. As we know that in singleton design pattern we can create only one instance and can access in whole application. But in some cases, it will break the singleton behavior.   

      There are mainly 3 concepts which can break singleton property of a singleton class. We can discuss how it can break and how to prevent from above concepts as below.


  • Reflection 
        Using Reflection API we can create multiple objects in singleton class. Consider the following example.

Example to show how reflection can break the singleton pattern:-

Singleton.java,
package com.example.demo;

public class Singleton {
 
        private static Singleton instance = null;
 
        private Singleton() {
  
        }
 
        public static Singleton getInstance() {
                if (instance == null) {
                         synchronized (Singleton.class) {
                                    if (instance == null) {
                                            instance = new Singleton();
                                    }
                         }
                 }
                 return instance;
         }
}

Using Reflection, we can break the above Singleton principle as shown in the below code.

package com.example.demo;

import java.lang.reflect.Constructor;

public class ReflectionSingleton {
 
         public static void main(String[] args) {
                   
                  Singleton objOne = Singleton.getInstance();
                  Singleton objTwo = null;
                  try {
                         Constructor constructor = Singleton.class.getDeclaredConstructor();
                         constructor.setAccessible(true);
                         objTwo = (Singleton) constructor.newInstance();
                   } catch (Exception ex) {
                            System.out.println(ex);
                   }
  
                   System.out.println("Hashcode of Object 1 - "+objOne.hashCode());
                   System.out.println("Hashcode of Object 2 - "+objTwo.hashCode());
         }

}

Output:-
Hashcode of Object 1 - 366712642
Hashcode of Object 2 - 1829164700

Same Singleton instances have different hashcodes so it breaks Singleton Principle.

How to Prevent Singleton pattern from Reflection:-

         There are many ways to prevent Singleton pattern from Reflection API, but one of best solution is to throw run time exception in constructor if instance is already exists. In this we can not able to create second instance.

Code:-
private Singleton() throws Exception {
        if (instance != null) {
                   throw new RuntimeException("Instance already exists");
        }
 }


  • Deserialization
        In serialization we can save the object of byte stream into a file or send over a network. Suppose if you serialize the Singleton class and then again de-serialize that object will create new instance, hence deserialization will break the Singleton pattern.

Below code is to illustrate how Singleton pattern breaks,

package com.example.demo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class DeserializationSingleton {
 
         public static void main(String[] args) throws Exception {
  
                   Singleton instanceOne = Singleton.getInstance(); 
                   ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text")); 
                   out.writeObject(instanceOne); 
                   out.close(); 
  
                   ObjectInput in = new ObjectInputStream(new FileInputStream("file.text")); 
                   Singleton instanceTwo = (Singleton) in.readObject(); 
                   in.close(); 
  
                    System.out.println("hashCode of instanceOne is - " + instanceOne.hashCode()); 
                    System.out.println("hashCode of instanceTwo is - " + instanceTwo.hashCode()); 
          }  

}

Output:-
hashCode of instanceOne is - 1550089733
hashCode of instanceTwo is - 2003749087

As we see above output, there are two objects got created because hashcodes of instanceOne and instanceTwo are different, hence it breaks singleton principle.


Prevent Singleton Pattern from Deserialization:-

To overcome this issue, we need to override readResolve() method in Singleton class and return same Singleton instance.
Singleton.java,
   @override
     protected Object readResolve() { 
           return instance; 
     } 

Now run above DeserializationDemo class and see the output,
hashCode of instanceOne is - 1550089733
hashCode of instanceTwo is - 1550089733


  • Cloning
        Using clone method we can create copy of original object, samething if we applied clone in singleton pattern, it will create two instances one original and another one cloned object. In this case will break Singleton principle as shown in below code.

Implement Cloneable interface and override clone method in the above Singleton class.

Singleton.java
  .....
  @Override
  protected Object clone() throws CloneNotSupportedException  { 
          return super.clone(); 
  } 
....

Main Class,
package com.example.demo;

public class CloningSingleton {
 
        public static void main(String[] args) throws CloneNotSupportedException, Exception {
                 Singleton instanceOne = Singleton.getInstance();
                 Singleton instanceTwo = (Singleton) instanceOne.clone();
                 System.out.println("hashCode of instance1 - "+instanceOne.hashCode());
                 System.out.println("hashCode of instance2 - "+instanceTwo.hashCode());
        }

}

Output:-
hashCode of instance1 - 366712642
hashCode of instance2 - 1829164700

If we see above output, two instances have different hascodes means these instances are not same. 


Prevent Singleton Pattern from Cloning:-

In the above code, breaks the Singleton principle i. e created two instances. To overcome above issue we need to implement/override clone() method and throw an exception i.e CloneNotSupportedException from clone method. If anyone try to create clone object of Singleton, it will throw an exception as see below code.

Singleton.java
  ......
  @Override
  protected Object clone() throws CloneNotSupportedException  { 
           throw new CloneNotSupportedException(); 
  } 
........

Now you can run the main class, it will throw CloneNotSupportedException while creating clone object of Singleton object.


Thank you for visiting blog...

Related Posts:--
1) Singleton Design Pattern in java with example
2) Factory Design Pattern in Java
3) Builder Design Pattern in Java

No comments:

Post a Comment