1 22 package org.jboss.aop.proxy; 23 24 import java.lang.ref.WeakReference ; 25 import java.lang.reflect.Field ; 26 import java.lang.reflect.Method ; 27 import java.util.HashMap ; 28 import java.util.HashSet ; 29 import java.util.Iterator ; 30 import java.util.Map ; 31 import java.util.WeakHashMap ; 32 import org.jboss.aop.AspectManager; 33 import org.jboss.aop.ClassAdvisor; 34 import org.jboss.aop.ClassInstanceAdvisor; 35 import org.jboss.aop.InstanceAdvisor; 36 import org.jboss.aop.instrument.TransformerCommon; 37 import org.jboss.aop.util.JavassistMethodHashing; 38 import org.jboss.aop.util.reference.MethodPersistentReference; 39 import org.jboss.aop.util.reference.PersistentReference; 40 import org.jboss.util.collection.WeakValueHashMap; 41 import javassist.ClassPool; 42 import javassist.CtClass; 43 import javassist.CtField; 44 import javassist.CtMethod; 45 import javassist.CtNewMethod; 46 import javassist.Modifier; 47 import javassist.SerialVersionUID; 48 49 50 54 public class ClassProxyFactory 55 { 56 private static Object maplock = new Object (); 57 private static WeakValueHashMap classnameMap = new WeakValueHashMap(); 58 private static WeakHashMap proxyCache = new WeakHashMap (); 59 private static WeakHashMap methodMapCache = new WeakHashMap (); 60 61 public static ClassProxy newInstance(Class clazz) throws Exception 62 { 63 return newInstance(clazz, null); 64 } 65 66 public static ClassProxy newInstance(Class clazz, ProxyMixin[] mixins) throws Exception 67 { 68 return newInstance(clazz, mixins, new ClassInstanceAdvisor()); 69 } 70 71 private static Class getProxyClass(Class clazz, ProxyMixin[] mixins) 72 throws Exception 73 { 74 if (ClassProxy.class.isAssignableFrom(clazz)) clazz = clazz.getSuperclass(); 76 77 Class proxyClass = null; 78 synchronized (maplock) 79 { 80 WeakReference ref = (WeakReference ) proxyCache.get(clazz); 81 if (ref != null) 82 { 83 proxyClass = (Class )ref.get(); 84 } 85 if (proxyClass == null) 86 { 87 proxyClass = generateProxy(clazz, mixins); 88 classnameMap.put(clazz.getName(), proxyClass); 89 proxyCache.put(clazz, new WeakReference (proxyClass)); 90 HashMap map = methodMap(clazz); 91 methodMapCache.put(proxyClass, map); 92 } 93 } 94 return proxyClass; 95 } 96 97 public static ClassProxy newInstance(Class clazz, ProxyMixin[] mixins, InstanceAdvisor advisor) throws Exception 98 { 99 Class proxyClass = getProxyClass(clazz, mixins); 100 ClassProxy proxy = (ClassProxy) proxyClass.newInstance(); 101 proxy.setMixins(mixins); 102 proxy._setInstanceAdvisor(advisor); 103 return proxy; 104 105 } 106 107 public static HashMap getMethodMap(String classname) 108 { 109 synchronized (maplock) 110 { 111 Class clazz = (Class ) classnameMap.get(classname); 112 if (clazz == null) return null; 113 return (HashMap ) methodMapCache.get(clazz); 114 } 115 } 116 117 public static HashMap getMethodMap(Class clazz) 118 { 119 HashMap map = getMethodMap(clazz.getName()); 120 if (map != null) return map; 121 try 122 { 123 return methodMap(clazz); 124 } 125 catch (Exception e) 126 { 127 throw new RuntimeException (e); } 129 } 130 131 private static int counter = 0; 132 133 private static CtClass createProxyCtClass(ProxyMixin[] mixins, Class clazz) 134 throws Exception 135 { 136 ClassPool pool = AspectManager.instance().findClassPool(clazz.getClassLoader()); 137 if (pool == null) throw new NullPointerException ("Could not find ClassPool"); 138 139 String classname = "AOPClassProxy$" + counter++; 140 141 CtClass template = pool.get("org.jboss.aop.proxy.ClassProxyTemplate"); 142 CtClass superclass = pool.get(clazz.getName()); 143 144 CtField mixinField = template.getField("mixins"); 145 CtField instanceAdvisor = template.getField("instanceAdvisor"); 146 147 148 CtClass proxy = TransformerCommon.makeClass(pool, classname, superclass); 149 150 mixinField = new CtField(mixinField.getType(), "mixins", proxy); 151 mixinField.setModifiers(Modifier.PRIVATE); 152 proxy.addField(mixinField); 153 instanceAdvisor = new CtField(instanceAdvisor.getType(), "instanceAdvisor", proxy); 154 instanceAdvisor.setModifiers(Modifier.PRIVATE); 155 proxy.addField(instanceAdvisor); 156 157 CtMethod writeEx = CtNewMethod.make(" public void writeExternal(java.io.ObjectOutput out)\n" + 158 " throws java.io.IOException\n" + 159 " {\n" + 160 " }", proxy); 161 CtMethod readEx = CtNewMethod.make(" public void readExternal(java.io.ObjectInput in)\n" + 162 " throws java.io.IOException, ClassNotFoundException\n" + 163 " {\n" + 164 " }", proxy); 165 CtMethod getInstanceAdvisor = CtNewMethod.make(" public org.jboss.aop.InstanceAdvisor _getInstanceAdvisor()\n" + 166 " {\n" + 167 " return instanceAdvisor;\n" + 168 " }", proxy); 169 CtMethod setInstanceAdvisor = CtNewMethod.make(" public void _setInstanceAdvisor(org.jboss.aop.InstanceAdvisor newAdvisor)\n" + 170 " {\n" + 171 " instanceAdvisor = (org.jboss.aop.ClassInstanceAdvisor) newAdvisor;\n" + 172 " }", proxy); 173 CtMethod dynamicInvoke = CtNewMethod.make(" public org.jboss.aop.joinpoint.InvocationResponse _dynamicInvoke(org.jboss.aop.joinpoint.Invocation invocation)\n" + 174 " throws Throwable\n" + 175 " {\n" + 176 " ((org.jboss.aop.joinpoint.InvocationBase) invocation).setInstanceResolver(instanceAdvisor.getMetaData());\n" + 177 " org.jboss.aop.advice.Interceptor[] aspects = instanceAdvisor.getInterceptors();\n" + 178 " return new org.jboss.aop.joinpoint.InvocationResponse(invocation.invokeNext(aspects));\n" + 179 " }", proxy); 180 181 CtMethod setMixins = CtNewMethod.make(" public void setMixins(org.jboss.aop.proxy.ProxyMixin[] mixins)\n" + 182 " {\n" + 183 " this.mixins = mixins;\n" + 184 " }", proxy); 185 186 CtMethod writeReplace = CtNewMethod.make(" public Object writeReplace() throws java.io.ObjectStreamException\n" + 187 " {\n" + 188 " return new org.jboss.aop.proxy.MarshalledClassProxy(this.getClass().getSuperclass(), mixins, instanceAdvisor);\n" + 189 " }", proxy); 190 191 192 proxy.addMethod(writeEx); 193 proxy.addMethod(readEx); 194 proxy.addMethod(getInstanceAdvisor); 195 proxy.addMethod(setInstanceAdvisor); 196 proxy.addMethod(dynamicInvoke); 197 proxy.addMethod(setMixins); 198 proxy.addMethod(writeReplace); 199 200 220 221 222 proxy.addInterface(pool.get("org.jboss.aop.proxy.ClassProxy")); 223 proxy.addInterface(pool.get("java.io.Externalizable")); 224 proxy.addInterface(pool.get("org.jboss.aop.instrument.Untransformable")); 225 proxy.addInterface(pool.get("org.jboss.aop.proxy.MethodMapped")); 226 227 CtClass map = pool.get("java.util.Map"); 228 CtField methodMap = new CtField(map, "methodMap", proxy); 229 methodMap.setModifiers(Modifier.PRIVATE | Modifier.STATIC); 230 proxy.addField(methodMap); 231 CtMethod getMethodMap = CtNewMethod.getter("getMethodMap", methodMap); 232 getMethodMap.setModifiers(Modifier.PUBLIC); 233 proxy.addMethod(getMethodMap); 234 235 HashSet addedInterfaces = new HashSet (); 236 HashSet addedMethods = new HashSet (); 237 if (mixins != null) 238 { 239 for (int i = 0; i < mixins.length; i++) 240 { 241 HashSet mixinMethods = new HashSet (); 242 Class [] mixinf = mixins[i].getInterfaces(); 243 ClassPool mixPool = AspectManager.instance().findClassPool(mixins[i].getMixin().getClass().getClassLoader()); 244 CtClass mixClass = mixPool.get(mixins[i].getMixin().getClass().getName()); 245 for (int j = 0; j < mixinf.length; j++) 246 { 247 if (addedInterfaces.contains(mixinf[j].getName())) throw new Exception ("2 mixins are implementing the same interfaces"); 248 ClassPool mixIntfPool = AspectManager.instance().findClassPool(mixinf[j].getClassLoader()); 249 CtClass intfClass = mixIntfPool.get(mixinf[j].getName()); 250 CtMethod[] methods = intfClass.getMethods(); 251 for (int m = 0; m < methods.length; m++) 252 { 253 if (methods[m].getDeclaringClass().getName().equals("java.lang.Object")) continue; 254 Long hash = new Long (JavassistMethodHashing.methodHash(methods[m])); 255 if (mixinMethods.contains(hash)) continue; 256 if (addedMethods.contains(hash)) throw new Exception ("More than one mixin has same method"); 257 mixinMethods.add(hash); 258 addedMethods.add(hash); 259 String returnStr = (methods[m].getReturnType().equals(CtClass.voidType)) ? "" : "return "; 260 String code = "{" + 261 " " + mixClass.getName() + " mixin = (" + mixClass.getName() + ")mixins[" + i + "].getMixin();" + 262 " " + returnStr + " mixin." + methods[m].getName() + "($$);" + 263 "}"; 264 CtMethod newMethod = CtNewMethod.make(methods[m].getReturnType(), methods[m].getName(), methods[m].getParameterTypes(), methods[m].getExceptionTypes(), code, proxy); 265 newMethod.setModifiers(Modifier.PUBLIC); 266 proxy.addMethod(newMethod); 267 } 268 269 proxy.addInterface(intfClass); 270 addedInterfaces.add(intfClass.getName()); 271 } 272 } 273 } 274 275 HashMap allMethods = JavassistMethodHashing.getMethodMap(superclass); 276 277 Iterator it = allMethods.entrySet().iterator(); 278 while (it.hasNext()) 279 { 280 Map.Entry entry = (Map.Entry ) it.next(); 281 CtMethod m = (CtMethod) entry.getValue(); 282 if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers())) continue; 283 284 Long hash = (Long ) entry.getKey(); 285 if (addedMethods.contains(hash)) continue; 286 addedMethods.add(hash); 287 String aopReturnStr = (m.getReturnType().equals(CtClass.voidType)) ? "" : "return ($r)"; 288 String args = "null"; 289 if (m.getParameterTypes().length > 0) args = "$args"; 290 String code = "{ " + 291 " org.jboss.aop.advice.Interceptor[] aspects = instanceAdvisor.getInterceptors(); " + 292 " org.jboss.aop.MethodInfo mi = new org.jboss.aop.MethodInfo(); " + 293 " mi.setHash(" + hash.longValue() + "L);" + 294 " org.jboss.aop.proxy.ProxyMethodInvocation invocation = new org.jboss.aop.proxy.ProxyMethodInvocation(this, mi, aspects); " + 295 " invocation.setInstanceResolver(instanceAdvisor.getMetaData()); " + 296 " invocation.setArguments(" + args + "); " + 297 " " + aopReturnStr + " invocation.invokeNext(); " + 298 "}"; 299 CtMethod newMethod = CtNewMethod.make(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes(), code, proxy); 300 newMethod.setModifiers(Modifier.PUBLIC); 301 proxy.addMethod(newMethod); 302 } 303 SerialVersionUID.setSerialVersionUID(proxy); 304 return proxy; 305 } 306 307 private static Class generateProxy(Class clazz, ProxyMixin[] mixins) throws Exception 308 { 309 CtClass proxy = createProxyCtClass(mixins, clazz); 310 Class proxyClass = TransformerCommon.toClass(proxy); 311 Map methodmap = ClassProxyFactory.getMethodMap(proxyClass); 312 Field field = proxyClass.getDeclaredField("methodMap"); 313 field.setAccessible(true); 314 field.set(null, methodmap); 315 return proxyClass; 316 } 317 318 private static void populateMethodTables(HashMap advised, Class superclass) 319 throws Exception 320 { 321 if (superclass == null) return; 322 if (superclass.getName().equals("java.lang.Object")) return; 323 324 populateMethodTables(advised, superclass.getSuperclass()); 325 326 Method [] declaredMethods = superclass.getDeclaredMethods(); 327 for (int i = 0; i < declaredMethods.length; i++) 328 { 329 if (ClassAdvisor.isAdvisable(declaredMethods[i])) 330 { 331 long hash = org.jboss.aop.util.MethodHashing.methodHash(declaredMethods[i]); 332 advised.put(new Long (hash), new MethodPersistentReference(declaredMethods[i], PersistentReference.REFERENCE_SOFT)); 333 } 334 } 335 } 336 337 public static HashMap methodMap(Class clazz) 338 throws Exception 339 { 340 HashMap methods = new HashMap (); 341 populateMethodTables(methods, clazz); 342 return methods; 343 } 344 345 } 346 | Popular Tags |