KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > proxy > ClassProxyFactory


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.aop.proxy;
23
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.lang.reflect.Field JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.WeakHashMap JavaDoc;
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 /**
51  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
52  * @version $Revision: 45977 $
53  */

54 public class ClassProxyFactory
55 {
56    private static Object JavaDoc maplock = new Object JavaDoc();
57    private static WeakValueHashMap classnameMap = new WeakValueHashMap();
58    private static WeakHashMap JavaDoc proxyCache = new WeakHashMap JavaDoc();
59    private static WeakHashMap JavaDoc methodMapCache = new WeakHashMap JavaDoc();
60
61    public static ClassProxy newInstance(Class JavaDoc clazz) throws Exception JavaDoc
62    {
63       return newInstance(clazz, null);
64    }
65
66    public static ClassProxy newInstance(Class JavaDoc clazz, ProxyMixin[] mixins) throws Exception JavaDoc
67    {
68       return newInstance(clazz, mixins, new ClassInstanceAdvisor());
69    }
70
71    private static Class JavaDoc getProxyClass(Class JavaDoc clazz, ProxyMixin[] mixins)
72    throws Exception JavaDoc
73    {
74       // Don't make a proxy of a proxy !
75
if (ClassProxy.class.isAssignableFrom(clazz)) clazz = clazz.getSuperclass();
76
77       Class JavaDoc proxyClass = null;
78       synchronized (maplock)
79       {
80          WeakReference JavaDoc ref = (WeakReference JavaDoc) proxyCache.get(clazz);
81          if (ref != null)
82          {
83             proxyClass = (Class JavaDoc)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 JavaDoc(proxyClass));
90             HashMap JavaDoc map = methodMap(clazz);
91             methodMapCache.put(proxyClass, map);
92          }
93       }
94       return proxyClass;
95    }
96
97    public static ClassProxy newInstance(Class JavaDoc clazz, ProxyMixin[] mixins, InstanceAdvisor advisor) throws Exception JavaDoc
98    {
99       Class JavaDoc 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 JavaDoc getMethodMap(String JavaDoc classname)
108    {
109       synchronized (maplock)
110       {
111          Class JavaDoc clazz = (Class JavaDoc) classnameMap.get(classname);
112          if (clazz == null) return null;
113          return (HashMap JavaDoc) methodMapCache.get(clazz);
114       }
115    }
116
117    public static HashMap JavaDoc getMethodMap(Class JavaDoc clazz)
118    {
119       HashMap JavaDoc map = getMethodMap(clazz.getName());
120       if (map != null) return map;
121       try
122       {
123          return methodMap(clazz);
124       }
125       catch (Exception JavaDoc e)
126       {
127          throw new RuntimeException JavaDoc(e); //To change body of catch statement use Options | File Templates.
128
}
129    }
130
131    private static int counter = 0;
132
133    private static CtClass createProxyCtClass(ProxyMixin[] mixins, Class JavaDoc clazz)
134    throws Exception JavaDoc
135    {
136       ClassPool pool = AspectManager.instance().findClassPool(clazz.getClassLoader());
137       if (pool == null) throw new NullPointerException JavaDoc("Could not find ClassPool");
138
139       String JavaDoc 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       /*
201       CtMethod writeEx = template.getDeclaredMethod("writeExternal");
202       CtMethod readEx = template.getDeclaredMethod("readExternal");
203       CtMethod getInstanceAdvisor = template.getDeclaredMethod("_getInstanceAdvisor");
204       CtMethod setInstanceAdvisor = template.getDeclaredMethod("_setInstanceAdvisor");
205       CtMethod dynamicInvoke = template.getDeclaredMethod("_dynamicInvoke");
206       CtMethod setMixins = template.getDeclaredMethod("setMixins");
207       CtMethod writeReplace = template.getDeclaredMethod("writeReplace");
208
209
210
211
212       proxy.addMethod(CtNewMethod.copy(writeEx, proxy, null));
213       proxy.addMethod(CtNewMethod.copy(readEx, proxy, null));
214       proxy.addMethod(CtNewMethod.copy(getInstanceAdvisor, proxy, null));
215       proxy.addMethod(CtNewMethod.copy(setInstanceAdvisor, proxy, null));
216       proxy.addMethod(CtNewMethod.copy(dynamicInvoke, proxy, null));
217       proxy.addMethod(CtNewMethod.copy(setMixins, proxy, null));
218       proxy.addMethod(CtNewMethod.copy(writeReplace, proxy, null));
219       */

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 JavaDoc addedInterfaces = new HashSet JavaDoc();
236       HashSet JavaDoc addedMethods = new HashSet JavaDoc();
237       if (mixins != null)
238       {
239          for (int i = 0; i < mixins.length; i++)
240          {
241             HashSet JavaDoc mixinMethods = new HashSet JavaDoc();
242             Class JavaDoc[] 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 JavaDoc("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 JavaDoc hash = new Long JavaDoc(JavassistMethodHashing.methodHash(methods[m]));
255                   if (mixinMethods.contains(hash)) continue;
256                   if (addedMethods.contains(hash)) throw new Exception JavaDoc("More than one mixin has same method");
257                   mixinMethods.add(hash);
258                   addedMethods.add(hash);
259                   String JavaDoc returnStr = (methods[m].getReturnType().equals(CtClass.voidType)) ? "" : "return ";
260                   String JavaDoc 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 JavaDoc allMethods = JavassistMethodHashing.getMethodMap(superclass);
276
277       Iterator JavaDoc it = allMethods.entrySet().iterator();
278       while (it.hasNext())
279       {
280          Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
281          CtMethod m = (CtMethod) entry.getValue();
282          if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers())) continue;
283
284          Long JavaDoc hash = (Long JavaDoc) entry.getKey();
285          if (addedMethods.contains(hash)) continue;
286          addedMethods.add(hash);
287          String JavaDoc aopReturnStr = (m.getReturnType().equals(CtClass.voidType)) ? "" : "return ($r)";
288          String JavaDoc args = "null";
289          if (m.getParameterTypes().length > 0) args = "$args";
290          String JavaDoc 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 JavaDoc generateProxy(Class JavaDoc clazz, ProxyMixin[] mixins) throws Exception JavaDoc
308    {
309       CtClass proxy = createProxyCtClass(mixins, clazz);
310       Class JavaDoc proxyClass = TransformerCommon.toClass(proxy);
311       Map JavaDoc methodmap = ClassProxyFactory.getMethodMap(proxyClass);
312       Field JavaDoc field = proxyClass.getDeclaredField("methodMap");
313       field.setAccessible(true);
314       field.set(null, methodmap);
315       return proxyClass;
316    }
317
318    private static void populateMethodTables(HashMap JavaDoc advised, Class JavaDoc superclass)
319    throws Exception JavaDoc
320    {
321       if (superclass == null) return;
322       if (superclass.getName().equals("java.lang.Object")) return;
323
324       populateMethodTables(advised, superclass.getSuperclass());
325
326       Method JavaDoc[] 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 JavaDoc(hash), new MethodPersistentReference(declaredMethods[i], PersistentReference.REFERENCE_SOFT));
333          }
334       }
335    }
336
337    public static HashMap JavaDoc methodMap(Class JavaDoc clazz)
338    throws Exception JavaDoc
339    {
340       HashMap JavaDoc methods = new HashMap JavaDoc();
341       populateMethodTables(methods, clazz);
342       return methods;
343    }
344
345 }
346
Popular Tags