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