KickJava   Java API By Example, From Geeks To Geeks.

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

114 public class SerialVersionUIDAdder extends ClassAdapter {
115
116     /**
117      * Flag that indicates if we need to compute SVUID.
118      */

119     protected boolean computeSVUID;
120
121     /**
122      * Set to true if the class already has SVUID.
123      */

124     protected boolean hasSVUID;
125
126     /**
127      * Classes access flags.
128      */

129     protected int access;
130
131     /**
132      * Internal name of the class
133      */

134     protected String JavaDoc name;
135
136     /**
137      * Interfaces implemented by the class.
138      */

139     protected String JavaDoc[] interfaces;
140
141     /**
142      * Collection of fields. (except private static and private transient
143      * fields)
144      */

145     protected Collection JavaDoc svuidFields;
146
147     /**
148      * Set to true if the class has static initializer.
149      */

150     protected boolean hasStaticInitializer;
151
152     /**
153      * Collection of non-private constructors.
154      */

155     protected Collection JavaDoc svuidConstructors;
156
157     /**
158      * Collection of non-private methods.
159      */

160     protected Collection JavaDoc svuidMethods;
161
162     /**
163      * Creates a new {@link SerialVersionUIDAdder}.
164      *
165      * @param cv a {@link ClassVisitor} to which this visitor will delegate
166      * calls.
167      */

168     public SerialVersionUIDAdder(final ClassVisitor cv) {
169         super(cv);
170         svuidFields = new ArrayList JavaDoc();
171         svuidConstructors = new ArrayList JavaDoc();
172         svuidMethods = new ArrayList JavaDoc();
173     }
174
175     // ------------------------------------------------------------------------
176
// Overriden methods
177
// ------------------------------------------------------------------------
178

179     /*
180      * Visit class header and get class name, access , and intefraces
181      * informatoin (step 1,2, and 3) for SVUID computation.
182      */

183     public void visit(
184         final int version,
185         final int access,
186         final String JavaDoc name,
187         final String JavaDoc signature,
188         final String JavaDoc superName,
189         final String JavaDoc[] interfaces)
190     {
191         computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
192
193         if (computeSVUID) {
194             this.name = name;
195             this.access = access;
196             this.interfaces = interfaces;
197         }
198
199         super.visit(version, access, name, signature, superName, interfaces);
200     }
201
202     /*
203      * Visit the methods and get constructor and method information (step 5 and
204      * 7). Also determince if there is a class initializer (step 6).
205      */

206     public MethodVisitor visitMethod(
207         final int access,
208         final String JavaDoc name,
209         final String JavaDoc desc,
210         final String JavaDoc signature,
211         final String JavaDoc[] exceptions)
212     {
213         if (computeSVUID) {
214             if (name.equals("<clinit>")) {
215                 hasStaticInitializer = true;
216             }
217             /*
218              * Remembers non private constructors and methods for SVUID
219              * computation For constructor and method modifiers, only the
220              * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
221              * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
222              * are used.
223              */

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

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

266             int mods = access
267                     & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
268                             | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
269                             | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
270
271             if (((access & Opcodes.ACC_PRIVATE) == 0)
272                     || ((access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0))
273             {
274                 svuidFields.add(new Item(name, mods, desc));
275             }
276         }
277
278         return super.visitField(access, name, desc, signature, value);
279     }
280
281     /*
282      * Add the SVUID if class doesn't have one
283      */

284     public void visitEnd() {
285         // compute SVUID and add it to the class
286
if (computeSVUID && !hasSVUID) {
287             try {
288                 cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
289                         "serialVersionUID",
290                         "J",
291                         null,
292                         new Long JavaDoc(computeSVUID()));
293             } catch (Throwable JavaDoc e) {
294                 throw new RuntimeException JavaDoc("Error while computing SVUID for "
295                         + name, e);
296             }
297         }
298
299         super.visitEnd();
300     }
301
302     // ------------------------------------------------------------------------
303
// Utility methods
304
// ------------------------------------------------------------------------
305

306     /**
307      * Returns the value of SVUID if the class doesn't have one already. Please
308      * note that 0 is returned if the class already has SVUID, thus use
309      * <code>isHasSVUID</code> to determine if the class already had an SVUID.
310      *
311      * @return Returns the serial version UID
312      * @throws IOException
313      * @throws NoSuchAlgorithmException
314      */

315     protected long computeSVUID() throws IOException JavaDoc, NoSuchAlgorithmException JavaDoc {
316         if (hasSVUID) {
317             return 0;
318         }
319
320         ByteArrayOutputStream JavaDoc bos = null;
321         DataOutputStream JavaDoc dos = null;
322         long svuid = 0;
323
324         try {
325             bos = new ByteArrayOutputStream JavaDoc();
326             dos = new DataOutputStream JavaDoc(bos);
327
328             /*
329              * 1. The class name written using UTF encoding.
330              */

331             dos.writeUTF(name.replace('/', '.'));
332
333             /*
334              * 2. The class modifiers written as a 32-bit integer.
335              */

336             dos.writeInt(access
337                     & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
338                             | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
339
340             /*
341              * 3. The name of each interface sorted by name written using UTF
342              * encoding.
343              */

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

360             writeItems(svuidFields, dos, false);
361
362             /*
363              * 5. If a class initializer exists, write out the following: 1. The
364              * name of the method, <clinit>, in UTF encoding. 2. The modifier of
365              * the method, java.lang.reflect.Modifier.STATIC, written as a
366              * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
367              * encoding.
368              */

369             if (hasStaticInitializer) {
370                 dos.writeUTF("<clinit>");
371                 dos.writeInt(Opcodes.ACC_STATIC);
372                 dos.writeUTF("()V");
373             } // if..
374

375             /*
376              * 6. For each non-private constructor sorted by method name and
377              * signature: 1. The name of the method, <init>, in UTF encoding. 2.
378              * The modifiers of the method written as a 32-bit integer. 3. The
379              * descriptor of the method in UTF encoding.
380              */

381             writeItems(svuidConstructors, dos, true);
382
383             /*
384              * 7. For each non-private method sorted by method name and
385              * signature: 1. The name of the method in UTF encoding. 2. The
386              * modifiers of the method written as a 32-bit integer. 3. The
387              * descriptor of the method in UTF encoding.
388              */

389             writeItems(svuidMethods, dos, true);
390
391             dos.flush();
392
393             /*
394              * 8. The SHA-1 algorithm is executed on the stream of bytes
395              * produced by DataOutputStream and produces five 32-bit values
396              * sha[0..4].
397              */

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

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

435     private void writeItems(
436         final Collection JavaDoc itemCollection,
437         final DataOutputStream JavaDoc dos,
438         final boolean dotted) throws IOException JavaDoc
439     {
440         int size = itemCollection.size();
441         Item items[] = (Item[]) itemCollection.toArray(new Item[size]);
442         Arrays.sort(items);
443         for (int i = 0; i < size; i++) {
444             dos.writeUTF(items[i].name);
445             dos.writeInt(items[i].access);
446             dos.writeUTF(dotted
447                     ? items[i].desc.replace('/', '.')
448                     : items[i].desc);
449         }
450     }
451
452     // ------------------------------------------------------------------------
453
// Inner classes
454
// ------------------------------------------------------------------------
455

456     static class Item implements Comparable JavaDoc {
457
458         String JavaDoc name;
459
460         int access;
461
462         String JavaDoc desc;
463
464         Item(final String JavaDoc name, final int access, final String JavaDoc desc) {
465             this.name = name;
466             this.access = access;
467             this.desc = desc;
468         }
469
470         public int compareTo(final Object JavaDoc o) {
471             Item other = (Item) o;
472             int retVal = name.compareTo(other.name);
473             if (retVal == 0) {
474                 retVal = desc.compareTo(other.desc);
475             }
476             return retVal;
477         }
478     }
479 }
480
Popular Tags