Home | History | Annotate | Download | only in rmi
      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.tools.rmi;
     17 
     18 import javassist.*;
     19 import java.lang.reflect.Method;
     20 import java.util.Hashtable;
     21 import javassist.CtMethod.ConstParameter;
     22 
     23 /**
     24  * A stub-code generator.  It is used for producing a proxy class.
     25  *
     26  * <p>The proxy class for class A is as follows:
     27  *
     28  * <ul><pre>public class A implements Proxy, Serializable {
     29  *   private ObjectImporter importer;
     30  *   private int objectId;
     31  *   public int _getObjectId() { return objectId; }
     32  *   public A(ObjectImporter oi, int id) {
     33  *     importer = oi; objectId = id;
     34  *   }
     35  *
     36  *   ... the same methods that the original class A declares ...
     37  * }</pre></ul>
     38  *
     39  * <p>Instances of the proxy class is created by an
     40  * <code>ObjectImporter</code> object.
     41  */
     42 public class StubGenerator implements Translator {
     43     private static final String fieldImporter = "importer";
     44     private static final String fieldObjectId = "objectId";
     45     private static final String accessorObjectId = "_getObjectId";
     46     private static final String sampleClass = "javassist.tools.rmi.Sample";
     47 
     48     private ClassPool classPool;
     49     private Hashtable proxyClasses;
     50     private CtMethod forwardMethod;
     51     private CtMethod forwardStaticMethod;
     52 
     53     private CtClass[] proxyConstructorParamTypes;
     54     private CtClass[] interfacesForProxy;
     55     private CtClass[] exceptionForProxy;
     56 
     57     /**
     58      * Constructs a stub-code generator.
     59      */
     60     public StubGenerator() {
     61         proxyClasses = new Hashtable();
     62     }
     63 
     64     /**
     65      * Initializes the object.
     66      * This is a method declared in javassist.Translator.
     67      *
     68      * @see javassist.Translator#start(ClassPool)
     69      */
     70     public void start(ClassPool pool) throws NotFoundException {
     71         classPool = pool;
     72         CtClass c = pool.get(sampleClass);
     73         forwardMethod = c.getDeclaredMethod("forward");
     74         forwardStaticMethod = c.getDeclaredMethod("forwardStatic");
     75 
     76         proxyConstructorParamTypes
     77             = pool.get(new String[] { "javassist.tools.rmi.ObjectImporter",
     78                                          "int" });
     79         interfacesForProxy
     80             = pool.get(new String[] { "java.io.Serializable",
     81                                          "javassist.tools.rmi.Proxy" });
     82         exceptionForProxy
     83             = new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") };
     84     }
     85 
     86     /**
     87      * Does nothing.
     88      * This is a method declared in javassist.Translator.
     89      * @see javassist.Translator#onLoad(ClassPool,String)
     90      */
     91     public void onLoad(ClassPool pool, String classname) {}
     92 
     93     /**
     94      * Returns <code>true</code> if the specified class is a proxy class
     95      * recorded by <code>makeProxyClass()</code>.
     96      *
     97      * @param name              a fully-qualified class name
     98      */
     99     public boolean isProxyClass(String name) {
    100         return proxyClasses.get(name) != null;
    101     }
    102 
    103     /**
    104      * Makes a proxy class.  The produced class is substituted
    105      * for the original class.
    106      *
    107      * @param clazz             the class referenced
    108      *                          through the proxy class.
    109      * @return          <code>false</code> if the proxy class
    110      *                  has been already produced.
    111      */
    112     public synchronized boolean makeProxyClass(Class clazz)
    113         throws CannotCompileException, NotFoundException
    114     {
    115         String classname = clazz.getName();
    116         if (proxyClasses.get(classname) != null)
    117             return false;
    118         else {
    119             CtClass ctclazz = produceProxyClass(classPool.get(classname),
    120                                                 clazz);
    121             proxyClasses.put(classname, ctclazz);
    122             modifySuperclass(ctclazz);
    123             return true;
    124         }
    125     }
    126 
    127     private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass)
    128         throws CannotCompileException, NotFoundException
    129     {
    130         int modify = orgclass.getModifiers();
    131         if (Modifier.isAbstract(modify) || Modifier.isNative(modify)
    132             || !Modifier.isPublic(modify))
    133             throw new CannotCompileException(orgclass.getName()
    134                         + " must be public, non-native, and non-abstract.");
    135 
    136         CtClass proxy = classPool.makeClass(orgclass.getName(),
    137                                               orgclass.getSuperclass());
    138 
    139         proxy.setInterfaces(interfacesForProxy);
    140 
    141         CtField f
    142             = new CtField(classPool.get("javassist.tools.rmi.ObjectImporter"),
    143                           fieldImporter, proxy);
    144         f.setModifiers(Modifier.PRIVATE);
    145         proxy.addField(f, CtField.Initializer.byParameter(0));
    146 
    147         f = new CtField(CtClass.intType, fieldObjectId, proxy);
    148         f.setModifiers(Modifier.PRIVATE);
    149         proxy.addField(f, CtField.Initializer.byParameter(1));
    150 
    151         proxy.addMethod(CtNewMethod.getter(accessorObjectId, f));
    152 
    153         proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy));
    154         CtConstructor cons
    155             = CtNewConstructor.skeleton(proxyConstructorParamTypes,
    156                                         null, proxy);
    157         proxy.addConstructor(cons);
    158 
    159         try {
    160             addMethods(proxy, orgRtClass.getMethods());
    161             return proxy;
    162         }
    163         catch (SecurityException e) {
    164             throw new CannotCompileException(e);
    165         }
    166     }
    167 
    168     private CtClass toCtClass(Class rtclass) throws NotFoundException {
    169         String name;
    170         if (!rtclass.isArray())
    171             name = rtclass.getName();
    172         else {
    173             StringBuffer sbuf = new StringBuffer();
    174             do {
    175                 sbuf.append("[]");
    176                 rtclass = rtclass.getComponentType();
    177             } while(rtclass.isArray());
    178             sbuf.insert(0, rtclass.getName());
    179             name = sbuf.toString();
    180         }
    181 
    182         return classPool.get(name);
    183     }
    184 
    185     private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException {
    186         int n = rtclasses.length;
    187         CtClass[] ctclasses = new CtClass[n];
    188         for (int i = 0; i < n; ++i)
    189             ctclasses[i] = toCtClass(rtclasses[i]);
    190 
    191         return ctclasses;
    192     }
    193 
    194     /* ms must not be an array of CtMethod.  To invoke a method ms[i]
    195      * on a server, a client must send i to the server.
    196      */
    197     private void addMethods(CtClass proxy, Method[] ms)
    198         throws CannotCompileException, NotFoundException
    199     {
    200         CtMethod wmethod;
    201         for (int i = 0; i < ms.length; ++i) {
    202             Method m = ms[i];
    203             int mod = m.getModifiers();
    204             if (m.getDeclaringClass() != Object.class
    205                         && !Modifier.isFinal(mod))
    206                 if (Modifier.isPublic(mod)) {
    207                     CtMethod body;
    208                     if (Modifier.isStatic(mod))
    209                         body = forwardStaticMethod;
    210                     else
    211                         body = forwardMethod;
    212 
    213                     wmethod
    214                         = CtNewMethod.wrapped(toCtClass(m.getReturnType()),
    215                                               m.getName(),
    216                                               toCtClass(m.getParameterTypes()),
    217                                               exceptionForProxy,
    218                                               body,
    219                                               ConstParameter.integer(i),
    220                                               proxy);
    221                     wmethod.setModifiers(mod);
    222                     proxy.addMethod(wmethod);
    223                 }
    224                 else if (!Modifier.isProtected(mod)
    225                          && !Modifier.isPrivate(mod))
    226                     // if package method
    227                     throw new CannotCompileException(
    228                         "the methods must be public, protected, or private.");
    229         }
    230     }
    231 
    232     /**
    233      * Adds a default constructor to the super classes.
    234      */
    235     private void modifySuperclass(CtClass orgclass)
    236         throws CannotCompileException, NotFoundException
    237     {
    238         CtClass superclazz;
    239         for (;; orgclass = superclazz) {
    240             superclazz = orgclass.getSuperclass();
    241             if (superclazz == null)
    242                 break;
    243 
    244             try {
    245                 superclazz.getDeclaredConstructor(null);
    246                 break;  // the constructor with no arguments is found.
    247             }
    248             catch (NotFoundException e) {
    249             }
    250 
    251             superclazz.addConstructor(
    252                         CtNewConstructor.defaultConstructor(superclazz));
    253         }
    254     }
    255 }
    256