View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.transform.delegation;
9   
10  import org.codehaus.aspectwerkz.definition.SystemDefinition;
11  import org.codehaus.aspectwerkz.expression.ExpressionContext;
12  import org.codehaus.aspectwerkz.expression.PointcutType;
13  import org.codehaus.aspectwerkz.reflect.ClassInfo;
14  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
15  import org.codehaus.aspectwerkz.reflect.MemberInfo;
16  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfo;
17  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistConstructorInfo;
18  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistMethodInfo;
19  import org.codehaus.aspectwerkz.transform.Context;
20  import org.codehaus.aspectwerkz.transform.TransformationUtil;
21  import org.codehaus.aspectwerkz.transform.Transformer;
22  import org.codehaus.aspectwerkz.transform.TransformationConstants;
23  import org.codehaus.aspectwerkz.transform.TransformationConstants;
24  
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import javassist.CannotCompileException;
29  import javassist.CtBehavior;
30  import javassist.CtClass;
31  import javassist.CtConstructor;
32  import javassist.CtField;
33  import javassist.CtMethod;
34  import javassist.Modifier;
35  import javassist.NotFoundException;
36  import javassist.expr.ExprEditor;
37  import javassist.expr.NewExpr;
38  
39  /***
40   * Advises constructor CALL join points.
41   * 
42   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
43   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
44   */
45  public class ConstructorCallTransformer implements Transformer {
46      /***
47       * The join point index.
48       */
49  
50      //AXprivate int m_joinPointIndex;
51      /***
52       * Transforms the call side pointcuts.
53       * 
54       * @param context the transformation context
55       * @param klass the class set.
56       */
57      public void transform(final Context context, final Klass klass) throws NotFoundException, CannotCompileException {
58          List definitions = context.getDefinitions();
59  
60          //AXm_joinPointIndex =
61          // TransformationUtil.getJoinPointIndex(klass.getCtClass()); //TODO is
62          // not thread safe / reentrant
63          for (Iterator it = definitions.iterator(); it.hasNext();) {
64              final SystemDefinition definition = (SystemDefinition) it.next();
65              final CtClass ctClass = klass.getCtClass();
66              ClassInfo classInfo = JavassistClassInfo.getClassInfo(ctClass, context.getLoader());
67              if (classFilter(definition, new ExpressionContext(PointcutType.CALL, null, classInfo), ctClass)) {
68                  continue;
69              }
70              ctClass.instrument(new ExprEditor() {
71                  public void edit(NewExpr newExpr) throws CannotCompileException {
72                      try {
73                          CtBehavior where = null;
74                          try {
75                              where = newExpr.where();
76                          } catch (RuntimeException e) {
77                              // <clinit> access leads to a bug in Javassist
78                              where = ctClass.getClassInitializer();
79                          }
80  
81                          // filter caller methods
82                          if (methodFilterCaller(where)) {
83                              return;
84                          }
85                          CtConstructor ctConstructor = newExpr.getConstructor();
86                          String calleeClassName = newExpr.getClassName();
87  
88                          // filter callee classes
89                          if (!definition.inIncludePackage(calleeClassName)) {
90                              return;
91                          }
92  
93                          // filter the constructors
94                          if (constructorFilter(ctConstructor)) {
95                              return;
96                          }
97  
98                          // create the caller method info
99                          MemberInfo withinMethodInfo = null;
100                         if (where instanceof CtMethod) {
101                             withinMethodInfo = JavassistMethodInfo.getMethodInfo((CtMethod) where, context.getLoader());
102                         } else if (where instanceof CtConstructor) {
103                             withinMethodInfo = JavassistConstructorInfo.getConstructorInfo(
104                                 (CtConstructor) where,
105                                 context.getLoader());
106                         }
107 
108                         // create the constructor info
109                         CtConstructor constructor = newExpr.getConstructor();
110                         ConstructorInfo calleeSideConstructorInfo = JavassistConstructorInfo.getConstructorInfo(
111                             constructor,
112                             context.getLoader());
113                         ExpressionContext ctx = new ExpressionContext(
114                             PointcutType.CALL,
115                             calleeSideConstructorInfo,
116                             withinMethodInfo);
117 
118                         // is this a caller side method pointcut?
119                         if (definition.hasPointcut(ctx)) {
120                             // check the callee class is not the same as target
121                             // class, if that is the case
122                             // then we have have class loaded and set in the
123                             // ___AW_clazz already
124                             String declaringClassMethodName = TransformationConstants.STATIC_CLASS_FIELD;
125                             CtClass declaringClass = ctConstructor.getDeclaringClass();
126                             if (!declaringClass.getName().replace('/', '.').equals(
127                                 where.getDeclaringClass().getName().replace('/', '.'))) {
128                                 declaringClassMethodName = addCalleeMethodDeclaringClassField(ctClass, ctConstructor);
129                             }
130 
131                             // call the wrapper method instead of the callee
132                             // method
133                             StringBuffer body = new StringBuffer();
134                             body.append('{');
135                             if (ctConstructor.getParameterTypes().length > 0) {
136                                 body.append("Object[] args = $args; ");
137                             } else {
138                                 body.append("Object[] args = null; ");
139                             }
140                             body.append("Class declaringClass = ");
141                             body.append(declaringClassMethodName);
142                             body.append("; ");
143                             if (Modifier.isStatic(where.getModifiers())) {
144                                 body.append("Object nullObject = null;");
145                             }
146                             body.append("$_ = ($r)");
147                             body.append(TransformationConstants.JOIN_POINT_MANAGER_FIELD);
148                             body.append('.');
149                             body.append(TransformationConstants.PROCEED_WITH_CALL_JOIN_POINT_METHOD);
150                             body.append('(');
151                             body.append(JavassistHelper.calculateHash(ctConstructor));
152                             body.append(',');
153                             body.append(klass.getJoinPointIndex());
154                             body.append(", args, ");
155                             body.append(TransformationConstants.STATIC_CLASS_FIELD);
156                             if (Modifier.isStatic(where.getModifiers())) {
157                                 body.append(", nullObject, ");
158                             } else {
159                                 body.append(", this, ");
160                             }
161                             body.append("declaringClass, $0, \"");
162                             body.append(where.getName());
163                             body.append("\",\"");
164                             body.append(where.getSignature());
165                             body.append("\",");
166                             body.append(TransformationConstants.JOIN_POINT_TYPE_CONSTRUCTOR_CALL);
167                             body.append("); }");
168                             newExpr.replace(body.toString());
169                             context.markAsAdvised();
170                             klass.incrementJoinPointIndex();
171                         }
172                     } catch (NotFoundException nfe) {
173                         nfe.printStackTrace();
174 
175                         // TODO: should we swallow this exception?
176                     }
177                 }
178             });
179         }
180 
181         //TransformationUtil.setJoinPointIndex(klass.getCtClass(),
182         // m_joinPointIndex);
183         klass.flushJoinPointIndex();
184     }
185 
186     /***
187      * Creates a new static class field, for the declaring class of the constructor.
188      * 
189      * @param ctClass the class
190      * @param ctConstructor the constructor
191      * @return the name of the field
192      */
193     private String addCalleeMethodDeclaringClassField(final CtClass ctClass, final CtConstructor ctConstructor) throws NotFoundException,
194             CannotCompileException {
195         String fieldName = TransformationConstants.STATIC_CLASS_FIELD
196             + TransformationConstants.DELIMITER
197             + "init"
198             + TransformationConstants.DELIMITER
199             + ctConstructor.getDeclaringClass().getName().replace('.', '_');
200         boolean hasField = false;
201         CtField[] fields = ctClass.getDeclaredFields();
202         for (int i = 0; i < fields.length; i++) {
203             CtField field = fields[i];
204             if (field.getName().equals(fieldName)) {
205                 hasField = true;
206                 break;
207             }
208         }
209         if (!hasField) {
210             CtField field = new CtField(ctClass.getClassPool().get("java.lang.Class"), fieldName, ctClass);
211             field.setModifiers(Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL);
212             ctClass.addField(field, "java.lang.Class#forName(\""
213                 + ctConstructor.getDeclaringClass().getName().replace('/', '.')
214                 + "\")");
215         }
216         return fieldName;
217     }
218 
219     /***
220      * Filters the classes to be transformed.
221      * 
222      * @param definition
223      * @param ctx the context
224      * @param cg the class to filter
225      * @return boolean true if the method should be filtered away
226      */
227     public static boolean classFilter(final SystemDefinition definition, final ExpressionContext ctx, final CtClass cg) {
228         if (cg.isInterface()) {
229             return true;
230         }
231         String className = cg.getName().replace('/', '.');
232         if (definition.inExcludePackage(className)) {
233             return true;
234         }
235         if (!definition.inIncludePackage(className)) {
236             return true;
237         }
238         if (definition.isAdvised(ctx)) {
239             return false;
240         }
241         return true;
242     }
243 
244     /***
245      * Filters the caller methods.
246      * 
247      * @param method the method to filter
248      * @return boolean true if the method should be filtered away
249      */
250     public static boolean methodFilterCaller(final CtBehavior method) {
251         if (Modifier.isNative(method.getModifiers())
252             || Modifier.isInterface(method.getModifiers())
253             || method.getName().equals(TransformationConstants.CLASS_LOOKUP_METHOD)) {
254             return true;
255         } else {
256             return false;
257         }
258     }
259 
260     /***
261      * Filters the constructor.
262      * 
263      * @param constructor the name of method to filter
264      * @return boolean true if the method should be filtered away
265      */
266     public static boolean constructorFilter(final CtConstructor constructor) {
267         if (constructor.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
268             return true;
269         } else {
270             return false;
271         }
272     }
273 }