Serializable Singleton Class

Previously I have posted about Singleton Design Pattern implementation and handling concurrency. In this post I am going to explains challenges for serializable Singleton class.
Lets know about Singleton Class ( Single Design Pattern), A Singleton class restricts access to its constructor to ensure that only a single instance is ever created. I have taken example from previous post only
 
   1: /**
   2:  * Singleton Class
   3:  * 
   4:  * @author ydevatra
   5:  * 
   6:  */
   7: public class SingletonResource implements Serializable {
   8:  
   9:     /* Private instance reference */
  10:     private static SingletonResource resource = new SingletonResource();
  11:  
  12:     /**
  13:      * Private constructor to restrict creation of instance of this class by
  14:      * other class.
  15:      */
  16:     private SingletonResource() {
  17:         // Initialization code...
  18:     }
  19:  
  20:     /**
  21:      * provides access to single instance of this class.
  22:      * 
  23:      * @return
  24:      */
  25:     public SingletonResource getInstance() {
  26:         if (resource == null) {
  27:             resource = new SingletonResource();
  28:         }
  29:         return resource;
  30:     }
  31: }



This class would no longer be a singleton if the words “implements Serializable” were added to its declaration. It doesn’t matter whether the class uses the default serialized form or a custom serialized form ,nor does it matter whether the class provides an explicit readObject method. Any readObject method, whether explicit or default, returns a newly created instance, which will not be the same instance that was created at class initialization time.

Lets test this, I have updated same class to implement Serializable and wrote a main class to  serialize and deserialize resource object . Here is the complete code

   1: package com.ydtech.singleton;
   2:  
   3: import java.io.ByteArrayInputStream;
   4: import java.io.ByteArrayOutputStream;
   5: import java.io.IOException;
   6: import java.io.ObjectInputStream;
   7: import java.io.ObjectOutputStream;
   8: import java.io.Serializable;
   9:  
  10: /**
  11:  * Singleton Class
  12:  * 
  13:  * @author ydevatra
  14:  * 
  15:  */
  16: public class SingletonResource implements Serializable {
  17:     
  18:     private String resourceName = "Resource 1";
  19:  
  20:     public String getResourceName() {
  21:         return resourceName;
  22:     }
  23:  
  24:     public void setResourceName(String resourceName) {
  25:         this.resourceName = resourceName;
  26:     }
  27:  
  28:     /* Private instance reference */
  29:     private static SingletonResource resource = new SingletonResource();
  30:  
  31:     /**
  32:      * Private constructor to restrict creation of instance of this class by
  33:      * other class.
  34:      */
  35:     private SingletonResource() {
  36:         // Initialization code...
  37:     }
  38:  
  39:     /**
  40:      * provides access to single instance of this class.
  41:      * 
  42:      * @return
  43:      */
  44:     public static SingletonResource getInstance() {
  45:         if (resource == null) {
  46:             resource = new SingletonResource();
  47:         }
  48:         return resource;
  49:     }
  50:  
  51:     public static void main(String[] args) {
  52:          SingletonResource resource = SingletonResource.getInstance();
  53:          byte[] byteStream = serialize(resource);
  54:          SingletonResource deserializedResource = (SingletonResource)deserialize(byteStream);
  55:          System.out.println(deserializedResource.getResourceName());
  56:          System.out.println("Are they equal ? " + (resource == deserializedResource));
  57:          
  58:     }
  59:      
  60:     private static byte[] serialize(Object obj){
  61:         ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
  62:         try {
  63:             ObjectOutputStream objStr = new ObjectOutputStream(byteStream);
  64:             objStr.writeObject(obj);
  65:         } catch (IOException e) {
  66:             e.printStackTrace();
  67:         }
  68:         return byteStream.toByteArray();
  69:     }
  70:     
  71:     private static Object deserialize(byte[] byteStream){
  72:         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteStream);
  73:         Object obj = null;
  74:         try {
  75:             ObjectInputStream objStream = new ObjectInputStream(byteInputStream);
  76:             obj = objStream.readObject();
  77:         } catch (IOException e) {
  78:             e.printStackTrace();
  79:         } catch (ClassNotFoundException e) {
  80:             e.printStackTrace();
  81:         }
  82:         return obj;
  83:     }
  84: }
  85:  
  86:  

