KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > transform > inlining > weaver > SerialVersionUidVisitor


1 /**************************************************************************************
2  * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

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

41 public class SerialVersionUidVisitor extends ClassAdapter implements Constants {
42
43     public static final String JavaDoc CLINIT = "<clinit>";
44     public static final String JavaDoc INIT = "<init>";
45     public static final String JavaDoc SVUID_NAME = "serialVersionUID";
46
47     /**
48      * flag that indicates if we need to compute SVUID (no need for interfaces)
49      */

50     protected boolean m_computeSVUID = true;
51
52     /**
53      * Set to true if the class already has SVUID
54      */

55     protected boolean m_hadSVUID = false;
56
57     /**
58      * The SVUID value (valid at the end of the visit only ie the one that was present or the computed one)
59      */

60     protected long m_SVUID;
61
62     /**
63      * Internal name of the class
64      */

65     protected String JavaDoc m_className;
66
67     /**
68      * Classes access flag
69      */

70     protected int m_access;
71
72     /**
73      * Interfaces implemented by the class
74      */

75     protected String JavaDoc[] m_interfaces;
76
77     /**
78      * Collection of fields. (except private static
79      * and private transient fields)
80      */

81     protected Collection JavaDoc m_svuidFields = new ArrayList JavaDoc();
82
83     /**
84      * Set to true if the class has static initializer
85      */

86     protected boolean m_hasStaticInitializer = false;
87
88     /**
89      * Collection of non private constructors.
90      */

91     protected Collection JavaDoc m_svuidConstructors = new ArrayList JavaDoc();
92
93     /**
94      * Collection of non private method
95      */

96     protected Collection JavaDoc m_svuidMethods = new ArrayList JavaDoc();
97
98     /**
99      * helper method (test purpose)
100      * @param klass
101      * @return
102      */

103     public static long calculateSerialVersionUID(Class JavaDoc klass) {
104         try {
105             ClassReader cr = new ClassReader(klass.getName());
106             ClassWriter cw = AsmHelper.newClassWriter(true);
107             SerialVersionUidVisitor sv = new SerialVersionUidVisitor(cw);
108             cr.accept(sv, true);
109             return sv.m_SVUID;
110         } catch (IOException JavaDoc e) {
111             throw new RuntimeException JavaDoc(e);
112         }
113     }
114
115     private SerialVersionUidVisitor(final ClassVisitor cv) {
116         super(cv);
117     }
118
119     /**
120      * Visit class header and get class name, access , and interfaces information
121      * (step 1,2, and 3) for SVUID computation.
122      */

123     public void visit(int version, int access,
124                       String JavaDoc name, String JavaDoc superName,
125                       String JavaDoc[] interfaces, String JavaDoc sourceFile) {
126         // get SVUID info. only if check passes
127
if (mayNeedSerialVersionUid(access)) {
128             m_className = name;
129             m_access = access;
130             m_interfaces = interfaces;
131         }
132
133         // delegate call to class visitor
134
super.visit(version, access, name, superName, interfaces, sourceFile);
135     }
136
137     /**
138      * Visit the methods and get constructor and method information (step
139      * 5 and 7). Also determince if there is a class initializer (step 6).
140      */

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

171     public void visitField(int access, String JavaDoc name, String JavaDoc desc,
172                            Object JavaDoc value, Attribute attrs) {
173         // get SVUID info
174
if (m_computeSVUID) {
175
176             // check SVUID
177
if (name.equals(SVUID_NAME)) {
178                 m_hadSVUID = true;
179                 // we then don't need to compute it actually
180
m_computeSVUID = false;
181                 m_SVUID = ((Long JavaDoc) value).longValue();
182             }
183
184             /*
185              * Remember field for SVUID computation later.
186              * except private static and private transient fields
187              */

188             if (((access & ACC_PRIVATE) == 0) ||
189                     ((access & (ACC_STATIC | ACC_TRANSIENT)) == 0)) {
190                 m_svuidFields.add(new FieldItem(name, access, desc));
191             }
192
193         }
194
195         // delegate call to class visitor
196
super.visitField(access, name, desc, value, attrs);
197     }
198
199     /**
200      * Add the SVUID if class doesn't have one
201      */

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

