Thursday, December 16, 2021

Reflection, Serialization and Cloning Prevent in Singleton Pattern

Q:- What is singletone class in java? Nubmer of ways to create singletone class in java?

Answer:- if you don't know about singletone class please read - Singletone class in java

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

Reflection: Reflection can break the singleton property of singleton class, as we can change the accessible using setAccessible true using reflection as shown in following example:
package com.javaiq.singletone.reflection;

import java.lang.reflect.Constructor;

//Singleton class
class Singleton {
	// public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}
}

public class SingletonReflectionTest {

	public static void main(String[] args) {
		Singleton instance1 = Singleton.instance;
		Singleton instance2 = null;
		try {
			Constructor[] constructors = Singleton.class.getDeclaredConstructors();
			for (Constructor constructor : constructors) {
				// Below code will destroy the singleton pattern
				constructor.setAccessible(true);
				instance2 = (Singleton) constructor.newInstance();
				break;
			}
		}

		catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("hashCode of instance1:- " + instance1.hashCode());
		System.out.println("hashCode of instance2:- " + instance2.hashCode());
	}
}
Output: As in below output we can see two instance of singletone class have different hascode that means, 2 objects of same class are created and singleton pattern has been destroyed.
hashCode of instance1:- 1829164700
hashCode of instance2:- 2018699554
Overcome reflection issue:To overcome reflection issue on singletone class, we can use enums because java ensures internally that enum value is instantiated only once. Since java Enums are globally accessible,So we can use enums for singletons. Its only drawback is that it is not flexible i.e it does not allow lazy initialization.
Enums. An enum is a special "class" that represents a group of constants (unchangeable variables, like final variables).
package com.javaiq.in;

public enum Singleton 
{
  INSTANCE;
}
package com.javaiq.singletone.reflection;

//Java code to Overcome reflection issue on Singleton property

import java.lang.reflect.Constructor;

public class SingletonOvercomeReflectionIssue {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.INSTANCE;  // Using enum Singleton
		Singleton instance2 = null;
		try {
			Constructor[] constructors = Singleton.class.getDeclaredConstructors();
			for (Constructor constructor : constructors) {
				// Below code will destroy the singleton pattern
				constructor.setAccessible(true);
				instance2 = (Singleton) constructor.newInstance();
				break;
			}
		}

		catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("instance1.hashCode():- " + instance1.hashCode());
		System.out.println("instance2.hashCode():- " + instance2.hashCode());
	}
}
Output: It clear from below output by using enum we can not break singleton propery of singletone class by using reflection.
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.javaiq.singletone.reflection.SingletonOvercomeReflectionIssue.main(SingletonOvercomeReflectionIssue.java:16)
instance1.hashCode():- 2018699554Exception in thread "main" java.lang.NullPointerException
	at com.javaiq.singletone.reflection.SingletonOvercomeReflectionIssue.main(SingletonOvercomeReflectionIssue.java:26)
Serialization: While doing Serialization on singleton classes it can also break the singleton property of singleton classes. Serialization is used to convert an object of byte stream and save in a file or send over a network. Suppose if we serialize an object of a singleton class and then if we de-serialize same object it will create a new instance and hence break the singleton pattern.
package com.javaiq.in;

//Java code to explain breakage of singleton property.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Singleton implements Serializable {
	// public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}
}

public class SingletonSerializationTest {

	public static void main(String[] args) {
		try {
			Singleton instance1 = Singleton.instance;
			ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text"));
			out.writeObject(instance1);
			out.close();

			// deserialize from file to object
			ObjectInput in = new ObjectInputStream(new FileInputStream("file.text"));

			Singleton instance2 = (Singleton) in.readObject();
			in.close();

			System.out.println("hashCode of instance1:- " + instance1.hashCode());
			System.out.println("hashCode of instance2:- " + instance2.hashCode());
		}

		catch (Exception e) {
			e.printStackTrace();
		}
	}
}
Output: As clear from below output by serialization its break singletone property of singletone class.
hashCode of instance1:- 865113938
hashCode of instance2:- 250421012
Overcome serialization issue:To overcome serialization issue, we need to implement readResolve() method in singleton class and return same instance. 
readResolve() method:-For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized.
package com.javaiq.singletone.serialization;

