• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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      */
setSerialVersionUID(CtClass clazz)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      */
isSerializable(CtClass clazz)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      */
calculateDefault(CtClass clazz)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 
javaName(CtClass clazz)203     private static String javaName(CtClass clazz) {
204         return Descriptor.toJavaName(Descriptor.toJvmName(clazz));
205     }
206 
javaName(String name)207     private static String javaName(String name) {
208         return Descriptor.toJavaName(Descriptor.toJvmName(name));
209     }
210 }
211