Home | History | Annotate | Download | only in javassist
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 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  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist;
     17 
     18 import java.io.*;
     19 import java.lang.reflect.Modifier;
     20 
     21 import javassist.bytecode.*;
     22 import java.util.*;
     23 import java.security.*;
     24 
     25 /**
     26  * Utility for calculating serialVersionUIDs for Serializable classes.
     27  *
     28  * @author Bob Lee (crazybob (at) crazybob.org)
     29  * @author modified by Shigeru Chiba
     30  */
     31 public class SerialVersionUID {
     32 
     33     /**
     34      * Adds serialVersionUID if one does not already exist. Call this before
     35      * modifying a class to maintain serialization compatability.
     36      */
     37     public static void setSerialVersionUID(CtClass clazz)
     38         throws CannotCompileException, NotFoundException
     39     {
     40         // check for pre-existing field.
     41         try {
     42             clazz.getDeclaredField("serialVersionUID");
     43             return;
     44         }
     45         catch (NotFoundException e) {}
     46 
     47         // check if the class is serializable.
     48         if (!isSerializable(clazz))
     49             return;
     50 
     51         // add field with default value.
     52         CtField field = new CtField(CtClass.longType, "serialVersionUID",
     53                                     clazz);
     54         field.setModifiers(Modifier.PRIVATE | Modifier.STATIC |
     55                            Modifier.FINAL);
     56         clazz.addField(field, calculateDefault(clazz) + "L");
     57     }
     58 
     59     /**
     60      * Does the class implement Serializable?
     61      */
     62     private static boolean isSerializable(CtClass clazz)
     63         throws NotFoundException
     64     {
     65         ClassPool pool = clazz.getClassPool();
     66         return clazz.subtypeOf(pool.get("java.io.Serializable"));
     67     }
     68 
     69     /**
     70      * Calculate default value. See Java Serialization Specification, Stream
     71      * Unique Identifiers.
     72      */
     73     static long calculateDefault(CtClass clazz)
     74         throws CannotCompileException
     75     {
     76         try {
     77             ByteArrayOutputStream bout = new ByteArrayOutputStream();
     78             DataOutputStream out = new DataOutputStream(bout);
     79             ClassFile classFile = clazz.getClassFile();
     80 
     81             // class name.
     82             String javaName = javaName(clazz);
     83             out.writeUTF(javaName);
     84 
     85             CtMethod[] methods = clazz.getDeclaredMethods();
     86 
     87             // class modifiers.
     88             int classMods = clazz.getModifiers();
     89             if ((classMods & Modifier.INTERFACE) != 0)
     90                 if (methods.length > 0)
     91                     classMods = classMods | Modifier.ABSTRACT;
     92                 else
     93                     classMods = classMods & ~Modifier.ABSTRACT;
     94 
     95             out.writeInt(classMods);
     96 
     97             // interfaces.
     98             String[] interfaces = classFile.getInterfaces();
     99             for (int i = 0; i < interfaces.length; i++)
    100                 interfaces[i] = javaName(interfaces[i]);
    101 
    102             Arrays.sort(interfaces);
    103             for (int i = 0; i < interfaces.length; i++)
    104                 out.writeUTF(interfaces[i]);
    105 
    106             // fields.
    107             CtField[] fields = clazz.getDeclaredFields();
    108             Arrays.sort(fields, new Comparator() {
    109                 public int compare(Object o1, Object o2) {
    110                     CtField field1 = (CtField)o1;
    111                     CtField field2 = (CtField)o2;
    112                     return field1.getName().compareTo(field2.getName());
    113                 }
    114             });
    115 
    116             for (int i = 0; i < fields.length; i++) {
    117                 CtField field = (CtField) fields[i];
    118                 int mods = field.getModifiers();
    119                 if (((mods & Modifier.PRIVATE) == 0) ||
    120                     ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
    121                     out.writeUTF(field.getName());
    122                     out.writeInt(mods);
    123                     out.writeUTF(field.getFieldInfo2().getDescriptor());
    124                 }
    125             }
    126 
    127             // static initializer.
    128             if (classFile.getStaticInitializer() != null) {
    129                 out.writeUTF("<clinit>");
    130                 out.writeInt(Modifier.STATIC);
    131                 out.writeUTF("()V");
    132             }
    133 
    134             // constructors.
    135             CtConstructor[] constructors = clazz.getDeclaredConstructors();
    136             Arrays.sort(constructors, new Comparator() {
    137                 public int compare(Object o1, Object o2) {
    138                     CtConstructor c1 = (CtConstructor)o1;
    139                     CtConstructor c2 = (CtConstructor)o2;
    140                     return c1.getMethodInfo2().getDescriptor().compareTo(
    141                                         c2.getMethodInfo2().getDescriptor());
    142                 }
    143             });
    144 
    145             for (int i = 0; i < constructors.length; i++) {
    146                 CtConstructor constructor = constructors[i];
    147                 int mods = constructor.getModifiers();
    148                 if ((mods & Modifier.PRIVATE) == 0) {
    149                     out.writeUTF("<init>");
    150                     out.writeInt(mods);
    151                     out.writeUTF(constructor.getMethodInfo2()
    152                                  .getDescriptor().replace('/', '.'));
    153                 }
    154             }
    155 
    156             // methods.
    157             Arrays.sort(methods, new Comparator() {
    158                 public int compare(Object o1, Object o2) {
    159                     CtMethod m1 = (CtMethod)o1;
    160                     CtMethod m2 = (CtMethod)o2;
    161                     int value = m1.getName().compareTo(m2.getName());
    162                     if (value == 0)
    163                         value = m1.getMethodInfo2().getDescriptor()
    164                             .compareTo(m2.getMethodInfo2().getDescriptor());
    165 
    166                     return value;
    167                 }
    168             });
    169 
    170             for (int i = 0; i < methods.length; i++) {
    171                 CtMethod method = methods[i];
    172                 int mods = method.getModifiers()
    173                            & (Modifier.PUBLIC | Modifier.PRIVATE
    174                               | Modifier.PROTECTED | Modifier.STATIC
    175                               | Modifier.FINAL | Modifier.SYNCHRONIZED
    176                               | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT);
    177                 if ((mods & Modifier.PRIVATE) == 0) {
    178                     out.writeUTF(method.getName());
    179                     out.writeInt(mods);
    180                     out.writeUTF(method.getMethodInfo2()
    181                                  .getDescriptor().replace('/', '.'));
    182                 }
    183             }
    184 
    185             // calculate hash.
    186             out.flush();
    187             MessageDigest digest = MessageDigest.getInstance("SHA");
    188             byte[] digested = digest.digest(bout.toByteArray());
    189             long hash = 0;
    190             for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--)
    191                 hash = (hash << 8) | (digested[i] & 0xFF);
    192 
    193             return hash;
    194         }
    195         catch (IOException e) {
    196             throw new CannotCompileException(e);
    197         }
    198         catch (NoSuchAlgorithmException e) {
    199             throw new CannotCompileException(e);
    200         }
    201     }
    202 
    203     private static String javaName(CtClass clazz) {
    204         return Descriptor.toJavaName(Descriptor.toJvmName(clazz));
    205     }
    206 
    207     private static String javaName(String name) {
    208         return Descriptor.toJavaName(Descriptor.toJvmName(name));
    209     }
    210 }
    211