Sunday, March 7, 2010

Java Tip for Object Serialization

Ever faced a dilemma over how could you control the Java Serialization mechanism. Recently, I was in a similar situation where one of our third party classes actually broke its backward compatibility by changing the Serial version UID that resulted in failure to read serialized objects for that class.

One of the Threads on the java forum helped me in finding a solution to the above problem. I thought it would be worth sharing it with you all.

The problem was addressed by writing a class that extends the ObjectinputStream class and overriding the readClassDescriptor Method that return the latest description of the class being deserialized.

Here is a sample code that explains it.

public class CustomObjectInputStream extends ObjectInputStream

{

/**

* Collection of classes for which this hook should come into picture.

*/

private static final ArrayList hookedClasses = new ArrayList();

static

{

hookedClasses.add("org.apache.xerces.dom.AttributeMap");

}

public CustomObjectInputStream(InputStream in) throws IOException

{

super(in);

}

/*

* (non-Javadoc)

* @see java.io.ObjectInputStream#readClassDescriptor()

*/

protected ObjectStreamClass readClassDescriptor() throws IOException,

ClassNotFoundException

{

// initially streams descriptor

ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

Class localClass = Class.forName(resultClassDescriptor.getName());

if (localClass == null)

{

Logger.error("No local class for "

+ resultClassDescriptor.getName());

return resultClassDescriptor;

}

if (!hookedClasses.contains(localClass.getName()))

{

return resultClassDescriptor;

}

ObjectStreamClass localClassDescriptor = ObjectStreamClass

.lookup(localClass);

if (localClassDescriptor != null)

{ // only if class implements serializable

final long localSUID = localClassDescriptor.getSerialVersionUID();

final long streamSUID = resultClassDescriptor.getSerialVersionUID();

if (streamSUID != localSUID)

{ // check for serialVersionUID mismatch.

final StringBuffer s = new StringBuffer(

"WARNING: Overriding serialized class version mismatch for class: "

+ localClass.getName());

s.append(" local serialVersionUID = ").append(localSUID);

s.append(" stream serialVersionUID = ").append(streamSUID);

Logger.debug(s.toString());

resultClassDescriptor = localClassDescriptor; // Use local class

// descriptor

// for

// deserialization

}

}

return resultClassDescriptor;

}

}