If I run this class I will get following result :

Are they equal ? false

Here we have adapted default serialization by just implementing Serializable interface. As serialized/deserialized object are always two different objects , above result is expected.

Solving this problem :


The readResolve feature allows you to substitute another instance for the one created by readObject. If the class of an object being deserialized defines a readResolve method with the proper declaration, this method is invoked on the newly created object after it is deserialized. The object reference returned by this method is then returned in place of the newly created object. In most uses of this feature, no reference to the newly created object is retained, so it immediately becomes eligible for garbage collection. If the SingletonResource class is made to implement Serializable, the following readResolve method suffices to guarantee the singleton property:

   1: // readResolve for instance control - you can do better!
   2: private Object readResolve() {
   3:     return resource;
   4: }


In fact, if you depend on readResolve for instance control, all instance fields with object reference types must be declared transient. Otherwise, it is possible for a determined attacker to secure a reference to the deserialized object before its readResolve method is run.

Lets test again with readResolve() implementation , the final code will be like below

   1: package com.ydtech.singleton;
   2:  
   3: import java.io.ByteArrayInputStream;
   4: import java.io.ByteArrayOutputStream;
   5: import java.io.IOException;
   6: import java.io.ObjectInputStream;
   7: import java.io.ObjectOutputStream;
   8: import java.io.Serializable;
   9:  
  10: /**
  11:  * Singleton Class
  12:  * 
  13:  * @author ydevatra
  14:  * 
  15:  */
  16: public class SingletonResource implements Serializable {
  17:     
  18:     private String resourceName = "Resource 1";
  19:  
  20:     public String getResourceName() {
  21:         return resourceName;
  22:     }
  23:  
  24:     public void setResourceName(String resourceName) {
  25:         this.resourceName = resourceName;
  26:     }
  27:  
  28:     /* Private instance reference */
  29:     private static SingletonResource resource = new SingletonResource();
  30:  
  31:     /**
  32:      * Private constructor to restrict creation of instance of this class by
  33:      * other class.
  34:      */
  35:     private SingletonResource() {
  36:         // Initialization code...
  37:     }
  38:  
  39:     /**
  40:      * provides access to single instance of this class.
  41:      * 
  42:      * @return
  43:      */
  44:     public static SingletonResource getInstance() {
  45:         if (resource == null) {
  46:             resource = new SingletonResource();
  47:         }
  48:         return resource;
  49:     }
  50:  
  51:     // readResolve for instance control - you can do better!
  52:     private Object readResolve() {
  53:         return resource;
  54:     }
  55:     
  56:     public static void main(String[] args) {
  57:          SingletonResource resource = SingletonResource.getInstance();
  58:          byte[] byteStream = serialize(resource);
  59:          SingletonResource deserializedResource = (SingletonResource)deserialize(byteStream);
  60:         // System.out.println(deserializedResource.getResourceName());
  61:          System.out.println("Are they equal ? " + (resource == deserializedResource));
  62:          
  63:     }
  64:      
  65:     private static byte[] serialize(Object obj){
  66:         ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
  67:         try {
  68:             ObjectOutputStream objStr = new ObjectOutputStream(byteStream);
  69:             objStr.writeObject(obj);
  70:         } catch (IOException e) {
  71:             e.printStackTrace();
  72:         }
  73:         return byteStream.toByteArray();
  74:     }
  75:     
  76:     private static Object deserialize(byte[] byteStream){
  77:         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteStream);
  78:         Object obj = null;
  79:         try {
  80:             ObjectInputStream objStream = new ObjectInputStream(byteInputStream);
  81:             obj = objStream.readObject();
  82:         } catch (IOException e) {
  83:             e.printStackTrace();
  84:         } catch (ClassNotFoundException e) {
  85:             e.printStackTrace();
  86:         }
  87:         return obj;
  88:     }
  89: }
  90:  
  91:  


Now run this class and result will be

Are they equal ? true



Here we have got result as true, it means readResolve() has done its magic to maintain single instance of or singleton class even after serialization/deserilization.


References :

Book [Effective Java 2nd Edition]

Comments

Popular posts from this blog

Composite Design Pattern by example

State Design Pattern by Example

Eclipse command framework core expression: Property tester