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
16package javassist.tools.rmi;
17
18import javassist.*;
19import java.lang.reflect.Method;
20import java.util.Hashtable;
21import 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 */
42public 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