Wednesday, September 29, 2010

An unwise attack on the Singletons

Few days back I was disturbed with some maven build issues in office. I got back home pretty late. Already, as you can understand, mood was off. I thought of relaxing by watching a good movie but next day I had an interview scheduled. So, I had to brush up my skills a bit (I don't use them often in office :-) ). The first topic I started with was Singleton pattern. This one is so famous among interviewers that you better be able to explain it.
I had read the pattern many a times already. Nothing to prove the book wrong, never occurred to me. But that day, may be due the horrible mood, I challenged the pattern. I thought I'll find ways to break the pattern down. I wanted to create 2 Singleton instances in same JVM. Thats how the attack began.

Below is the code for creating a Singleton class

package com.test;

import java.io.Serializable;

public class Singleton implements Serializable
{
   private static final long serialVersionUID = 1L;
   private static Singleton s = null;
   
   // to block instantiation by others
   private Singleton()
   {

   }
   
   public static synchronized Singleton getInstance()
   {
      if(s == null)
      {
         s = new Singleton();
      }
      return s;
   }
}

Here is the code to test the pattern

package com.test;

public class SingletonTest
{
   public static void main(String[] args)
   {
      Singleton s1 = Singleton.getInstance();
      Singleton s2 = Singleton.getInstance();
      try
      {
         // To wait for VisualVM to analyze
         Thread.sleep(60000);
      }
      catch (InterruptedException e)
      {
         e.printStackTrace();
      }
   }
}

I have used VisualVM (comes as part of JDK 6) tool to check the no. of instances for Singleton class. You just need to pass a VM argument (-Dcom.sun.management.jmxremote) while running the program.
So, far so good. Only one instance.


This was rather bookish experiment. I knew it would happen. But still tried to make sure my setup is ok. Though the actual action happens next.

Then I tried serialization of the singleton object. I saved the serialized object to a file. Next, I de-serialized the object from file. Hurray!!! 2 Singleton objects are loaded in JVM. The test class code is-


package com.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SingletonTest
{
   public static void main(String[] args)
   {
      Singleton s1 = Singleton.getInstance();
      Singleton s2 = null;
      try
      {
         ObjectOutputStream oos = new ObjectOutputStream(
             new FileOutputStream("D:\\singleton.ser"));
         oos.writeObject(s1);
         oos.close();
         ObjectInputStream ois = new ObjectInputStream(
             new FileInputStream("D:\\singleton.ser"));
         s2 = (Singleton)ois.readObject();
         // To wait for VisualVM to analyze
         Thread.sleep(60000);
      }
      catch (InterruptedException e)
      {
         e.printStackTrace();
      }
      catch (FileNotFoundException e)
      {
         e.printStackTrace();
      }
      catch (IOException e)
      {
         e.printStackTrace();
      }
      catch (ClassNotFoundException e)
      {
         e.printStackTrace();
      }
   }
}


Here is the VisualVM output-



So, the pattern has been broken. It is not anymore a Singleton. But the question here is, how to prevent this attack. The code goes here -



package com.test;

import java.io.Serializable;

public class Singleton implements Serializable
{
   private static final long serialVersionUID = 1L;
   private static Singleton s = null;
   
   // to block instantiation by others
   private Singleton()
   {

   }
   
   public static synchronized Singleton getInstance()
   {
      if(s == null)
      {
         s = new Singleton();
      }
      return s;
   }
   
   protected Object readResolve() 
   {
      return s;
   }
}

I have added a readReasolve() method in the modified Singleton class above. This gets called during de-serialization and the singleton static reference is returned.


Finally, I tried Java reflection (very elegant way indeed). It is so powerful that you can even call private constructors. See how-


package com.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;



public class SingletonTest
{
   public static void main(String[] args)
   {
      Singleton s1 = Singleton.getInstance();
      Singleton s2 = null;
      try
      {  
         Constructor<Singleton>[] con =
                  (Constructor<Singleton>[]) Singleton.class
                           .getDeclaredConstructors();
         for(Constructor<Singleton> c : con)
         {
            c.setAccessible(true);
            s2 = c.newInstance(null);
         }
         // To wait for VisualVM to analyze
         Thread.sleep(60000);
      }
      catch (InterruptedException e)
      {
         e.printStackTrace();
      }
      catch (InstantiationException e)
      {
         e.printStackTrace();
      }
      catch (IllegalAccessException e)
      {
         e.printStackTrace();
      }
      catch (IllegalArgumentException e)
      {
         e.printStackTrace();
      }
      catch (InvocationTargetException e)
      {
         e.printStackTrace();
      }
   }
}

Yahooo!!! again success. This time also I was able to create 2 instances of the singleton class. Though by this time I was more oriented to find a solution to this one. Let's see that-

package com.test;

import java.io.Serializable;

public class Singleton implements Serializable
{
   private static final long serialVersionUID = 1L;
   private static Singleton s = null;
   
   // to block instantiation by others
   private Singleton() throws InstantiationException
   {
      if(s != null)
      {
         throw new InstantiationException();
      }
   }
   
   public static synchronized Singleton getInstance()
            throws InstantiationException
   {
      if(s == null)
      {
         s = new Singleton();
      }
      return s;
   }
   
   protected Object readResolve() 
   {
      return s;
   }
}

In this solution, I have made the private constructor throw InstantiationException when the static reference is not null (yes, constructors can throw exceptions). Thus it will throw an exception if we call the private constructor 2nd time even if in a reflective way.
Is the last solution thread safe? Frankly speaking, I don't know. This part is yet to be explored. There may be some gotchas here. But I am not feeling wise enough to try it now. May be sometime later.