Make Java runtime ignore serialVersionUIDs?

11,326

Solution 1

If you have access to the code base, you could use the SerialVer task for Ant to insert and to modify the serialVersionUID in the source code of a serializable class and fix the problem once for all.

If you can't, or if this is not an option (e.g. if you have already serialized some objects that you need to deserialize), one solution would be to extend ObjectInputStream. Augment its behavior to compare the serialVersionUID of the stream descriptor with the serialVersionUID of the class in the local JVM that this descriptor represents and to use the local class descriptor in case of mismatch. Then, just use this custom class for the deserialization. Something like this (credits to this message):

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 DecompressibleInputStream extends ObjectInputStream {

    private static Logger logger = LoggerFactory.getLogger(DecompressibleInputStream.class);
    
    public DecompressibleInputStream(InputStream in) throws IOException {
        super(in);
    }
    
    @Override
    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;
    }
}

Solution 2

How impractical is this to fix ? If you have the source and can rebuild, can you not just run a script over the entire codebase to insert a

private long serialVersionUID = 1L;

everywhere ?

Solution 3

Use CGLIB to insert them into the binary classes?

Solution 4

The serialization errors at runtime tell you explicitly what the ID is expected to be. Just change your classes to declare these as the ID and everything will be OK. This does involve you making changes but I don't believe that this can be avoided

Share:
11,326
kpozin
Author by

kpozin

Updated on June 13, 2022

Comments

  • kpozin
    kpozin almost 2 years

    I have to work with a large number of compiled Java classes which didn't explicitly specify a serialVersionUID. Because their UIDs were arbitrarily generated by the compiler, many of the classes which need to be serialized and deserialized end up causing exceptions, even though the actual class definitions match up. (This is all expected behavior, of course.)

    It is impractical for me to go back and fix all of this 3rd-party code.

    Therefore, my question is: Is there any way to make the Java runtime ignore differences in serialVersionUIDs, and only fail to deserialize when there are actual differences in structure?

  • Devanshu Mevada
    Devanshu Mevada over 14 years
    This is a good idea but this is not enough. The problem is not to add a serialVersionUID, the problem is to add the same serialVersionUID as in the serialized version.
  • Devanshu Mevada
    Devanshu Mevada over 14 years
    Good idea but... what value do you insert exactly? You need to read the serialVersionUID of the serialized version. That's the tricky part.
  • Artem
    Artem over 14 years
    Oh, whoops. Yes, you'd need to know what's out there.
  • Brian Agnew
    Brian Agnew almost 9 years
    Is the latter message link broken ? (after six years, I'm not hugely surprised, mind)
  • nserror
    nserror almost 8 years
    Many thanks. This snippet did in fact save the day for me as well.
  • nyholku
    nyholku about 6 years
    A rea life saver! Thanks!
  • Pecana
    Pecana about 4 years
    It is quite old, but how to use this then ? How to call it ?
  • OrangeDog
    OrangeDog over 2 years
    This can be greatly simplified to just return ObjectStreamClass.lookupAny(Class.forName(super.readClassDes‌​criptor().getName())‌​). None of the checks/catches are needed.
  • MG Developer
    MG Developer almost 2 years
    I am sorry but you need to really understand the ask before proposing some non workable solution. He said the objects are already serialized and he does not own the third party libraries. I am in the same boat, it would be been nice if Java had a flag to ignore UID.