Home | History | Annotate | Download | only in proxy
      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.util.proxy;
     17 
     18 import java.lang.reflect.Method;
     19 import java.io.BufferedOutputStream;
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.DataOutputStream;
     22 import java.io.File;
     23 import java.io.FileOutputStream;
     24 import java.io.IOException;
     25 import java.security.ProtectionDomain;
     26 
     27 import javassist.CannotCompileException;
     28 import javassist.bytecode.ClassFile;
     29 
     30 /**
     31  * A helper class for implementing <code>ProxyFactory</code>.
     32  * The users of <code>ProxyFactory</code> do not have to see this class.
     33  *
     34  * @see ProxyFactory
     35  */
     36 public class FactoryHelper {
     37     private static java.lang.reflect.Method defineClass1, defineClass2;
     38 
     39     static {
     40         try {
     41             Class cl = Class.forName("java.lang.ClassLoader");
     42             defineClass1 = SecurityActions.getDeclaredMethod(
     43                         cl,
     44                         "defineClass",
     45                         new Class[] { String.class, byte[].class,
     46                                       int.class, int.class });
     47 
     48             defineClass2 = SecurityActions.getDeclaredMethod(
     49                         cl,
     50                         "defineClass",
     51                         new Class[] { String.class, byte[].class,
     52                               int.class, int.class, ProtectionDomain.class });
     53         }
     54         catch (Exception e) {
     55             throw new RuntimeException("cannot initialize");
     56         }
     57     }
     58 
     59     /**
     60      * Returns an index for accessing arrays in this class.
     61      *
     62      * @throws RuntimeException     if a given type is not a primitive type.
     63      */
     64     public static final int typeIndex(Class type) {
     65         Class[] list = primitiveTypes;
     66         int n = list.length;
     67         for (int i = 0; i < n; i++)
     68             if (list[i] == type)
     69                 return i;
     70 
     71         throw new RuntimeException("bad type:" + type.getName());
     72     }
     73 
     74     /**
     75      * <code>Class</code> objects representing primitive types.
     76      */
     77     public static final Class[] primitiveTypes = {
     78         Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
     79         Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
     80     };
     81 
     82     /**
     83      * The fully-qualified names of wrapper classes for primitive types.
     84      */
     85     public static final String[] wrapperTypes = {
     86         "java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
     87         "java.lang.Short", "java.lang.Integer", "java.lang.Long",
     88         "java.lang.Float", "java.lang.Double", "java.lang.Void"
     89     };
     90 
     91     /**
     92      * The descriptors of the constructors of wrapper classes.
     93      */
     94     public static final String[] wrapperDesc = {
     95         "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V",
     96         "(F)V", "(D)V"
     97     };
     98 
     99     /**
    100      * The names of methods for obtaining a primitive value
    101      * from a wrapper object.  For example, <code>intValue()</code>
    102      * is such a method for obtaining an integer value from a
    103      * <code>java.lang.Integer</code> object.
    104      */
    105     public static final String[] unwarpMethods = {
    106         "booleanValue", "byteValue", "charValue", "shortValue",
    107         "intValue", "longValue", "floatValue", "doubleValue"
    108     };
    109 
    110     /**
    111      * The descriptors of the unwrapping methods contained
    112      * in <code>unwrapMethods</code>.
    113      */
    114     public static final String[] unwrapDesc = {
    115         "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D"
    116     };
    117 
    118     /**
    119      * The data size of primitive types.  <code>long</code>
    120      * and <code>double</code> are 2; the others are 1.
    121      */
    122     public static final int[] dataSize = {
    123         1, 1, 1, 1, 1, 2, 1, 2
    124     };
    125 
    126     /**
    127      * Loads a class file by a given class loader.
    128      * This method uses a default protection domain for the class
    129      * but it may not work with a security manager or a sigend jar file.
    130      *
    131      * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
    132      */
    133     public static Class toClass(ClassFile cf, ClassLoader loader)
    134         throws CannotCompileException
    135     {
    136         return toClass(cf, loader, null);
    137     }
    138 
    139     /**
    140      * Loads a class file by a given class loader.
    141      *
    142      * @param domain        if it is null, a default domain is used.
    143      * @since 3.3
    144      */
    145     public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain)
    146             throws CannotCompileException
    147     {
    148         try {
    149             byte[] b = toBytecode(cf);
    150             Method method;
    151             Object[] args;
    152             if (domain == null) {
    153                 method = defineClass1;
    154                 args = new Object[] { cf.getName(), b, new Integer(0),
    155                         new Integer(b.length) };
    156             }
    157             else {
    158                 method = defineClass2;
    159                 args = new Object[] { cf.getName(), b, new Integer(0),
    160                         new Integer(b.length), domain };
    161             }
    162 
    163             return toClass2(method, loader, args);
    164         }
    165         catch (RuntimeException e) {
    166             throw e;
    167         }
    168         catch (java.lang.reflect.InvocationTargetException e) {
    169             throw new CannotCompileException(e.getTargetException());
    170         }
    171         catch (Exception e) {
    172             throw new CannotCompileException(e);
    173         }
    174     }
    175 
    176     private static synchronized Class toClass2(Method method,
    177                                         ClassLoader loader, Object[] args)
    178         throws Exception
    179     {
    180         SecurityActions.setAccessible(method, true);
    181         Class clazz = (Class)method.invoke(loader, args);
    182         SecurityActions.setAccessible(method, false);
    183         return clazz;
    184     }
    185 
    186     private static byte[] toBytecode(ClassFile cf) throws IOException {
    187         ByteArrayOutputStream barray = new ByteArrayOutputStream();
    188         DataOutputStream out = new DataOutputStream(barray);
    189         try {
    190             cf.write(out);
    191         }
    192         finally {
    193             out.close();
    194         }
    195 
    196         return barray.toByteArray();
    197     }
    198 
    199     /**
    200      * Writes a class file.
    201      */
    202     public static void writeFile(ClassFile cf, String directoryName)
    203             throws CannotCompileException {
    204         try {
    205             writeFile0(cf, directoryName);
    206         }
    207         catch (IOException e) {
    208             throw new CannotCompileException(e);
    209         }
    210     }
    211 
    212     private static void writeFile0(ClassFile cf, String directoryName)
    213             throws CannotCompileException, IOException {
    214         String classname = cf.getName();
    215         String filename = directoryName + File.separatorChar
    216                 + classname.replace('.', File.separatorChar) + ".class";
    217         int pos = filename.lastIndexOf(File.separatorChar);
    218         if (pos > 0) {
    219             String dir = filename.substring(0, pos);
    220             if (!dir.equals("."))
    221                 new File(dir).mkdirs();
    222         }
    223 
    224         DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
    225                 new FileOutputStream(filename)));
    226         try {
    227             cf.write(out);
    228         }
    229         catch (IOException e) {
    230             throw e;
    231         }
    232         finally {
    233             out.close();
    234         }
    235     }
    236 }
    237