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