KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > asm > commons > SerialVersionUIDAdder


1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2005 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */

30 package org.objectweb.asm.commons;
31
32 import java.io.ByteArrayOutputStream JavaDoc;
33 import java.io.DataOutputStream JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.security.MessageDigest JavaDoc;
36 import java.security.NoSuchAlgorithmException JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Arrays JavaDoc;
39 import java.util.Collection JavaDoc;
40
41 import org.objectweb.asm.ClassAdapter;
42 import org.objectweb.asm.ClassVisitor;
43 import org.objectweb.asm.FieldVisitor;
44 import org.objectweb.asm.MethodVisitor;
45 import org.objectweb.asm.Opcodes;
46
47 /**
48  * A {@link ClassAdapter} that adds a serial version unique identifier to a
49  * class if missing. The SVUID algorithm can be found <a HREF=
50  * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
51  * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
52  *
53  * <pre>
54  * The serialVersionUID is computed using the signature of a stream of bytes
55  * that reflect the class definition. The National Institute of Standards and
56  * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
57  * signature for the stream. The first two 32-bit quantities are used to form a
58  * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
59  * types to a sequence of bytes. The values input to the stream are defined by
60  * the Java Virtual Machine (VM) specification for classes.
61  *
62  * The sequence of items in the stream is as follows:
63  *
64  * 1. The class name written using UTF encoding.
65  * 2. The class modifiers written as a 32-bit integer.
66  * 3. The name of each interface sorted by name written using UTF encoding.
67  * 4. For each field of the class sorted by field name (except private static
68  * and private transient fields):
69  * 1. The name of the field in UTF encoding.
70  * 2. The modifiers of the field written as a 32-bit integer.
71  * 3. The descriptor of the field in UTF encoding
72  * 5. If a class initializer exists, write out the following:
73  * 1. The name of the method, &lt;clinit&gt;, in UTF encoding.
74  * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
75  * written as a 32-bit integer.
76  * 3. The descriptor of the method, ()V, in UTF encoding.
77  * 6. For each non-private constructor sorted by method name and signature:
78  * 1. The name of the method, &lt;init&gt;, in UTF encoding.
79  * 2. The modifiers of the method written as a 32-bit integer.
80  * 3. The descriptor of the method in UTF encoding.
81  * 7. For each non-private method sorted by method name and signature:
82  * 1. The name of the method in UTF encoding.
83  * 2. The modifiers of the method written as a 32-bit integer.
84  * 3. The descriptor of the method in UTF encoding.
85  * 8. The SHA-1 algorithm is executed on the stream of bytes produced by
86  * DataOutputStream and produces five 32-bit values sha[0..4].
87  *
88  * 9. The hash value is assembled from the first and second 32-bit values of
89  * the SHA-1 message digest. If the result of the message digest, the five
90  * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
91  * sha, the hash value would be computed as follows:
92  *
93  * long hash = ((sha[0] &gt;&gt;&gt; 24) &amp; 0xFF) |
94  * ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
95  * ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
96  * ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
97  * ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
98  * ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
99  * ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
100  * ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
101  * </pre>
102  *
103  * @author Rajendra Inamdar, Vishal Vishnoi
104  */

