Here's an otherwise innocuous utility method:
public static Object toObject(byte[] bytes) throws IOException,
ClassNotFoundException
{
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
Why would something that works in a unit testing environment fail with a StreamCorruptedException in a more complex container? (HINT: Class loading is almost always the culprit...) As the javadoc for toObject() says, a StreamCorruptedException can result if the stream violates internal consistency checks, but what does this have to do with class loading?
Well... if you trace through the source, which is no mean feat, you'll see that ObjectInputStream uses the most recent non-null ClassLoader from the call stack, i.e., the ClassLoader that loaded the class with the utility method, when trying to locate classes by name. The StreamCorruptedException will show up if you use non-default serialization for a class and that class is not visible to the ClassLoader that loaded the class with the utility method!
Thus, if you must, a better form for the method would be:
public static Object toObject(byte[] bytes, ClassLoader cl)
throws IOException, ClassNotFoundException
{
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis) {
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException
{
String name = desc.getName();
Class c = cl.loadClass(name);
return c;
}
};
return ois.readObject();
}
This can have security implications (some folks don't want you to subclass on ObjectInputStream for obvious reasons), but it will work right under most circumstances, unlike the original. For what it's worth, it's probably better to not use a utility method and locate this operation with the caller and avoid the issue in the first place.
You just never know when Class.forName() is going to jump out and bite you...