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