//Java code to Overcome serialization issue of Serialization on singleton classes
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Singleton implements Serializable {
	// public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}

	// we need to implement readResolve method
	protected Object readResolve() {
		return instance;
	}
}

public class SingletonOvercomeSerializationIssueTest {

	public static void main(String[] args) {
		try {
			Singleton instance1 = Singleton.instance;
			ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text"));
			out.writeObject(instance1);
			out.close();

			// deserialize from file to object
			ObjectInput in = new ObjectInputStream(new FileInputStream("file.text"));
			Singleton instance2 = (Singleton) in.readObject();
			in.close();

			System.out.println("instance1 hashCode:- " + instance1.hashCode());
			System.out.println("instance2 hashCode:- " + instance2.hashCode());
		}

		catch (Exception e) {
			e.printStackTrace();
		}
	}
}
Output: Its clear from output we can safe singletone propery of singletone class by using readResolve() method.
instance1 hashCode:- 865113938
instance2 hashCode:- 865113938
Cloning: Cloning can also break singleton propery of singletone class. Cloning is a concept to create duplicate objects of same class. Using clone we can create copy of object. if we create clone of a singleton object, then it will create a copy (new object) that means now there are two instances of a singleton class, so the class is no more singleton.There are two way to overcone this issue.
package com.javaiq.singletone.cloning;

//Java code to cloning with singleton
class SuperClass implements Cloneable {
	int i = 10;

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

//Singleton class
class Singleton extends SuperClass {
//public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}
}

public class SingletonCloningTest {
	public static void main(String[] args) throws CloneNotSupportedException {
		Singleton instance1 = Singleton.instance;
		Singleton instance2 = (Singleton) instance1.clone();
		System.out.println("instance1 hashCode:- " + instance1.hashCode());
		System.out.println("instance2 hashCode:- " + instance2.hashCode());
	}
}
Output: As clear from below output by cloning its break singletone property of singletone class.
instance1 hashCode:- 1829164700
instance2 hashCode:- 2018699554
Overcome Cloning issue:- Way-1 - We can throw CloneNotSupportedException while clone object.
package com.javaiq.in;

//Java code to Overcome cloning issue with singleton
//Way-1 :  We can throw CloneNotSupportedException
class SuperClass implements Cloneable {
	int i = 10;

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

//Singleton class
class Singleton extends SuperClass {
//public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}

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

public class SingletonOvercomeCloningIssueTest1 {
	public static void main(String[] args) throws CloneNotSupportedException {
		Singleton instance1 = Singleton.instance;
		Singleton instance2 = (Singleton) instance1.clone();
		System.out.println("instance1 hashCode:- " + instance1.hashCode());
		System.out.println("instance2 hashCode:- " + instance2.hashCode());
	}
}
Output: We can see in the output its throwing exception while we are trying to clone instance of singletone class.
Exception in thread "main" java.lang.CloneNotSupportedException
	at com.javaiq.in.Singleton.clone(SingletonOvercomeCloningIssueTest1.java:25)
	at com.javaiq.in.SingletonOvercomeCloningIssueTest1.main(SingletonOvercomeCloningIssueTest1.java:32)
Overcome Cloning issue:- Way-2 :- We can return same instance while override clone method.
package com.javaiq.singletone.cloning;

//Java code to overcome cloning issue with singleton.
//Way-2:  we can return same instance in clone
class SuperClass implements Cloneable {
	int i = 10;

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

//Singleton class
class Singleton extends SuperClass {
//public instance initialized when loading the class
	public static Singleton instance = new Singleton();

	private Singleton() {
		// private constructor
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return instance;
	}
}

public class SingletonOvercomeCloningIssueTest2 {
	public static void main(String[] args) throws CloneNotSupportedException {
		Singleton instance1 = Singleton.instance;
		Singleton instance2 = (Singleton) instance1.clone();
		System.out.println("instance1 hashCode:- " + instance1.hashCode());
		System.out.println("instance2 hashCode:- " + instance2.hashCode());
	}
}
Output:
instance1 hashCode:- 1829164700
instance2 hashCode:- 1829164700

No comments:

Post a Comment