105 public class SerialVersionUIDAdder extends ClassAdapter {
106
107     /**
108      * Flag that indicates if we need to compute SVUID.
109      */

110     protected boolean computeSVUID;
111
112     /**
113      * Set to true if the class already has SVUID.
114      */

115     protected boolean hasSVUID;
116
117     /**
118      * Classes access flags.
119      */

120     protected int access;
121
122     /**
123      * Internal name of the class
124      */

125     protected String JavaDoc name;
126
127     /**
128      * Interfaces implemented by the class.
129      */

130     protected String JavaDoc[] interfaces;
131
132     /**
133      * Collection of fields. (except private static and private transient
134      * fields)
135      */

136     protected Collection JavaDoc svuidFields;
137
138     /**
139      * Set to true if the class has static initializer.
140      */

141     protected boolean hasStaticInitializer;
142
143     /**
144      * Collection of non-private constructors.
145      */

146     protected Collection JavaDoc svuidConstructors;
147
148     /**
149      * Collection of non-private methods.
150      */

151     protected Collection JavaDoc svuidMethods;
152
153     /**
154      * Creates a new {@link SerialVersionUIDAdder}.
155      *
156      * @param cv a {@link ClassVisitor} to which this visitor will delegate
157      * calls.
158      */

159     public SerialVersionUIDAdder(final ClassVisitor cv) {
160         super(cv);
161         svuidFields = new ArrayList JavaDoc();
162         svuidConstructors = new ArrayList JavaDoc();
163         svuidMethods = new ArrayList JavaDoc();
164     }
165
166     // ------------------------------------------------------------------------
167
// Overriden methods
168
// ------------------------------------------------------------------------
169

170     /*
171      * Visit class header and get class name, access , and intefraces
172      * informatoin (step 1,2, and 3) for SVUID computation.
173      */

174     public void visit(
175         final int version,
176         final int access,
177         final String JavaDoc name,
178         final String JavaDoc signature,
179         final String JavaDoc superName,
180         final String JavaDoc[] interfaces)
181     {
182         computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
183
184         if (computeSVUID) {
185             this.name = name;
186             this.access = access;
187             this.interfaces = interfaces;
188         }
189
190         super.visit(version, access, name, signature, superName, interfaces);
191     }
192
193     /*
194      * Visit the methods and get constructor and method information (step 5 and
195      * 7). Also determince if there is a class initializer (step 6).
196      */

197     public MethodVisitor visitMethod(
198         final int access,
199         final String JavaDoc name,
200         final String JavaDoc desc,
201         final String JavaDoc signature,
202         final String JavaDoc[] exceptions)
203     {
204         if (computeSVUID) {
205             if (name.equals("<clinit>")) {
206                 hasStaticInitializer = true;
207             }
208             /*
209              * Remembers non private constructors and methods for SVUID
210              * computation For constructor and method modifiers, only the
211              * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
212              * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
213              * are used.
214              */

215             int mods = access
216                     & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
217                             | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
218                             | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
219                             | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
220
221             // all non private methods
222
if ((access & Opcodes.ACC_PRIVATE) == 0) {
223                 if (name.equals("<init>")) {
224                     svuidConstructors.add(new Item(name, mods, desc));
225                 } else if (!name.equals("<clinit>")) {
226                     svuidMethods.add(new Item(name, mods, desc));
227                 }
228             }
229         }
230
231         return cv.visitMethod(access, name, desc, signature, exceptions);
232     }
233
234     /*
235      * Gets class field information for step 4 of the alogrithm. Also determines
236      * if the class already has a SVUID.
237      */

238     public FieldVisitor visitField(
239         final int access,
240         final String JavaDoc name,
241         final String JavaDoc desc,
242         final String JavaDoc signature,
243         final Object JavaDoc value)
244     {
245         if (computeSVUID) {
246             if (name.equals("serialVersionUID")) {
247                 // since the class already has SVUID, we won't be computing it.
248
computeSVUID = false;
249                 hasSVUID = true;
250             }
251             /*
252              * Remember field for SVUID computation For field modifiers, only
253              * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
254              * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
255              * computing serialVersionUID values.
256              */

257             int mods = access
258                     & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
259                             | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
260                             | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
261
262             if (((access & Opcodes.ACC_PRIVATE) == 0)
263                     || ((access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0))
264             {
265                 svuidFields.add(new Item(name, mods, desc));
266             }
267         }
268
269         return super.visitField(access, name, desc, signature, value);
270     }
271
272     /*
273      * Add the SVUID if class doesn't have one
274      */

275     public void visitEnd() {
276         // compute SVUID and add it to the class
277
if (computeSVUID && !hasSVUID) {
278             try {
279                 cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
280                         "serialVersionUID",
281                         "J",
282                         null,
283                         new Long JavaDoc(computeSVUID()));
284             } catch (Throwable JavaDoc e) {
285                 throw new RuntimeException JavaDoc("Error while computing SVUID for "
286                         + name, e);
287             }
288         }
289
290         super.visitEnd();
291     }
292
293     // ------------------------------------------------------------------------
294
// Utility methods
295
// ------------------------------------------------------------------------
296

297     /**
298      * Returns the value of SVUID if the class doesn't have one already. Please
299      * note that 0 is returned if the class already has SVUID, thus use
300      * <code>isHasSVUID</code> to determine if the class already had an SVUID.
301      *
302      * @return Returns the serial version UID
303      * @throws IOException
304      * @throws NoSuchAlgorithmException
305      */

306     protected long computeSVUID() throws IOException JavaDoc, NoSuchAlgorithmException JavaDoc {
307         if (hasSVUID) {
308             return 0;
309         }
310
311         ByteArrayOutputStream JavaDoc bos = null;
312         DataOutputStream JavaDoc dos = null;
313         long svuid = 0;
314
315         try {
316             bos = new ByteArrayOutputStream JavaDoc();
317             dos = new DataOutputStream JavaDoc(bos);
318
319             /*
320              * 1. The class name written using UTF encoding.
321              */

322             dos.writeUTF(name.replace('/', '.'));
323
324             /*
325              * 2. The class modifiers written as a 32-bit integer.
326              */

327             dos.writeInt(access
328                     & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
329                             | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
330
331             /*
332              * 3. The name of each interface sorted by name written using UTF
333              * encoding.
334              */

335             Arrays.sort(interfaces);
336             for (int i = 0; i < interfaces.length; i++) {
337                 dos.writeUTF(interfaces[i].replace('/', '.'));
338             }
339
340             /*
341              * 4. For each field of the class sorted by field name (except
342              * private static and private transient fields):
343              *
344              * 1. The name of the field in UTF encoding. 2. The modifiers of the
345              * field written as a 32-bit integer. 3. The descriptor of the field
346              * in UTF encoding
347              *
348              * Note that field signatutes are not dot separated. Method and
349              * constructor signatures are dot separated. Go figure...
350              */

351             writeItems(svuidFields, dos, false);
352
353             /*
354              * 5. If a class initializer exists, write out the following: 1. The
355              * name of the method, <clinit>, in UTF encoding. 2. The modifier of
356              * the method, java.lang.reflect.Modifier.STATIC, written as a
357              * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
358              * encoding.
359              */

360             if (hasStaticInitializer) {
361                 dos.writeUTF("<clinit>");
362                 dos.writeInt(Opcodes.ACC_STATIC);
363                 dos.writeUTF("()V");
364             } // if..
365

366             /*
367              * 6. For each non-private constructor sorted by method name and
368              * signature: 1. The name of the method, <init>, in UTF encoding. 2.
369              * The modifiers of the method written as a 32-bit integer. 3. The
370              * descriptor of the method in UTF encoding.
371              */

372             writeItems(svuidConstructors, dos, true);
373
374             /*
375              * 7. For each non-private method sorted by method name and
376              * signature: 1. The name of the method in UTF encoding. 2. The
377              * modifiers of the method written as a 32-bit integer. 3. The
378              * descriptor of the method in UTF encoding.
379              */

380             writeItems(svuidMethods, dos, true);
381
382             dos.flush();
383
384             /*
385              * 8. The SHA-1 algorithm is executed on the stream of bytes
386              * produced by DataOutputStream and produces five 32-bit values
387              * sha[0..4].
388              */

389             MessageDigest JavaDoc md = MessageDigest.getInstance("SHA");
390
391             /*
392              * 9. The hash value is assembled from the first and second 32-bit
393              * values of the SHA-1 message digest. If the result of the message
394              * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
395              * five int values named sha, the hash value would be computed as
396              * follows:
397              *
398              * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) <<
399              * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
400              * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
401              * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
402              * 56;
403              */

404             byte[] hashBytes = md.digest(bos.toByteArray());
405             for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
406                 svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
407             }
408         } finally {
409             // close the stream (if open)
410
if (dos != null) {
411                 dos.close();
412             }
413         }
414
415         return svuid;
416     }
417
418     /**
419      * Sorts the items in the collection and writes it to the data output stream
420      *
421      * @param itemCollection collection of items
422      * @param dos a <code>DataOutputStream</code> value
423      * @param dotted a <code>boolean</code> value
424      * @exception IOException if an error occurs
425      */

426     private void writeItems(
427         final Collection JavaDoc itemCollection,
428         final DataOutputStream JavaDoc dos,
429         final boolean dotted) throws IOException JavaDoc
430     {
431         int size = itemCollection.size();
432         Item items[] = (Item[]) itemCollection.toArray(new Item[size]);
433         Arrays.sort(items);
434         for (int i = 0; i < size; i++) {
435             dos.writeUTF(items[i].name);
436             dos.writeInt(items[i].access);
437             dos.writeUTF(dotted
438                     ? items[i].desc.replace('/', '.')
439                     : items[i].desc);
440         }
441     }
442
443     // ------------------------------------------------------------------------
444
// Inner classes
445
// ------------------------------------------------------------------------
446

447     static class Item implements Comparable JavaDoc {
448
449         String JavaDoc name;
450
451         int access;
452
453         String JavaDoc desc;
454
455         Item(final String JavaDoc name, final int access, final String JavaDoc desc) {
456             this.name = name;
457             this.access = access;
458             this.desc = desc;
459         }
460
461         public int compareTo(final Object JavaDoc o) {
462             Item other = (Item) o;
463             int retVal = name.compareTo(other.name);
464             if (retVal == 0) {
465                 retVal = desc.compareTo(other.desc);
466             }
467             return retVal;
468         }
469     }
470 }
471
Popular Tags