Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find path to offending non-Serializable member variable

In Java, Serialization makes reading and writing objects to streams REALLY easy. For instance, the following code snippet is mostly all it takes to write objects to a stream:

ObjectOutputStream oos = ... //Initialize your output stream
Object toWrite = ...         //Initialize what you want to write here
oos.writeObject(toWrite);    //Writes the object to the stream
oos.flush();

This will work just fine, provided that toWrite's class implements the Serializable interface, AND that all of toWrite's non-transient member variables are also Serializable. In other words, the entire Object hierarchy you are trying to send via the toWrite reference must be Serializable. Assume that the only problem with this code is that something inside toWrite isn't Serializable.

If the hierarchy isn't completely Serializable, the call to oos.writeObject(toWrite) throws a java.io.NotSerializableException. This is all fine and good, except that the Exception doesn't give you quite everything you need to fix the problem quickly. It will tell you what class couldn't be serialized. Your stack trace might look something like this:

java.io.NotSerializableException: some.package.and.ClassIDidntExplicitlyReference
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    ...
    at my.package.MyClass.codeThatWroteTheOffendingObject(MyClass.java:###)

This sort of points you in the right direction, but in cases that involve deep Object hierarchies referenced by toWrite, it's not always clear where the offending class reference came from. Let's say I assign toWrite to an instance of MyClass and my instance of MyClass has a member Object reference called nonSerializableReference that has been set to an instance of ClassIDidntExplicitlyReference, which is not Serializable. I'd like to see something like the following printed out:

my.package.MyClass.nonSerializableReference instanceof some.package.and.ClassIDidntExplicitlyReference

I know this problem probably doesn't have a quick solution, and will likely involve using Reflections. Has anyone here on SO done this before and if so, would you mind sharing your insight?

Answer

I ended up using Reflection's Field and Modifier class to find the path. Unfortunately, I can't share my code (against company policy), but I can show you my output. Class A holds an instance of B, B holds an instance of C, and C holds an instance of D. All are Serializable except for D. I ended up using Depth-First-Search to find the path:

A.b -> 
  B.c -> 
    C.d instanceof class D

If I make C.d transient, the search yields no results. Pretty cool!

like image 495
CodeBlind Avatar asked Dec 05 '12 23:12

CodeBlind


3 Answers

Pass the flag -Dsun.io.serialization.extendedDebugInfo=true to the JVM and it should give you exactly the information you want, when a NotSerializableException is thrown.

like image 133
user1586256 Avatar answered Nov 11 '22 14:11

user1586256


I had the same problem and I also implemented the crawler thing you talked about. If somebody is still interested in it, I presented the code here: A good way to find unserializable fields in Java

like image 31
lukas.coenig Avatar answered Nov 11 '22 15:11

lukas.coenig


This is a strange question - you're wanting to determine at runtime what you should be doing at compile time. In theory, the path to the object shouldn't matter - you need to find the objects that reference things that aren't serializable to fix your code.

That said, you could write a simple recursive tree crawler using reflection. That said, you might be able to get away with implementing your own ObjectInputStream that logs the objects coming in appropriately. I recommend taking a look at the source for more detail

like image 38
dfb Avatar answered Nov 11 '22 14:11

dfb