KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > instrument > CallerTransformer


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.instrument;
23
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import org.jboss.aop.AspectManager;
28 import org.jboss.aop.ClassAdvisor;
29 import org.jboss.aop.pointcut.Pointcut;
30 import org.jboss.aop.util.Advisable;
31 import org.jboss.aop.util.JavassistMethodHashing;
32 import javassist.CannotCompileException;
33 import javassist.CtBehavior;
34 import javassist.CtClass;
35 import javassist.CtConstructor;
36 import javassist.CtMethod;
37 import javassist.Modifier;
38 import javassist.NotFoundException;
39 import javassist.expr.ExprEditor;
40 import javassist.expr.MethodCall;
41 import javassist.expr.NewExpr;
42
43 /**
44  * Instruments Caller pointcuts
45  *
46  * @author <a HREF="mailto:kabirkhan@bigfoot.com">Kabir Khan</a>
47  */

48
49 public abstract class CallerTransformer
50 {
51    public static final String JavaDoc CON_BY_CON_INFO_CLASS_NAME = org.jboss.aop.ConByConInfo.class.getName();
52    public static final String JavaDoc CON_BY_METHOD_INFO_CLASS_NAME = org.jboss.aop.ConByMethodInfo.class.getName();
53    public static final String JavaDoc METHOD_BY_CON_INFO_CLASS_NAME = org.jboss.aop.MethodByConInfo.class.getName();
54    public static final String JavaDoc METHOD_BY_METHOD_INFO_CLASS_NAME = org.jboss.aop.MethodByMethodInfo.class.getName();
55    
56    Instrumentor instrumentor;
57    boolean optimize;
58    AspectManager manager;
59    CallerInfoAdder callerInfoAdder;
60
61    // Static --------------------------------------------------------
62

63    // Constructors --------------------------------------------------
64
protected CallerTransformer(
65          Instrumentor instrumentor,
66          AspectManager manager,
67          boolean optimize,
68          CallerInfoAdder callerInfoAdder)
69    {
70       this.instrumentor = instrumentor;
71       this.optimize = optimize;
72       this.manager = manager;
73       this.callerInfoAdder = callerInfoAdder;
74    }
75
76    protected abstract CallerExprEditor callerExprEditorFactory(ClassAdvisor advisor, CtClass clazz);
77    
78    public boolean applyCallerPointcuts(CtClass clazz, ClassAdvisor advisor) throws CannotCompileException
79    {
80       if (!advisor.getManager().isWithin() && !advisor.getManager().isCall() && !advisor.getManager().isWithincode())
81       {
82          if (AspectManager.verbose) System.out.println("[debug] There are no caller pointcuts!");
83          return false;
84       }
85       CallerExprEditor expr = callerExprEditorFactory(advisor, clazz);
86       CtMethod[] methods = clazz.getDeclaredMethods();
87       for (int i = 0; i < methods.length; i++)
88       {
89          if (Advisable.isAdvisable(methods[i]))
90          {
91             methods[i].instrument(expr);
92          }
93       }
94
95       CtConstructor[] cons = clazz.getDeclaredConstructors();
96       for (int i = 0; i < cons.length; i++)
97       {
98          cons[i].instrument(expr);
99       }
100
101       return expr.appliedCallerBinding;
102    }
103
104    private boolean isTargetConstructorAdvised(CtConstructor calledConstructor)
105    {
106       try
107       {
108          ClassAdvisor adv = manager.getTempClassAdvisor(calledConstructor.getDeclaringClass());
109          return ConstructorExecutionTransformer.isAdvisableConstructor(calledConstructor, adv);
110       }
111       catch (Exception JavaDoc e)
112       {
113          throw new RuntimeException JavaDoc(e);
114       }
115    }
116       
117
118    private static String JavaDoc getHashString(long hash)
119    {
120       if (hash < 0)
121       {
122          return "_N_" + (-1 * hash);
123       }
124       else
125       {
126          return "_" + hash;
127       }
128    }
129    
130    protected static String JavaDoc getUniqueInvocationFieldname(long callingHash, String JavaDoc classname, long calledHash)
131    {
132       classname = classname.replace('.', '_');
133       classname = classname.replace('/', '_');
134
135       return getHashString(callingHash) + classname + getHashString(calledHash);
136    }
137
138    protected static String JavaDoc getConByConInfoName(long callingIndex, String JavaDoc classname, long calledHash)
139    {
140       return "aop$constructorCall_con_" + getUniqueInvocationFieldname(callingIndex, classname, calledHash);
141    }
142
143    protected static String JavaDoc getConByMethodInfoName(long callingHash, String JavaDoc classname, long calledHash)
144    {
145       return "aop$constructorCall_" + getUniqueInvocationFieldname(callingHash, classname, calledHash);
146    }
147    
148    protected static String JavaDoc getMethodByConInfoName(int index, String JavaDoc classname, long calledHash)
149    {
150       return "aop$methodCall_con_" + getUniqueInvocationFieldname(index, classname, calledHash);
151    }
152    
153    protected static String JavaDoc getMethodByMethodInfoName(long callingHash, String JavaDoc classname, long calledHash)
154    {
155       return "aop$methodCall_" + getUniqueInvocationFieldname(callingHash, classname, calledHash);
156    }
157
158    protected static String JavaDoc conByConInfoFromWeakReference(String JavaDoc localName, String JavaDoc infoName)
159    {
160       return TransformerCommon.infoFromWeakReference(CON_BY_CON_INFO_CLASS_NAME, localName, infoName);
161    }
162
163    protected static String JavaDoc conByMethodInfoFromWeakReference(String JavaDoc localName, String JavaDoc infoName)
164    {
165       return TransformerCommon.infoFromWeakReference(CON_BY_METHOD_INFO_CLASS_NAME, localName, infoName);
166    }
167
168    protected static String JavaDoc methodByMethodInfoFromWeakReference(String JavaDoc localName, String JavaDoc infoName)
169    {
170       return TransformerCommon.infoFromWeakReference(METHOD_BY_METHOD_INFO_CLASS_NAME, localName, infoName);
171    }
172
173    protected static String JavaDoc methodByConInfoFromWeakReference(String JavaDoc localName, String JavaDoc infoName)
174    {
175       return TransformerCommon.infoFromWeakReference(METHOD_BY_CON_INFO_CLASS_NAME, localName, infoName);
176    }
177
178    
179    protected class ConstructorDetail
180    {
181       MethodCall call;
182       CtConstructor con;
183       int callingIndex;
184       long calledHash;
185       String JavaDoc callerInfoField;
186       CtMethod calledMethod;
187       String JavaDoc classname;
188       
189       ConstructorDetail(CallerExprEditor editor, MethodCall call, String JavaDoc classname)throws NotFoundException
190       {
191          this.call = call;
192          con = (CtConstructor) call.where();
193          callingIndex = editor.constructors.indexOf(con);
194          calledHash = JavassistMethodHashing.methodHash(call.getMethod());
195          callerInfoField = getMethodByConInfoName(callingIndex, classname, calledHash);
196          calledMethod = call.getMethod();
197          this.classname = classname;
198       }
199    }
200
201    protected class MethodDetail
202    {
203       MethodCall call;
204       CtMethod where;
205       long callingHash;
206       long calledHash;
207       String JavaDoc callerInfoField;
208       CtMethod calledMethod;
209       String JavaDoc classname;
210       
211       MethodDetail(CallerExprEditor editor, MethodCall call, String JavaDoc classname) throws NotFoundException
212       {
213          this.call = call;
214          where = (CtMethod) call.where();
215          callingHash = JavassistMethodHashing.methodHash((where));
216          calledHash = JavassistMethodHashing.methodHash(call.getMethod());
217          callerInfoField = getMethodByMethodInfoName(callingHash, classname, calledHash);
218          calledMethod = call.getMethod();
219          
220          this.classname = classname;
221       }
222    }
223
224    protected class ConByMethodDetail
225    {
226       NewExpr call;
227       boolean isTgtConAdvised;
228       CtMethod where;
229       long callingHash;
230       long calledHash;
231       String JavaDoc callerInfoField;
232       CtConstructor calledConstructor;
233       String JavaDoc classname;
234    
235       ConByMethodDetail(CallerExprEditor editor, NewExpr call, String JavaDoc classname)throws NotFoundException
236       {
237          this.call = call;
238          where = (CtMethod) call.where();
239          callingHash = JavassistMethodHashing.methodHash(where);
240          calledHash = JavassistMethodHashing.constructorHash(call.getConstructor());
241          callerInfoField = getConByMethodInfoName(callingHash, classname, calledHash);
242          calledConstructor = call.getConstructor();
243          this.classname = classname;
244          isTgtConAdvised = isTargetConstructorAdvised(calledConstructor);
245       }
246    }
247
248    protected class ConByConDetail
249    {
250       NewExpr call;
251       boolean isTgtConAdvised;
252       CtConstructor con;
253       int callingIndex;
254       long calledHash;
255       String JavaDoc callerInfoField;
256       CtConstructor calledConstructor;
257       String JavaDoc classname;
258    
259       ConByConDetail(CallerExprEditor editor, NewExpr call, String JavaDoc classname)throws NotFoundException
260       {
261          this.call = call;
262          con = (CtConstructor) call.where();
263          callingIndex = editor.constructors.indexOf(con);
264          calledHash = JavassistMethodHashing.constructorHash(call.getConstructor());
265          callerInfoField = getConByConInfoName(callingIndex, classname, calledHash);
266          calledConstructor = call.getConstructor();
267          this.classname = classname;
268          isTgtConAdvised = isTargetConstructorAdvised(calledConstructor);
269       }
270    }
271
272    abstract class CallerExprEditor extends ExprEditor
273    {
274       CtClass callingClass;
275       ClassAdvisor advisor;
276       List JavaDoc constructors;
277       public boolean appliedCallerBinding = false;
278
279       HashMap JavaDoc callerInfos = new HashMap JavaDoc();
280       int invocationCounter = 0;
281
282       public CallerExprEditor(ClassAdvisor advisor, CtClass callingClass)
283       {
284          this.advisor = advisor;
285          this.callingClass = callingClass;
286          this.constructors = instrumentor.getConstructors(callingClass);
287       }
288
289       private String JavaDoc getUniqueInvocationClassname(String JavaDoc classname)
290       {
291          return classname + "_" + (++invocationCounter) + "_";
292       }
293
294       protected String JavaDoc getOptimizedConCalledByMethodInvocationClassName(long callingHash, String JavaDoc classname, long calledHash)
295       {
296          return getUniqueInvocationClassname(classname) + "ConByMInvocation";
297       }
298
299       protected String JavaDoc getOptimizedConCalledByConInvocationClassName(long callingIndex, String JavaDoc classname, long calledHash)
300       {
301          return getUniqueInvocationClassname(classname) + "ConByConInvocation";
302       }
303
304       protected String JavaDoc getOptimizedMethodCalledByMethodClassName(long callingHash, String JavaDoc classname, long calledHash)
305       {
306          return getUniqueInvocationClassname(classname) + "MByMInvocation";
307       }
308
309       protected String JavaDoc getOptimizedMethodCalledByConstructorClassName(int callingIndex, String JavaDoc classname, long calledHash)
310       {
311          return getUniqueInvocationClassname(classname) + "MByConInvocation";
312       }
313
314       public void edit(MethodCall call) throws CannotCompileException
315       {
316          try
317          {
318             //System.out.println("XXX Call " + call.getMethodName() + " " + call.getMethod().getSignature() + " from " + callingClass.getName() + " - " + call.where().getName());
319

320             String JavaDoc classname = call.getClassName();
321             String JavaDoc methodName = call.getMethodName();
322             if (ClassAdvisor.isWithoutAdvisement(methodName)
323             || methodName.startsWith("_")
324             || classname.startsWith("org.jboss.aop")
325             || call.getMethodName().equals("class$") // todo not sure why this is part of the method call
326
|| !Instrumentor.isTransformable(callingClass)
327             )
328             {
329                //System.out.println("XXX RETURNING");
330
return;
331             }
332
333             CtBehavior behavior = call.where();
334
335             boolean hasPointcut = false;
336
337             DeclareChecker.checkDeclares(manager, call, advisor);
338             
339             // todo shouldn't iterate every time. must be a better way
340
Iterator JavaDoc it = manager.getPointcuts().values().iterator();
341             while (it.hasNext())
342             {
343                Pointcut p = (Pointcut) it.next();
344                if (p.matchesCall(advisor, call))
345                {
346                   hasPointcut = true;
347                   break;
348                }
349                else
350                {
351                   if (AspectManager.verbose) System.out.println("[debug] MethodCall does not match: " + p.getExpr());
352                }
353             }
354             if (hasPointcut)
355             {
356                if (behavior instanceof CtMethod)
357                   modifyMethod(call, classname);
358                else if (behavior instanceof CtConstructor) modifyConstructor(call, classname);
359             }
360          }
361          catch (Exception JavaDoc ex)
362          {
363             System.err.println("error getting:" + call.getClassName() + ". '" + call.getMethodName() + "'");
364             ex.printStackTrace();
365             throw new CannotCompileException(ex);
366          }
367       }
368
369       
370       private void modifyConstructor(MethodCall call, String JavaDoc classname) throws NotFoundException, CannotCompileException
371       {
372          instrumentor.setupBasics(callingClass);
373          ConstructorDetail cd = new ConstructorDetail(this, call, classname);
374          setupConstructor(cd);
375          replaceMethodCallInCon(cd);
376          appliedCallerBinding = true;
377       }
378       
379       /** Replaces constructor call with standard non optimized invocation code. Up to subclasses to override this behaviour
380        */

381       protected void replaceMethodCallInCon(ConstructorDetail cd)throws CannotCompileException, NotFoundException
382       {
383          String JavaDoc replaced =
384             methodByConInfoFromWeakReference("info", cd.callerInfoField) +
385             "if (info.getInterceptors() != (org.jboss.aop.advice.Interceptor[])null) { " +
386             "$_ = ($r)aop$classAdvisor$aop.invokeConstructorCaller(info, $0, $args);" +
387             //+ cd.callingIndex + ", $0, $args, " + cd.callerInfoField + "); " +
388
"} else { " +
389             "$_ = $proceed($$); " +
390             "}";
391             cd.call.replace(replaced);
392       }
393
394       private void modifyMethod(MethodCall call, String JavaDoc classname) throws NotFoundException, CannotCompileException
395       {
396          instrumentor.setupBasics(callingClass);
397          MethodDetail md = new MethodDetail(this, call, classname);
398          setupMethod(md);
399          replaceMethodCallInMethod(md);
400          appliedCallerBinding = true;
401       }
402
403       /** Replaces method call with standard non optimized invocation code. Up to subclasses to override this behaviour
404        */

405       protected void replaceMethodCallInMethod(MethodDetail md)throws NotFoundException, CannotCompileException
406       {
407          String JavaDoc callingObject = ", null";
408          if (!Modifier.isStatic(md.where.getModifiers()))
409          {
410             callingObject = ", this";
411          }
412          String JavaDoc replaced =
413             methodByMethodInfoFromWeakReference("info", md.callerInfoField) +
414             "if (info.getInterceptors() != (org.jboss.aop.advice.Interceptor[])null) { " +
415             "$_ = ($r)aop$classAdvisor$aop.invokeCaller(info" + callingObject + ", $0, $args);" +
416             //+ md.callingHash + "L, $0, $args, " + md.callerInfoField + callingObject + "); " +
417
"} else { " +
418             "$_ = $proceed($$); " +
419             "}";
420             md.call.replace(replaced);
421       }
422       
423       
424       public void edit(NewExpr call) throws CannotCompileException
425       {
426          try
427          {
428             String JavaDoc classname = call.getClassName();
429             if (classname.startsWith("org.jboss.aop") || !Instrumentor.isTransformable(callingClass))
430             {
431                return;
432             }
433             
434             DeclareChecker.checkDeclares(manager, call, advisor);
435             
436             boolean hasPointcut = false;
437
438             // todo shouldn't iterate every time. must be a better way
439
Iterator JavaDoc it = manager.getPointcuts().values().iterator();
440             while (it.hasNext())
441             {
442                Pointcut p = (Pointcut) it.next();
443                if (p.matchesCall(advisor, call))
444                {
445                   hasPointcut = true;
446                   break;
447                }
448             }
449             if (hasPointcut)
450             {
451                CtBehavior behavior = call.where();
452                if (behavior instanceof CtMethod)
453                   modifyMethod(call, classname);
454                else if (behavior instanceof CtConstructor) modifyConstructor(call, classname);
455             }
456          }
457          catch (Exception JavaDoc ex)
458          {
459             System.out.println(ex.getMessage());
460             ex.printStackTrace();
461             throw new CannotCompileException(ex);
462          }
463       }
464
465       private void modifyMethod(NewExpr call, String JavaDoc classname) throws Exception JavaDoc, NotFoundException, CannotCompileException
466       {
467          instrumentor.setupBasics(callingClass);
468          ConByMethodDetail cd = new ConByMethodDetail(this, call, classname);
469          setupMethod(cd);
470          replaceConCallInMethod(cd);
471          appliedCallerBinding = true;
472       }
473
474       /** Replaces constructor call with standard non optimized invocation code. Up to subclasses to override this behaviour
475        */

476       protected void replaceConCallInMethod(ConByMethodDetail cd) throws NotFoundException, CannotCompileException
477       {
478          String JavaDoc callingObject = "null";
479          if (!Modifier.isStatic(cd.where.getModifiers()))
480          {
481             callingObject = "this";
482          }
483
484          String JavaDoc replaced =
485             conByMethodInfoFromWeakReference("info", cd.callerInfoField) +
486             "if (info.getInterceptors() != (org.jboss.aop.advice.Interceptor[])null) { " +
487             "java.lang.Object callingObject = " + callingObject + "; " +
488             "$_ = ($r)aop$classAdvisor$aop.invokeConCalledByMethod(info, " + callingObject + ", $args);" +
489             //+ cd.callingHash + "L, $args, " + cd.callerInfoField + ", callingObject); " +
490
"} else { " +
491             "$_ = $proceed($$); " +
492             "}";
493
494             cd.call.replace(replaced);
495       }
496
497       private void modifyConstructor(NewExpr call, String JavaDoc classname) throws Exception JavaDoc, NotFoundException, CannotCompileException
498       {
499          instrumentor.setupBasics(callingClass);
500          ConByConDetail cd = new ConByConDetail(this, call, classname);
501          setupConstructor(cd);
502          replaceConCallInCon(cd);
503          appliedCallerBinding = true;
504       }
505
506       /** Replaces constructor call with standard non optimized invocation code. Up to subclasses to override this behaviour
507        */

508       protected void replaceConCallInCon(ConByConDetail cd)throws CannotCompileException, NotFoundException
509       {
510          String JavaDoc replaced =
511             conByConInfoFromWeakReference("info", cd.callerInfoField) +
512             "if (info.getInterceptors() != (org.jboss.aop.advice.Interceptor[])null) { " +
513             "$_ = ($r)aop$classAdvisor$aop.invokeConCalledByCon(info, $args);" +
514             //+ cd.callingIndex + ", $args, " + cd.callerInfoField + "); " +
515
"} else { " +
516             "$_ = $proceed($$); " +
517             "}";
518
519             cd.call.replace(replaced);
520       }
521       
522       protected abstract void setupConstructor(ConstructorDetail cd)throws NotFoundException, CannotCompileException;
523       protected abstract void setupMethod(MethodDetail md) throws NotFoundException, CannotCompileException;
524       protected abstract void setupMethod(ConByMethodDetail cd) throws NotFoundException, CannotCompileException;
525       protected abstract void setupConstructor(ConByConDetail cd)throws NotFoundException, CannotCompileException;
526    
527    }
528 }
529
Popular Tags