• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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