Making JVM to ignore serialVersionUIDs mismatch

Issue:

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class which is highly sensitive to the class details and may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. So,  if an older version of a serializable class doesn’t explicitly mention a serialVersionUID then deserialization of that object may fail even though the actual class definition matches.  In such a scenario we just want JVM to ignore differences in these serialVersionUIDs and not throw any exception in case they mismatch!

Solution:

Extend from ObjectInputStream class and override readClassDescriptor() to add logic which compares serialVersionUID of the class in local JVM and serialVersionUID of the stream descriptor and if they vary, then assign localClassDescriptor to the stream descriptor. Then wherever we are creating objects of ObjectInputStream, we use CustomInputStream instead and it deserializes the old object with new serialVersionUID.

import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomInputStream extends ObjectInputStream {

    private static Logger logger = LoggerFactory.getLogger(CustomInputStream.class);

    public CustomInputStream(InputStream in) throws IOException {
        super(in);
    }

    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
        Class localClass; // the class in the local JVM that this descriptor represents.
        try {
            localClass = Class.forName(resultClassDescriptor.getName());
        } catch (ClassNotFoundException e) {
            logger.error("No local class for " + resultClassDescriptor.getName(), e);
            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("Overriding serialized class version mismatch: ");
                s.append("local serialVersionUID = ").append(localSUID);
                s.append(" stream serialVersionUID = ").append(streamSUID);
                Exception e = new InvalidClassException(s.toString());
                logger.error("Potentially Fatal Deserialization Operation.", e);
                resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
            }
        }
        return resultClassDescriptor;
    }
}