2

There're some library classes that although implement Serializable, fail to serialize correctly. I can't fix them, but I can extend ObjectOutputStream and ObjectInputStream for some workaround.

I want my ObjectOutputStream to write additional data for each instance of class A and ObjectInputStream to read and apply that data after A is deserialized.

Currently I have a mid-workaround that requires explicit additional calls to writeObject() and readObject(). I'd prefer to manage without these calls.

Uncomment /* */ blocks to see how it works.

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class CloneSerializableTest2 {
    // library classes
        public static class A implements Serializable {
            public transient String s1;
        }

        public static class MyA extends A {

            public String s2;
        }

/*
    private static class AHolder implements Serializable {
        private static final Field s1Fld;
        static {
            try {
                s1Fld = A.class.getDeclaredField("s1");
                s1Fld.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException("Unexpected error", e);
            }
        }
        private String s1;
        private A a;
        public AHolder(A m) {
            this.a = m;
            try {
                s1 = (String)s1Fld.get(m);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Unexpected error", e);
            }
        }
        public void restoreA() {
            try {
                s1Fld.set(a, s1);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Unexpected error", e);
            }
        }
    }
*/
    @SuppressWarnings("unchecked")
    public static <T> T cloneSerializable(T o) {
        try {
/*
            final List<AHolder> accumSrc = new ArrayList<AHolder>();
*/
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bout)
/*
            {
                {
                    enableReplaceObject(true);
                }
                @Override
                protected Object replaceObject(Object obj) throws IOException
                {
                    if (obj instanceof A) {
                        accumSrc.add(new AHolder((A)obj));
                    }
                    return super.replaceObject(obj);
                }
            }
*/
            ;
            out.writeObject(o);
/*
            out.writeObject(accumSrc);
*/
            out.close();
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ObjectInputStream in = new ObjectInputStream(bin);
            Object copy = in.readObject();
/*
            List<AHolder> accumDst = (List<AHolder>)in.readObject();
            for (AHolder r : accumDst) {
                r.restoreA();
            }
*/
            in.close();
            return (T)copy;
        } catch (IOException e) {
            throw new RuntimeException("Unexpected error", e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Unexpected error", e);
        }
    }
    @Test
    public void testIt() throws Exception {
        try {
        MyA m1 = new MyA();
        m1.s1 = "a";
        m1.s2 = "b";

        m1 = cloneSerializable(m1);
        assertEquals("a", m1.s1);
        assertEquals("b", m1.s2);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }
}
6
  • Why can't you patch the class which is not correct? Commented Feb 7, 2012 at 12:42
  • I need reflection, because in real life the transient field is private. Note that "A" may have descendants with their own fields and all this should be handled properly Commented Feb 7, 2012 at 12:43
  • Peter: because it's in a library Commented Feb 7, 2012 at 12:46
  • You can patch any library, even the system libraries. There can be good reasons not to do this. I just wanted to make sure you have considered this already and have a specific reason this can't be done. Commented Feb 7, 2012 at 12:48
  • You want me to fork a non-free library and put it to repo2.maven.org ? Commented Feb 7, 2012 at 13:02

1 Answer 1

1

Answering to myself

import java.io.*;
import java.lang.reflect.Field;
import org.junit.Test;
import static org.junit.Assert.*;
public class CloneSerializableTest2 {
    // library classes
        public static class A implements Serializable {
            public transient String s1;
        }

        public static class MyA extends A {

            public String s2;
        }


    private static class AHolder implements Serializable, Externalizable {
        private static final Field s1Fld;
        static {
            try {
                s1Fld = A.class.getDeclaredField("s1");
                s1Fld.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException("Unexpected error", e);
            }
        }
        private String s1;
        private A a;

        @SuppressWarnings("unused")
        public AHolder() {
        }
        public AHolder(A m) {
            this.a = m;
            try {
                s1 = (String)s1Fld.get(m);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Unexpected error", e);
            }
        }

        private Object readResolve() {
            try {
                s1Fld.set(a, s1);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Unexpected error", e);
            }
            return a;
        }

        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(s1);
            ObjectOutputStream out2 = ((ObjectOutputStream)out);
            out2.writeUnshared(a);
        }

        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            s1 = (String)in.readObject();
            a = (A)in.readObject();
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T cloneSerializable(T o) {
        try {

            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bout)
            {
                {
                    enableReplaceObject(true);
                }
                @Override
                protected Object replaceObject(Object obj) throws IOException
                {
                    if (obj instanceof A) {
                        obj = new AHolder((A) obj);
                    } else if (obj instanceof AHolder) {
                        obj = ((AHolder)obj).a;
                    }
                    return super.replaceObject(obj);
                }
            };

            out.writeObject(o);

            out.close();
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ObjectInputStream in = new ObjectInputStream(bin);
            Object copy = in.readObject();

            in.close();
            return (T)copy;
        } catch (IOException e) {
            throw new RuntimeException("Unexpected error", e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Unexpected error", e);
        }
    }
    @Test
    public void testIt() throws Exception {
        try {
        MyA m1 = new MyA();
        m1.s1 = "a";
        m1.s2 = "b";

        m1 = cloneSerializable(m1);
        assertEquals("a", m1.s1);
        assertEquals("b", m1.s2);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.