KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > aspectwerkz > transform > inlining > weaver > SerialVersionUidVisitor


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.aspectwerkz.transform.inlining.weaver;
5
6 import com.tc.asm.*;
7
8 import com.tc.aspectwerkz.transform.InstrumentationContext;
9 import com.tc.aspectwerkz.transform.inlining.AsmHelper;
10 import com.tc.aspectwerkz.reflect.ClassInfo;
11 import com.tc.aspectwerkz.reflect.ClassInfoHelper;
12
13 import java.io.IOException JavaDoc;
14 import java.io.DataOutputStream JavaDoc;
15 import java.io.ByteArrayOutputStream JavaDoc;
16 import java.util.Collection JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Arrays JavaDoc;
19 import java.security.NoSuchAlgorithmException JavaDoc;
20 import java.security.MessageDigest JavaDoc;
21
22 /**
23  * See http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/class.html#60
24  * <p/>
25  * The SerialVersionUidVisitor lookups for the serial ver uid and compute it when not found.
26  * See Add and Compute subclasses.
27  * <p/>
28  * Initial implementation courtesy of Vishal Vishnoi <vvishnoi AT bea DOT com>
29  *
30  * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
31  */

32 public class SerialVersionUidVisitor extends ClassAdapter implements Opcodes {
33
34   public static final String JavaDoc CLINIT = "<clinit>";
35   public static final String JavaDoc INIT = "<init>";
36   public static final String JavaDoc SVUID_NAME = "serialVersionUID";
37
38   /**
39    * flag that indicates if we need to compute SVUID (no need for interfaces)
40    */

41   protected boolean m_computeSVUID = true;
42
43   /**
44    * Set to true if the class already has SVUID
45    */

46   protected boolean m_hadSVUID = false;
47
48   /**
49    * The SVUID value (valid at the end of the visit only ie the one that was present or the computed one)
50    */

51   protected long m_SVUID;
52
53   /**
54    * Internal name of the class
55    */

56   protected String JavaDoc m_className;
57
58   /**
59    * Classes access flag
60    */

61   protected int m_access;
62
63   /**
64    * Interfaces implemented by the class
65    */

66   protected String JavaDoc[] m_interfaces;
67
68   /**
69    * Collection of fields. (except private static
70    * and private transient fields)
71    */

72   protected Collection JavaDoc m_svuidFields = new ArrayList JavaDoc();
73
74   /**
75    * Set to true if the class has static initializer
76    */

77   protected boolean m_hasStaticInitializer = false;
78
79   /**
80    * Collection of non private constructors.
81    */

82   protected Collection JavaDoc m_svuidConstructors = new ArrayList JavaDoc();
83
84   /**
85    * Collection of non private method
86    */

87   protected Collection JavaDoc m_svuidMethods = new ArrayList JavaDoc();
88
89   /**
90    * helper method (test purpose)
91    *
92    * @param klass
93    * @return
94    */

95   public static long calculateSerialVersionUID(Class JavaDoc klass) {
96     try {
97       ClassReader cr = new ClassReader(klass.getName());
98       ClassWriter cw = AsmHelper.newClassWriter(true);
99       SerialVersionUidVisitor sv = new SerialVersionUidVisitor(cw);
100       cr.accept(sv, true);
101       return sv.m_SVUID;
102     } catch (IOException JavaDoc e) {
103       throw new RuntimeException JavaDoc(e);
104     }
105   }
106
107   private SerialVersionUidVisitor(final ClassVisitor cv) {
108     super(cv);
109   }
110
111   /**
112    * Visit class header and getDefault class name, access , and interfaces information
113    * (step 1,2, and 3) for SVUID computation.
114    */

115   public void visit(int version, int access,
116                     String JavaDoc name, String JavaDoc signature, String JavaDoc superName,
117                     String JavaDoc[] interfaces) {
118     // getDefault SVUID info. only if check passes
119
if (mayNeedSerialVersionUid(access)) {
120       m_className = name;
121       m_access = access;
122       m_interfaces = interfaces;
123     }
124
125     // delegate call to class visitor
126
super.visit(version, access, name, signature, superName, interfaces);
127   }
128
129   /**
130    * Visit the methods and getDefault constructor and method information (step
131    * 5 and 7). Also determince if there is a class initializer (step 6).
132    */

133   public MethodVisitor visitMethod(int access,
134                                    String JavaDoc name, String JavaDoc desc, String JavaDoc signature,
135                                    String JavaDoc[] exceptions) {
136     // getDefault SVUI info
137
if (m_computeSVUID) {
138
139       // class initialized
140
if (name.equals(CLINIT)) {
141         m_hasStaticInitializer = true;
142       } else {
143         // Remember non private constructors and methods for SVUID computation later.
144
if ((access & ACC_PRIVATE) == 0) {
145           if (name.equals(INIT)) {
146             m_svuidConstructors.add(new MethodItem(name, access, desc));
147           } else {
148             m_svuidMethods.add(new MethodItem(name, access, desc));
149           }
150         }
151       }
152
153     }
154
155     // delegate call to class visitor
156
return cv.visitMethod(access, name, desc, signature, exceptions);
157   }
158
159   /**
160    * Gets class field information for step 4 of the alogrithm. Also determines
161    * if the class already has a SVUID.
162    */

163   public FieldVisitor visitField(int access, String JavaDoc name, String JavaDoc desc, String JavaDoc signature, Object JavaDoc value) {
164     // getDefault SVUID info
165
if (m_computeSVUID) {
166
167       // check SVUID
168
if (name.equals(SVUID_NAME)) {
169         m_hadSVUID = true;
170         // we then don't need to compute it actually
171
m_computeSVUID = false;
172         m_SVUID = ((Long JavaDoc) value).longValue();
173       }
174
175       /*
176       * Remember field for SVUID computation later.
177       * except private static and private transient fields
178       */

179       if (((access & ACC_PRIVATE) == 0) ||
180               ((access & (ACC_STATIC | ACC_TRANSIENT)) == 0)) {
181         m_svuidFields.add(new FieldItem(name, access, desc));
182       }
183
184     }
185
186     // delegate call to class visitor
187
return super.visitField(access, name, desc, signature, value);
188   }
189
190   /**
191    * Add the SVUID if class doesn't have one
192    */

193   public void visitEnd() {
194     if (m_computeSVUID) {
195       // compute SVUID if the class doesn't have one
196
if (!m_hadSVUID) {
197         try {
198           m_SVUID = computeSVUID();
199         } catch (Throwable JavaDoc e) {
200           throw new RuntimeException JavaDoc("Error while computing SVUID for " + m_className, e);
201         }
202       }
203     }
204
205     // delegate call to class visitor
206
super.visitEnd();
207   }
208
209   protected boolean mayNeedSerialVersionUid(int access) {
210     return true;
211     // we don't need to compute SVUID for interfaces //TODO why ???
212
// if ((access & ACC_INTERFACE) == ACC_INTERFACE) {
213
// m_computeSVUID = false;
214
// } else {
215
// m_computeSVUID = true;
216
// }
217
// return m_computeSVUID;
218
}
219
220   /**
221    * Returns the value of SVUID if the class doesn't have one already. Please
222    * note that 0 is returned if the class already has SVUID, thus use
223    * <code>isHasSVUID</code> to determine if the class already had an SVUID.
224    *
225    * @return Returns the serila version UID
226    */

227   protected long computeSVUID() throws IOException JavaDoc, NoSuchAlgorithmException JavaDoc {
228     ByteArrayOutputStream JavaDoc bos = null;
229     DataOutputStream JavaDoc dos = null;
230     long svuid = 0;
231
232     try {
233
234       bos = new ByteArrayOutputStream JavaDoc();
235       dos = new DataOutputStream JavaDoc(bos);
236
237       /*
238         1. The class name written using UTF encoding.
239       */

240       dos.writeUTF(m_className.replace('/', '.'));
241
242       /*
243         2. The class modifiers written as a 32-bit integer.
244       */

245       int classMods = m_access & (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT);
246       dos.writeInt(classMods);
247
248       /*
249         3. The name of each interface sorted by name written using UTF encoding.
250       */

251       Arrays.sort(m_interfaces);
252       for (int i = 0; i < m_interfaces.length; i++) {
253         String JavaDoc ifs = m_interfaces[i].replace('/', '.');
254         dos.writeUTF(ifs);
255       }
256
257       /*
258         4. For each field of the class sorted by field name (except private
259         static and private transient fields):
260
261           1. The name of the field in UTF encoding.
262           2. The modifiers of the field written as a 32-bit integer.
263           3. The descriptor of the field in UTF encoding
264
265         Note that field signatutes are not dot separated. Method and
266         constructor signatures are dot separated. Go figure...
267       */

268       writeItems(m_svuidFields, dos, false);
269
270       /*
271         5. If a class initializer exists, write out the following:
272           1. The name of the method, <clinit>, in UTF encoding.
273           2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
274              written as a 32-bit integer.
275           3. The descriptor of the method, ()V, in UTF encoding.
276       */

277       if (m_hasStaticInitializer) {
278         dos.writeUTF("<clinit>");
279         dos.writeInt(ACC_STATIC);
280         dos.writeUTF("()V");
281       }
282
283       /*
284         6. For each non-private constructor sorted by method name and signature:
285           1. The name of the method, <init>, in UTF encoding.
286           2. The modifiers of the method written as a 32-bit integer.
287           3. The descriptor of the method in UTF encoding.
288       */

289       writeItems(m_svuidConstructors, dos, true);
290
291       /*
292         7. For each non-private method sorted by method name and signature:
293           1. The name of the method in UTF encoding.
294           2. The modifiers of the method written as a 32-bit integer.
295           3. The descriptor of the method in UTF encoding.
296       */

297       writeItems(m_svuidMethods, dos, true);
298
299       dos.flush();
300
301       /*
302         8. The SHA-1 algorithm is executed on the stream of bytes produced by
303         DataOutputStream and produces five 32-bit values sha[0..4].
304       */

305       MessageDigest JavaDoc md = MessageDigest.getInstance("SHA");
306
307       /*
308         9. The hash value is assembled from the first and second 32-bit values
309         of the SHA-1 message digest. If the result of the message digest, the
310         five 32-bit words H0 H1 H2 H3 H4, is in an array of five int values
311         named sha, the hash value would be computed as follows:
312
313         long hash = ((sha[0] >>> 24) & 0xFF) |
314         ((sha[0] >>> 16) & 0xFF) << 8 |
315         ((sha[0] >>> 8) & 0xFF) << 16 |
316         ((sha[0] >>> 0) & 0xFF) << 24 |
317         ((sha[1] >>> 24) & 0xFF) << 32 |
318         ((sha[1] >>> 16) & 0xFF) << 40 |
319         ((sha[1] >>> 8) & 0xFF) << 48 |
320         ((sha[1] >>> 0) & 0xFF) << 56;
321       */

322       byte[] hashBytes = md.digest(bos.toByteArray());
323       for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
324         svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
325       }
326
327     } finally {
328       // close the stream (if open)
329
if (dos != null) {
330         dos.close();
331       }
332     }
333
334     return svuid;
335   }
336
337   /**
338    * Sorts the items in the collection and writes it to the data output stream
339    *
340    * @param itemCollection collection of items
341    * @param dos a <code>DataOutputStream</code> value
342    * @param dotted a <code>boolean</code> value
343    * @throws IOException if an error occurs
344    */

