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