Uploaded image for project: 'Wicket'
  1. Wicket
  2. WICKET-265

Improve diagnostics on serialization exceptions

    Details

    • Type: Improvement
    • Status: Resolved
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 2.0 branch (discontinued), 1.3.0-beta1
    • Component/s: None
    • Labels:
      None

      Description

      The JDK's default serialization exception doesn't give a whole lot of information other than the object that didn't implement serializable. We can try to improve on this by giving more info of the object tree that was being serialized

        Activity

        Hide
        ehillenius Eelco Hillenius added a comment -

        Al provides this test:

        import java.io.IOException;
        import java.io.NotSerializableException;
        import java.io.ObjectOutputStream;
        import java.io.Serializable;
        import java.lang.reflect.Field;
        import java.lang.reflect.Modifier;
        import java.util.HashSet;
        import java.util.LinkedList;

        public class Foo extends ObjectOutputStream {

        private static final long serialVersionUID = 1L;

        private LinkedList stack = new LinkedList();
        private HashSet set = new HashSet();

        public static void main(String[] args) throws Exception

        { new Foo().writeObject(new A()); }

        public Foo() throws IOException {}

        @Override
        protected final void writeObjectOverride(Object obj) throws IOException
        {
        // Check for circular reference.
        if (set.contains(obj))

        { return; }

        if (stack.isEmpty())

        { stack.add("Class " + obj.getClass().getName()); }

        set.add(obj);
        Field[] fields = obj.getClass().getFields();
        for (int i = 0; i < fields.length; i++)
        {
        StringBuffer buffer = new StringBuffer();
        Field f = fields[i];
        int m = f.getModifiers();
        if (fields[i].getType().isPrimitive() || Modifier.isTransient(m))

        { continue; }

        if (Modifier.isPrivate(m))

        { buffer.append("private "); }

        if (Modifier.isProtected(m))

        { buffer.append("protected "); }

        if (Modifier.isPublic(m))

        { buffer.append("public "); }

        if (Modifier.isAbstract(m))

        { buffer.append("abstract "); }

        if (Modifier.isFinal(m))

        { buffer.append("final "); }

        if (Modifier.isStatic(m))

        { buffer.append("static "); }

        if (Modifier.isVolatile(m))

        { buffer.append("volatile "); }

        buffer.append(f.getType().getName()).append("");
        buffer.append(" ").append(f.getName());
        stack.add(buffer.toString());
        if (Serializable.class.isAssignableFrom(fields[i].getType()))
        {
        try

        { writeObjectOverride(fields[i].get(obj)); }

        catch (IllegalAccessException e)

        { throw new RuntimeException(getPrettyPrintedStack(), e); }

        }
        else

        { throw new RuntimeException(getPrettyPrintedStack().toString(), new NotSerializableException(fields[i].getType().getName())); }

        stack.removeLast();
        }
        if (stack.size() == 1)

        { set.clear(); }

        }

        private String getPrettyPrintedStack()
        {
        StringBuffer result = new StringBuffer("Unable to serialise class.\nField hierarchy is:");
        StringBuffer spaces = new StringBuffer();
        while (!stack.isEmpty())

        { spaces.append(" "); result.append("\n").append(spaces).append(stack.removeFirst()); }

        return result.toString();
        }
        }

        class A implements Serializable
        {
        private static final long serialVersionUID = 1L;
        public B myFieldForB = new B();
        public D myFieldForD = new D();
        }

        class B implements Serializable
        {
        private static final long serialVersionUID = 1L;
        public C myFieldForC = new C();
        }

        class C
        {
        private static final long serialVersionUID = 1L;
        public int foo = 1;
        }

        class D implements Serializable
        {
        public int bar = 2;
        }

        Show
        ehillenius Eelco Hillenius added a comment - Al provides this test: import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.LinkedList; public class Foo extends ObjectOutputStream { private static final long serialVersionUID = 1L; private LinkedList stack = new LinkedList(); private HashSet set = new HashSet(); public static void main(String[] args) throws Exception { new Foo().writeObject(new A()); } public Foo() throws IOException {} @Override protected final void writeObjectOverride(Object obj) throws IOException { // Check for circular reference. if (set.contains(obj)) { return; } if (stack.isEmpty()) { stack.add("Class " + obj.getClass().getName()); } set.add(obj); Field[] fields = obj.getClass().getFields(); for (int i = 0; i < fields.length; i++) { StringBuffer buffer = new StringBuffer(); Field f = fields [i] ; int m = f.getModifiers(); if (fields [i] .getType().isPrimitive() || Modifier.isTransient(m)) { continue; } if (Modifier.isPrivate(m)) { buffer.append("private "); } if (Modifier.isProtected(m)) { buffer.append("protected "); } if (Modifier.isPublic(m)) { buffer.append("public "); } if (Modifier.isAbstract(m)) { buffer.append("abstract "); } if (Modifier.isFinal(m)) { buffer.append("final "); } if (Modifier.isStatic(m)) { buffer.append("static "); } if (Modifier.isVolatile(m)) { buffer.append("volatile "); } buffer.append(f.getType().getName()).append(""); buffer.append(" ").append(f.getName()); stack.add(buffer.toString()); if (Serializable.class.isAssignableFrom(fields [i] .getType())) { try { writeObjectOverride(fields[i].get(obj)); } catch (IllegalAccessException e) { throw new RuntimeException(getPrettyPrintedStack(), e); } } else { throw new RuntimeException(getPrettyPrintedStack().toString(), new NotSerializableException(fields[i].getType().getName())); } stack.removeLast(); } if (stack.size() == 1) { set.clear(); } } private String getPrettyPrintedStack() { StringBuffer result = new StringBuffer("Unable to serialise class.\nField hierarchy is:"); StringBuffer spaces = new StringBuffer(); while (!stack.isEmpty()) { spaces.append(" "); result.append("\n").append(spaces).append(stack.removeFirst()); } return result.toString(); } } class A implements Serializable { private static final long serialVersionUID = 1L; public B myFieldForB = new B(); public D myFieldForD = new D(); } class B implements Serializable { private static final long serialVersionUID = 1L; public C myFieldForC = new C(); } class C { private static final long serialVersionUID = 1L; public int foo = 1; } class D implements Serializable { public int bar = 2; }
        Hide
        almaw Alastair Maw added a comment -

        An ObjectOutputStream implementation that produces a nice pretty tree. Note that it's pretty expensive running this - only do it when you know something is broken.

        Show
        almaw Alastair Maw added a comment - An ObjectOutputStream implementation that produces a nice pretty tree. Note that it's pretty expensive running this - only do it when you know something is broken.
        Hide
        ehillenius Eelco Hillenius added a comment -

        Improved version

        Show
        ehillenius Eelco Hillenius added a comment - Improved version
        Hide
        ehillenius Eelco Hillenius added a comment -

        Agh. Still not there. Gotta filter out static and transient fields.

        Show
        ehillenius Eelco Hillenius added a comment - Agh. Still not there. Gotta filter out static and transient fields.
        Hide
        ehillenius Eelco Hillenius added a comment -

        correction, only static

        Show
        ehillenius Eelco Hillenius added a comment - correction, only static
        Hide
        ehillenius Eelco Hillenius added a comment -

        3rd try

        Show
        ehillenius Eelco Hillenius added a comment - 3rd try
        Hide
        ehillenius Eelco Hillenius added a comment -

        On second thought: that ObjectOutputStream didn't really make sense. Rewrote it to a static method in wicket.util.Objects (that in turn uses internal classes).

        Show
        ehillenius Eelco Hillenius added a comment - On second thought: that ObjectOutputStream didn't really make sense. Rewrote it to a static method in wicket.util.Objects (that in turn uses internal classes).
        Hide
        ehillenius Eelco Hillenius added a comment -

        Ok, that didn't actually work at all. Turned out to be a pretty naive approach. We have to rewrite it to make it resemble actual serialization more closely.

        Show
        ehillenius Eelco Hillenius added a comment - Ok, that didn't actually work at all. Turned out to be a pretty naive approach. We have to rewrite it to make it resemble actual serialization more closely.
        Hide
        ehillenius Eelco Hillenius added a comment -

        wicket.util.io.SerializableChecker does the job. Currently implemented for 1.3. I'll port later this week.

        Show
        ehillenius Eelco Hillenius added a comment - wicket.util.io.SerializableChecker does the job. Currently implemented for 1.3. I'll port later this week.

          People

          • Assignee:
            ehillenius Eelco Hillenius
            Reporter:
            ehillenius Eelco Hillenius
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development