345   protected void writeItems(Collection JavaDoc itemCollection,
346                             DataOutputStream JavaDoc dos,
347                             boolean dotted) throws IOException JavaDoc {
348     int size = itemCollection.size();
349     Item items[] = new Item[size];
350     items = (Item[]) itemCollection.toArray(items);
351     Arrays.sort(items);
352
353     for (int i = 0; i < size; i++) {
354       items[i].write(dos, dotted);
355     }
356   }
357
358   /**
359    * An Item represent a field / method / constructor needed in the computation
360    */

361   private static abstract class Item implements Comparable JavaDoc {
362     private String JavaDoc m_name;
363     private int m_access;
364     private String JavaDoc m_desc;
365
366     Item(String JavaDoc name, int access, String JavaDoc desc) {
367       m_name = name;
368       m_access = access;
369       m_desc = desc;
370     }
371
372     // see spec, modifiers must be filtered
373
protected abstract int filterAccess(int access);
374
375     public int compareTo(Object JavaDoc o) {
376       Item other = (Item) o;
377       int retVal = m_name.compareTo(other.m_name);
378       if (retVal == 0) {
379         retVal = m_desc.compareTo(other.m_desc);
380       }
381       return retVal;
382     }
383
384     void write(DataOutputStream JavaDoc dos, boolean dotted) throws IOException JavaDoc {
385       dos.writeUTF(m_name);
386       dos.writeInt(filterAccess(m_access));
387       if (dotted) {
388         dos.writeUTF(m_desc.replace('/', '.'));
389       } else {
390         dos.writeUTF(m_desc);
391       }
392     }
393   }
394
395   /**
396    * A field item
397    */

