Unit Testing Serialization Evolution, Take 2
Tom Hawtin suggested that I override
writeClassDescriptor() instead. It's a good thing because in doing so I realized overridding writeUTF() as I did in my first try doesn't intercept field types correctly. Enjoy. public static <S> S serializeAndDeserialize(Object o,
Class<S> spoofedType) throws IOException {
ByteArrayOutputStream bout =
new ByteArrayOutputStream();
ObjectOutputStream oout =
new SpoofingObjectOutputStream(bout, o.getClass(),
spoofedType);
oout.writeObject(o);
oout.flush();
oout.close();
ByteArrayInputStream bin =
new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream oin = new ObjectInputStream(bin);
try {
return spoofedType.cast(oin.readObject());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
static class SpoofingObjectOutputStream
extends ObjectOutputStream {
String oldName;
String newName;
public SpoofingObjectOutputStream(OutputStream out,
Class oldClass, Class newClass) throws IOException {
super(out);
this.oldName = oldClass.getName();
this.newName = newClass.getName();
}
@Override protected void writeClassDescriptor(
ObjectStreamClass descriptor) throws IOException {
Class clazz = descriptor.forClass();
boolean externalizable =
Externalizable.class.isAssignableFrom(clazz);
boolean serializable =
Serializable.class.isAssignableFrom(clazz);
boolean hasWriteObjectData =
hasWriteObjectMethod(clazz);
boolean isEnum = Enum.class.isAssignableFrom(clazz);
writeUTF(replace(descriptor.getName()));
writeLong(descriptor.getSerialVersionUID());
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
writeByte(flags);
ObjectStreamField[] fields = descriptor.getFields();
writeShort(fields.length);
for (ObjectStreamField field : fields) {
writeByte(field.getTypeCode());
writeUTF(field.getName());
if (!field.isPrimitive()) {
writeObject(replace(field.getTypeString()));
}
}
}
String replace(String className) {
if (className.equals(newName)) {
throw new RuntimeException(
"Found instance of " + className + "."
+ " Expected instance of " + oldName + ".");
}
return className.equals(oldName) ? newName : className;
}
boolean hasWriteObjectMethod(Class clazz) {
try {
Method method =
clazz.getDeclaredMethod("writeObject",
ObjectOutputStream.class);
int modifiers = method.getModifiers();
return method.getReturnType() == Void.TYPE
&& !Modifier.isStatic(modifiers)
&& Modifier.isPrivate(modifiers);
} catch (NoSuchMethodException e) {
return false;
}
}
}


3 Comments:
What's with the Java? Fun fun! (I'm really anti-java these days)
Shaun
ohpunk.blogspot.com
Sorry. Blogger doesn't support categories or tags. :(
Maybe that's something else FeedBurner could add--tagging.
Hi,
I've used your serialization testing toolkit for a while and it helps me a lot - thank you very much!
But for some weeks I'm going to apply Serialization Proxy pattern (as described in Josh Bloch's Effective Java book). And now your solution breaks.
After fiddling a little bit with the code I think I get a workable solution. Is there any way to post it alongside your solution?
Christian
Post a Comment
<< Home