java.io.NotSerializableException in your HttpSession

Ever had a java.io.NotSerializableException in your code and found it very hard to debug? The JVM stack trace is nearly useless, telling you which code triggered the serialization, but not what it is trying to serialize.

Particularly tedious are HTTP sessions that refuse to serialize. In Wicket we store detached components in the session between requests. If you run a clustered app-server with session replication and add objects to your components that don’t implement Serializable, you’re toast. To avoid you deploying something with this issue and getting fired, we have a setting in development mode that tries to serialize the session on every page request. Unfortunately, although it helps you spot this problem early, it’s lumbered with the same unhelpful java.io.NotSerializableException as everything else in the world.

How do we make these issues easier to debug? Well, I’ve just written an ObjectOutputStream subclass which produces output like this:

java.lang.RuntimeException: Unable to serialize class: com.foo.C
Field hierarchy is:
  Class com.foo.A
    public com.foo.B myFieldForB
      public com.foo.C myFieldForC <----- field that is not serializable
	at DebuggingObjectOutputStream.writeObjectOverride(DebuggingObjectOutputStream.java:105)
	at DebuggingObjectOutputStream.writeObjectOverride(DebuggingObjectOutputStream.java:96)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:298)
	at DebuggingObjectOutputStream.main(DebuggingObjectOutputStream.java:28)
Caused by: java.io.NotSerializableException: com.foo.C
	... 4 more


Eelco is going to plumb this into our Wicket session serialization debug check so we have much sweeter output for these problems in future. Nice!:-)

Here's the code:

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 DebuggingObjectOutputStream extends ObjectOutputStream {
 
  private static final long serialVersionUID = 1L;
 
  private LinkedList stack = new LinkedList();
  private HashSet set = new HashSet();
 
  public DebuggingObjectOutputStream() 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) ||
          Modifier.isStatic(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.isFinal(m)) {
        buffer.append("final ");
      }
      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(fields[i].getType().getName()), e);
        }
      }
 
      else {
        throw new RuntimeException(
            getPrettyPrintedStack(fields[i].getType().getName()).toString(),
            new NotSerializableException(fields[i].getType().getName())
        );
      }
      stack.removeLast();
    }
    if (stack.size() == 1) {
      set.clear();
      stack.removeLast();
    }
  }
 
  private String getPrettyPrintedStack(String type) {
    set.clear();
    StringBuffer result = new StringBuffer();
    StringBuffer spaces = new StringBuffer();
    result.append("Unable to serialize class: ");
    result.append(type);
    result.append("nField hierarchy is:");
    while (!stack.isEmpty()) {
      spaces.append("  ");
      result.append("n").append(spaces).append(stack.removeFirst());
    }
    result.append(" <----- field that is not serializable");
    return result.toString();
  }
}

:-)

7 thoughts on “java.io.NotSerializableException in your HttpSession”