398   private static class FieldItem extends Item {
399     FieldItem(String JavaDoc name, int access, String JavaDoc desc) {
400       super(name, access, desc);
401     }
402
403     protected int filterAccess(int access) {
404       return access & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
405               | ACC_VOLATILE | ACC_TRANSIENT);
406     }
407   }
408
409   /**
410    * A method / constructor item
411    */

412   private static class MethodItem extends Item {
413     MethodItem(String JavaDoc name, int access, String JavaDoc desc) {
414       super(name, access, desc);
415     }
416
417     protected int filterAccess(int access) {
418       return access & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
419               | ACC_SYNCHRONIZED | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT);
420     }
421   }
422
423   /**
424    * Add the serial version uid to the class if not already present
425    *
426    * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
427    */

428   public static class Add extends ClassAdapter {
429
430     private InstrumentationContext m_ctx;
431     private ClassInfo m_classInfo;
432
433     public Add(ClassVisitor classVisitor, InstrumentationContext ctx, ClassInfo classInfo) {
434       super(classVisitor);
435       m_ctx = (InstrumentationContext) ctx;
436       m_classInfo = classInfo;
437     }
438
439     public void visitEnd() {
440       if (ClassInfoHelper.implementsInterface(m_classInfo, "java.io.Serializable")) {
441         ClassReader cr = new ClassReader(m_ctx.getInitialBytecode());
442         ClassWriter cw = AsmHelper.newClassWriter(true);
443         SerialVersionUidVisitor sv = new SerialVersionUidVisitor(cw);
444         cr.accept(sv, true);
445         if (sv.m_computeSVUID && !sv.m_hadSVUID) {
446           cv.visitField(ACC_FINAL + ACC_STATIC, SVUID_NAME, "J", null, new Long JavaDoc(sv.m_SVUID));
447         }
448       }
449       super.visitEnd();
450     }
451   }
452 }
453
Popular Tags