Please note that I have gone through the below thread :
What is an efficient way to implement a singleton pattern in Java?
To summarize, there are important considerations while writing a singleton :
- Multiple threads access must not lead to multiple instances
- The singleton,if serializable, must ensure that de-serialization doesn't create a new instance
- In case of a reflection attack, an exception/error must be thrown.
Now, as mentioned in the above thread too, using the enumeration for creating a singleton ensures all the 3 points mentioned above.
Below are the sample codes I wrote
/*Singleton class using enum*/
package com.java.patterns;
public enum MemoryTasks {
INSTANCE;
public void performScheduleTasks(){
System.out.println("In performScheduleTasks().");
}
private MemoryTasks(){
System.out.println("In private constructor."+this.hashCode());
}
public int returnHashCodeOfInstance(){
return INSTANCE.hashCode();
}
}
/*Class to access private constructor of the Singleton*/
package com.java.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import com.java.patterns.MemoryTasks;
public class LaunchReflection {
public static void main(String[] args) {
launchRelectionAttack();
}
public static void launchRelectionAttack(){
Class vulnClass = null;
Constructor [] vulClassConstr = null;
Type [] vulClassConstrParamTypes = null;
try {
vulnClass = Class.forName("com.java.patterns.MemoryTasks");
vulClassConstr = vulnClass.getDeclaredConstructors();
for(Constructor constr : vulClassConstr){
vulClassConstrParamTypes = constr.getGenericParameterTypes();
System.out.println("Modifier private ? "+Modifier.isPrivate(constr.getModifiers()));
}
/*for(Type paramType : vulClassConstrParamTypes){
System.out.println(paramType.toString());
}*/
System.out.println(MemoryTasks.INSTANCE.returnHashCodeOfInstance());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/*Class to write the enum to a file*/
package com.java.io.serialize;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import com.java.patterns.MemoryTasks;
public class ObjectWriter {
public static void main(String[] args) {
try {
writeSerObjectToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeSerObjectToFile() throws IOException {
File file = null;
FileOutputStream fos = null;
ObjectOutputStream oos = null;
file = new File("D:/Omkar/Dump/SerObj");
try{
if(!file.exists()){
file.createNewFile();
}
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(MemoryTasks.INSTANCE);
}catch(IOException e){
e.printStackTrace();
}
finally{
oos.close();
fos.close();
}
}
}
/*Class to read the serialized enum from file*/
package com.java.io.serialize;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.java.patterns.MemoryTasks;
public class ObjectRead {
public static void main(String[] args) {
readSerObjFromFile();
}
private static void readSerObjFromFile() {
File file = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
file = new File("D:/Omkar/Dump/SerObj");
try {
fis = new FileInputStream(file);
if(fis.available() > 0){
ois = new ObjectInputStream(fis);
MemoryTasks instance = (MemoryTasks) ois.readObject();
System.out.println("Reading from serialised file : "+instance.returnHashCodeOfInstance());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I would be glad if I am explained how the points 2 and 3 are ensured !