Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing class name in serialized data

I want to replace the String "com.oldpackage.className" with "com.newPackage.className" in a stream of serialized data. This serialized data is read from the DB and updated after replacing the string.

I am facing some problems while doing the same. If you already guessed it, this is part of a refactoring exercise. Are there any libraries that would help me in manipulating the serialized data? If you can also please comment on any precautions or caveats, it would be of great help.

Thanks a lot, Chris. P.S: Both the old class and the new class do not declare a serialversion ID as part of its fields.

like image 200
ChrisOdney Avatar asked Jan 12 '11 12:01

ChrisOdney


3 Answers

I do not know how to do what your are trying but I can suggest you "legal" solution. Implement convertor that has both old and new packages in classpath and does the following: reads data from DB, de-serializes it using the old package, converts old instances to new in java and then stores new data in DB again.

This solution requires performing some dirty job but it is safe. Moreover the results may be reused in future. But I wish you good luck to find solution that just replaces class name in serialized data.

like image 195
AlexR Avatar answered Nov 11 '22 10:11

AlexR


To precise Tom Hawtin's reply, I have managed to implement it using the following code:

public static ObjectInputStream getSwapOIS( InputStream in ,String fromClass,String toClass)
    throws IOException ,ClassNotFoundException {
    final String from="^"+fromClass,fromArray="^\\[L"+fromClass,toArray="[L"+toClass;
    return new ObjectInputStream(in) {
        protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException
        {
            String name = desc.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            return Class.forName(name);
        }
        protected ObjectStreamClass readClassDescriptor()
            throws IOException, ClassNotFoundException
        {
            ObjectStreamClass cd = super.readClassDescriptor();
            String name = cd.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            if(!name.equals(cd.getName())) {
                cd = ObjectStreamClass.lookup(Class.forName(name));
            }
            return cd;
        }
    };
}

Note that you also need to override readClassDescriptor(). It works for both standard types and arrays and you can even change the class name not just the package name. Just do:

 InputStream in = new ByteArrayInputStream(classBytes);
 ObjectInputStream ois = getSwapOIS(    in,
                                        "com.oldpackage.className",
                                        "com.newpackage.newClassName");
 Object myObject= ois.readObject();
like image 25
ehubin Avatar answered Nov 11 '22 10:11

ehubin


IIRC you can (with sufficient permissions) override ObjectInputStream.resolveClass (and resolveProxyClass) to return a Class with a different package names. Although, IIRC, you cannot change the simple class name.

like image 3
Tom Hawtin - tackline Avatar answered Nov 11 '22 11:11

Tom Hawtin - tackline