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