236     protected long computeSVUID() throws IOException JavaDoc, NoSuchAlgorithmException JavaDoc {
237         ByteArrayOutputStream JavaDoc bos = null;
238         DataOutputStream JavaDoc dos = null;
239         long svuid = 0;
240
241         try {
242
243             bos = new ByteArrayOutputStream JavaDoc();
244             dos = new DataOutputStream JavaDoc(bos);
245
246             /*
247               1. The class name written using UTF encoding.
248             */

249             dos.writeUTF(m_className.replace('/', '.'));
250
251             /*
252               2. The class modifiers written as a 32-bit integer.
253             */

254             int classMods = m_access & (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT);
255             dos.writeInt(classMods);
256
257             /*
258               3. The name of each interface sorted by name written using UTF encoding.
259             */

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

277             writeItems(m_svuidFields, dos, false);
278
279             /*
280               5. If a class initializer exists, write out the following:
281                 1. The name of the method, <clinit>, in UTF encoding.
282                 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
283                    written as a 32-bit integer.
284                 3. The descriptor of the method, ()V, in UTF encoding.
285             */

286             if (m_hasStaticInitializer) {
287                 dos.writeUTF("<clinit>");
288                 dos.writeInt(ACC_STATIC);
289                 dos.writeUTF("()V");
290             }
291
292             /*
293               6. For each non-private constructor sorted by method name and signature:
294                 1. The name of the method, <init>, in UTF encoding.
295                 2. The modifiers of the method written as a 32-bit integer.
296                 3. The descriptor of the method in UTF encoding.
297             */

298             writeItems(m_svuidConstructors, dos, true);
299
300             /*
301               7. For each non-private method sorted by method name and signature:
302                 1. The name of the method in UTF encoding.
303                 2. The modifiers of the method written as a 32-bit integer.
304                 3. The descriptor of the method in UTF encoding.
305             */

306             writeItems(m_svuidMethods, dos, true);
307
308             dos.flush();
309
310             /*
311               8. The SHA-1 algorithm is executed on the stream of bytes produced by
312               DataOutputStream and produces five 32-bit values sha[0..4].
313             */

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

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

354     protected void writeItems(Collection JavaDoc itemCollection,
355                               DataOutputStream JavaDoc dos,
356                               boolean dotted) throws IOException JavaDoc {
357         int size = itemCollection.size();
358         Item items[] = new Item[size];
359         items = (Item[]) itemCollection.toArray(items);
360         Arrays.sort(items);
361
362         for (int i = 0; i < size; i++) {
363             items[i].write(dos, dotted);
364         }
365     }
366
367     /**
368      * An Item represent a field / method / constructor needed in the computation
369      */

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

407     private static class FieldItem extends Item {
408         FieldItem(String JavaDoc name, int access, String JavaDoc desc) {
409             super(name, access, desc);
410         }
411
412         protected int filterAccess(int access) {
413             return access & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
414                     | ACC_VOLATILE | ACC_TRANSIENT);
415         }
416     }
417
418     /**
419      * A method / constructor item
420      */

421     private static class MethodItem extends Item {
422         MethodItem(String JavaDoc name, int access, String JavaDoc desc) {
423             super(name, access, desc);
424         }
425
426         protected int filterAccess(int access) {
427             return access & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
428                     | ACC_SYNCHRONIZED | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT);
429         }
430     }
431
432     /**
433      * Add the serial version uid to the class if not already present
434      *
435      * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
436      */

437     public static class Add extends ClassAdapter {
438
439         private ContextImpl m_ctx;
440         private ClassInfo m_classInfo;
441
442         public Add(ClassVisitor classVisitor, Context ctx, ClassInfo classInfo) {
443             super(classVisitor);
444             m_ctx = (ContextImpl) ctx;
445             m_classInfo = classInfo;
446         }
447
448         public void visitEnd() {
449             if (ClassInfoHelper.implementsInterface(m_classInfo, "java.io.Serializable")) {
450                 ClassReader cr = new ClassReader(m_ctx.getInitialBytecode());
451                 ClassWriter cw = AsmHelper.newClassWriter(true);
452                 SerialVersionUidVisitor sv = new SerialVersionUidVisitor(cw);
453                 cr.accept(sv, true);
454                 if (sv.m_computeSVUID && !sv.m_hadSVUID) {
455                     cv.visitField(ACC_FINAL + ACC_STATIC, SVUID_NAME, "J", new Long JavaDoc(sv.m_SVUID), null);
456                 }
457             }
458             super.visitEnd();
459         }
460     }
461 }
462
Popular Tags