serialVersionUID issue
spampete Dec 4, 2008 4:23 AMHi all,
I have released a small memory leak detector on http://www.qarks.com web site, which is using javassist. Despite using the serialVersionUID helper, recent tests showed a remaining issue with some serializable classes during unmarshalling, with InvalidClassException due to inconsistent serialVersionUID.
I've had a look to Sun's ObjectStreamClass implementation (JDK 1.6) and found indeed some differences explaining the situation. Actually, I do not know if the issue is on Sun's side, not respecting its own spec, or not, but the fix I brough does work on my test bench.
I'd be interested in having your feedback, all of you guys !!
Meanwhile, here is the method I changed within javassist serialVersionUID helper, which I will also send to Shigeru Chiba by email:
static long calculateDefault(CtClass clazz) throws CannotCompileException {
ByteArrayOutputStream bout;
try {
bout = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bout);
ClassFile classFile = clazz.getClassFile();
String javaName = javaName(clazz);
out.writeUTF(javaName);
CtMethod[] methods = clazz.getDeclaredMethods();
int classMods = clazz.getModifiers();
if ((classMods & Modifier.INTERFACE) != 0) {
classMods = (methods.length > 0) ?
(classMods | Modifier.ABSTRACT) :
(classMods & ~Modifier.ABSTRACT);
}
out.writeInt(classMods);
if (!clazz.isArray()){
String[] interfaces = classFile.getInterfaces();
for (int i = 0; i < interfaces.length; ++i)
interfaces = javaName(interfaces);
Arrays.sort(interfaces);
for (int i = 0; i < interfaces.length; ++i) {
out.writeUTF(interfaces);
}
}
CtField[] fields = clazz.getDeclaredFields();
Arrays.sort(fields, new Comparator() {
public int compare(Object o1, Object o2) {
CtField field1 = (CtField) o1;
CtField field2 = (CtField) o2;
return field1.getName().compareTo(field2.getName());
}
});
for (int i = 0; i < fields.length; ++i) {
CtField field = fields;
int mods = field.getModifiers();
if (((mods & 0x2) == 0) || ((mods & 0x88) == 0)) {
out.writeUTF(field.getName());
out.writeInt(mods);
out.writeUTF(field.getFieldInfo2().getDescriptor());
}
}
if (classFile.getStaticInitializer() != null) {
out.writeUTF("<clinit>");
out.writeInt(8);
out.writeUTF("()V");
}
CtConstructor[] constructors = clazz.getDeclaredConstructors();
Arrays.sort(constructors, new Comparator() {
public int compare(Object o1, Object o2) {
CtConstructor c1 = (CtConstructor) o1;
CtConstructor c2 = (CtConstructor) o2;
return c1.getMethodInfo2().getDescriptor().compareTo(
c2.getMethodInfo2().getDescriptor());
}
});
for (int i = 0; i < constructors.length; ++i) {
CtConstructor constructor = constructors;
int mods = constructor.getModifiers();
if ((mods & 0x2) == 0) {
out.writeUTF("<init>");
out.writeInt(mods);
out.writeUTF(constructor.getMethodInfo2().getDescriptor()
.replace('/', '.'));
}
}
//CtMethod[] methods = clazz.getDeclaredMethods();
Arrays.sort(methods, new Comparator() {
public int compare(Object o1, Object o2) {
CtMethod m1 = (CtMethod) o1;
CtMethod m2 = (CtMethod) o2;
int value = m1.getName().compareTo(m2.getName());
if (value == 0) {
value = m1.getMethodInfo2().getDescriptor().compareTo(
m2.getMethodInfo2().getDescriptor());
}
return value;
}
});
for (int i = 0; i < methods.length; ++i) {
CtMethod method = methods;
int mods = method.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE |
Modifier.ABSTRACT | Modifier.STRICT);
if ((mods & Modifier.PRIVATE) == 0) {
out.writeUTF(method.getName());
out.writeInt(mods);
out.writeUTF(method.getMethodInfo2().getDescriptor()
.replace('/', '.'));
}
}
out.flush();
MessageDigest digest = MessageDigest.getInstance("SHA");
byte[] digested = digest.digest(bout.toByteArray());
long hash = 0L;
for (int i = Math.min(digested.length, 8) - 1; i >= 0; --i)
hash = hash << 8 | digested & 0xFF;
return hash;
} catch (IOException e) {
throw new CannotCompileException(e);
} catch (NoSuchAlgorithmException e) {
throw new CannotCompileException(e);
}
}
kind regards,
Pierre