1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist; 17 18 import java.io.*; 19 import java.lang.reflect.Modifier; 20 21 import javassist.bytecode.*; 22 import java.util.*; 23 import java.security.*; 24 25 /** 26 * Utility for calculating serialVersionUIDs for Serializable classes. 27 * 28 * @author Bob Lee (crazybob (at) crazybob.org) 29 * @author modified by Shigeru Chiba 30 */ 31 public class SerialVersionUID { 32 33 /** 34 * Adds serialVersionUID if one does not already exist. Call this before 35 * modifying a class to maintain serialization compatability. 36 */ 37 public static void setSerialVersionUID(CtClass clazz) 38 throws CannotCompileException, NotFoundException 39 { 40 // check for pre-existing field. 41 try { 42 clazz.getDeclaredField("serialVersionUID"); 43 return; 44 } 45 catch (NotFoundException e) {} 46 47 // check if the class is serializable. 48 if (!isSerializable(clazz)) 49 return; 50 51 // add field with default value. 52 CtField field = new CtField(CtClass.longType, "serialVersionUID", 53 clazz); 54 field.setModifiers(Modifier.PRIVATE | Modifier.STATIC | 55 Modifier.FINAL); 56 clazz.addField(field, calculateDefault(clazz) + "L"); 57 } 58 59 /** 60 * Does the class implement Serializable? 61 */ 62 private static boolean isSerializable(CtClass clazz) 63 throws NotFoundException 64 { 65 ClassPool pool = clazz.getClassPool(); 66 return clazz.subtypeOf(pool.get("java.io.Serializable")); 67 } 68 69 /** 70 * Calculate default value. See Java Serialization Specification, Stream 71 * Unique Identifiers. 72 */ 73 static long calculateDefault(CtClass clazz) 74 throws CannotCompileException 75 { 76 try { 77 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 78 DataOutputStream out = new DataOutputStream(bout); 79 ClassFile classFile = clazz.getClassFile(); 80 81 // class name. 82 String javaName = javaName(clazz); 83 out.writeUTF(javaName); 84 85 CtMethod[] methods = clazz.getDeclaredMethods(); 86 87 // class modifiers. 88 int classMods = clazz.getModifiers(); 89 if ((classMods & Modifier.INTERFACE) != 0) 90 if (methods.length > 0) 91 classMods = classMods | Modifier.ABSTRACT; 92 else 93 classMods = classMods & ~Modifier.ABSTRACT; 94 95 out.writeInt(classMods); 96 97 // interfaces. 98 String[] interfaces = classFile.getInterfaces(); 99 for (int i = 0; i < interfaces.length; i++) 100 interfaces[i] = javaName(interfaces[i]); 101 102 Arrays.sort(interfaces); 103 for (int i = 0; i < interfaces.length; i++) 104 out.writeUTF(interfaces[i]); 105 106 // fields. 107 CtField[] fields = clazz.getDeclaredFields(); 108 Arrays.sort(fields, new Comparator() { 109 public int compare(Object o1, Object o2) { 110 CtField field1 = (CtField)o1; 111 CtField field2 = (CtField)o2; 112 return field1.getName().compareTo(field2.getName()); 113 } 114 }); 115 116 for (int i = 0; i < fields.length; i++) { 117 CtField field = (CtField) fields[i]; 118 int mods = field.getModifiers(); 119 if (((mods & Modifier.PRIVATE) == 0) || 120 ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) { 121 out.writeUTF(field.getName()); 122 out.writeInt(mods); 123 out.writeUTF(field.getFieldInfo2().getDescriptor()); 124 } 125 } 126 127 // static initializer. 128 if (classFile.getStaticInitializer() != null) { 129 out.writeUTF("<clinit>"); 130 out.writeInt(Modifier.STATIC); 131 out.writeUTF("()V"); 132 } 133 134 // constructors. 135 CtConstructor[] constructors = clazz.getDeclaredConstructors(); 136 Arrays.sort(constructors, new Comparator() { 137 public int compare(Object o1, Object o2) { 138 CtConstructor c1 = (CtConstructor)o1; 139 CtConstructor c2 = (CtConstructor)o2; 140 return c1.getMethodInfo2().getDescriptor().compareTo( 141 c2.getMethodInfo2().getDescriptor()); 142 } 143 }); 144 145 for (int i = 0; i < constructors.length; i++) { 146 CtConstructor constructor = constructors[i]; 147 int mods = constructor.getModifiers(); 148 if ((mods & Modifier.PRIVATE) == 0) { 149 out.writeUTF("<init>"); 150 out.writeInt(mods); 151 out.writeUTF(constructor.getMethodInfo2() 152 .getDescriptor().replace('/', '.')); 153 } 154 } 155 156 // methods. 157 Arrays.sort(methods, new Comparator() { 158 public int compare(Object o1, Object o2) { 159 CtMethod m1 = (CtMethod)o1; 160 CtMethod m2 = (CtMethod)o2; 161 int value = m1.getName().compareTo(m2.getName()); 162 if (value == 0) 163 value = m1.getMethodInfo2().getDescriptor() 164 .compareTo(m2.getMethodInfo2().getDescriptor()); 165 166 return value; 167 } 168 }); 169 170 for (int i = 0; i < methods.length; i++) { 171 CtMethod method = methods[i]; 172 int mods = method.getModifiers() 173 & (Modifier.PUBLIC | Modifier.PRIVATE 174 | Modifier.PROTECTED | Modifier.STATIC 175 | Modifier.FINAL | Modifier.SYNCHRONIZED 176 | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT); 177 if ((mods & Modifier.PRIVATE) == 0) { 178 out.writeUTF(method.getName()); 179 out.writeInt(mods); 180 out.writeUTF(method.getMethodInfo2() 181 .getDescriptor().replace('/', '.')); 182 } 183 } 184 185 // calculate hash. 186 out.flush(); 187 MessageDigest digest = MessageDigest.getInstance("SHA"); 188 byte[] digested = digest.digest(bout.toByteArray()); 189 long hash = 0; 190 for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--) 191 hash = (hash << 8) | (digested[i] & 0xFF); 192 193 return hash; 194 } 195 catch (IOException e) { 196 throw new CannotCompileException(e); 197 } 198 catch (NoSuchAlgorithmException e) { 199 throw new CannotCompileException(e); 200 } 201 } 202 203 private static String javaName(CtClass clazz) { 204 return Descriptor.toJavaName(Descriptor.toJvmName(clazz)); 205 } 206 207 private static String javaName(String name) { 208 return Descriptor.toJavaName(Descriptor.toJvmName(name)); 209 } 210 } 211