Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Singleton in Java

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 :

  1. Multiple threads access must not lead to multiple instances
  2. The singleton,if serializable, must ensure that de-serialization doesn't create a new instance
  3. 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 !

like image 464
Procrastinator Avatar asked Nov 14 '22 23:11

Procrastinator


1 Answers

Both of those are guaranteed by Java Language specification:

The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

More details can be found from http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469 and java.lang.reflect API.

Item 2: The singleton,if serializable, must ensure that de-serialization doesn't create a new instance is guaranteed by specification of enum serialization.

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant's enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream.

The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization.

Item 3: In case of a reflection attack, an exception/error must be thrown.

Creating new instances via newInstance method is doomed to fail:

IllegalArgumentException ... if this constructor pertains to an enum type.

like image 83
Mikko Maunu Avatar answered Dec 08 '22 00:12

Mikko Maunu