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.transform.Context;
12  import org.codehaus.aspectwerkz.transform.TransformationUtil;
13  import org.codehaus.aspectwerkz.transform.Transformer;
14  import org.codehaus.aspectwerkz.transform.TransformationConstants;
15  
16  import java.util.ArrayList;
17  import java.util.HashMap;
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javassist.CannotCompileException;
23  import javassist.CtClass;
24  import javassist.CtMethod;
25  import javassist.CtNewMethod;
26  import javassist.Modifier;
27  import javassist.NotFoundException;
28  
29  /***
30   * Prepare class for further hotswap for execution pointcut TODO support for constructor pointcuts TODO AOPC def model
31   * 
32   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
33   */
34  public class PrepareTransformer implements Transformer {
35      /***
36       * Add the class static field, the joinpoint manager, and add method stubs
37       * 
38       * @param context the transformation context
39       * @param klass the class set.
40       */
41      public void transform(final Context context, final Klass klass) throws NotFoundException, CannotCompileException {
42          List definitions = context.getDefinitions();
43  
44          // loop over all the definitions
45          for (Iterator it = definitions.iterator(); it.hasNext();) {
46              SystemDefinition definition = (SystemDefinition) it.next();
47              final CtClass ctClass = klass.getCtClass();
48  
49              //            ClassInfo classInfo = new JavassistClassInfo(ctClass,
50              // context.getLoader());
51              // do we need to prepare the class
52              if (classFilter(definition, ctClass)) {
53                  continue;
54              }
55              System.out.println("preparing: " + ctClass.getName());
56  
57              // mark as prepared immediately
58              // to trigger bytecode cache even if class has already some
59              // pointcuts
60              context.markAsPrepared();
61              final CtMethod[] methods = ctClass.getDeclaredMethods();
62  
63              // build the method lookup list
64              final List methodLookupList = new ArrayList();
65              for (int i = 0; i < methods.length; i++) {
66                  if (methodFilter(methods[i])) {
67                      continue;
68                  }
69                  methodLookupList.add(methods[i]);
70              }
71              final Map methodSequences = new HashMap();
72              final List wrapperMethods = new ArrayList();
73              boolean isClassAdvised = false;
74              for (Iterator i = methodLookupList.iterator(); i.hasNext();) {
75                  CtMethod method = (CtMethod) i.next();
76  
77                  // take care of identification of overloaded methods by
78                  // inserting a sequence number
79                  if (methodSequences.containsKey(method.getName())) {
80                      int sequence = ((Integer) methodSequences.get(method.getName())).intValue();
81                      methodSequences.remove(method.getName());
82                      sequence++;
83                      methodSequences.put(method.getName(), new Integer(sequence));
84                  } else {
85                      methodSequences.put(method.getName(), new Integer(1));
86                  }
87                  final int methodSequence = ((Integer) methodSequences.get(method.getName())).intValue();
88                  CtMethod wrapperMethod = createEmptyWrapperMethod(ctClass, method, methodSequence);
89                  if (wrapperMethod != null) {
90                      isClassAdvised = true;
91                      wrapperMethods.add(wrapperMethod);
92                  }
93              }
94              if (isClassAdvised) {
95                  context.markAsAdvised();
96  
97                  // add the wrapper methods
98                  for (Iterator it2 = wrapperMethods.iterator(); it2.hasNext();) {
99                      ctClass.addMethod((CtMethod) it2.next());
100                 }
101             }
102         }
103     }
104 
105     /***
106      * Creates an empty wrapper method to allow HotSwap without schema change
107      * 
108      * @param ctClass the ClassGen
109      * @param originalMethod the current method
110      * @param methodSequence the method hash
111      * @return the wrapper method
112      */
113     private CtMethod createEmptyWrapperMethod(
114         final CtClass ctClass,
115         final CtMethod originalMethod,
116         final int methodSequence) throws NotFoundException, CannotCompileException {
117         String wrapperMethodName = TransformationUtil.getPrefixedOriginalMethodName(
118             originalMethod.getName(),
119             methodSequence,
120             ctClass.getName().replace('/', '.'));
121 
122         // check if methods does not already exists
123         if (JavassistHelper.hasMethod(ctClass, wrapperMethodName)) {
124             return null;
125         }
126 
127         // determine the method access flags (should always be set to protected)
128         int accessFlags = originalMethod.getModifiers();
129         if ((accessFlags & Modifier.PROTECTED) == 0) {
130             // set the protected flag
131             accessFlags |= Modifier.PROTECTED;
132         }
133         if ((accessFlags & Modifier.PRIVATE) != 0) {
134             // clear the private flag
135             accessFlags &= ~Modifier.PRIVATE;
136         }
137         if ((accessFlags & Modifier.PUBLIC) != 0) {
138             // clear the public flag
139             accessFlags &= ~Modifier.PUBLIC;
140         }
141 
142         // add an empty body
143         StringBuffer body = new StringBuffer();
144         if (originalMethod.getReturnType() == CtClass.voidType) {
145             // special handling for void return type leads to cleaner bytecode
146             // generation with Javassist
147             body.append("{}");
148         } else if (!originalMethod.getReturnType().isPrimitive()) {
149             body.append("{ return null;}");
150         } else {
151             body.append("{ return ");
152             body.append(JavassistHelper.getDefaultPrimitiveValue(originalMethod.getReturnType()));
153             body.append("; }");
154         }
155         CtMethod method = null;
156         if (Modifier.isStatic(originalMethod.getModifiers())) {
157             method = JavassistHelper.makeStatic(originalMethod.getReturnType(), wrapperMethodName, originalMethod
158                     .getParameterTypes(), originalMethod.getExceptionTypes(), body.toString(), ctClass);
159         } else {
160             method = CtNewMethod.make(originalMethod.getReturnType(), wrapperMethodName, originalMethod
161                     .getParameterTypes(), originalMethod.getExceptionTypes(), body.toString(), ctClass);
162             method.setModifiers(accessFlags);
163         }
164 
165         // add a method level attribute so that we remember it is an empty
166         // method
167         JavassistHelper.setAnnotatedEmpty(method);
168         return method;
169     }
170 
171     /***
172      * Filters the classes to be transformed. Takes only "prepare" declarations into account
173      * 
174      * @param definition the definition
175      * @param cg the class to filter
176      * @return boolean true if the method should be filtered away
177      */
178     public static boolean classFilter(final SystemDefinition definition, final CtClass cg) {
179         if (cg.isInterface()) {
180             return true;
181         }
182         String className = cg.getName().replace('/', '.');
183         if (definition.inExcludePackage(className)) {
184             return true;
185         }
186         if (!definition.inIncludePackage(className)) {
187             return true;
188         }
189         if (definition.inPreparePackage(className)) {
190             return false;
191         }
192         return true;
193     }
194 
195     /***
196      * Filters the methods to be transformed. Does not check execution pointcuts
197      * 
198      * @param method the method to filter
199      * @return boolean
200      */
201     public static boolean methodFilter(final CtMethod method) {
202         if (Modifier.isAbstract(method.getModifiers())
203             || Modifier.isNative(method.getModifiers())
204             || method.getName().equals("<init>")
205             || method.getName().equals("<clinit>")
206             || method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX)
207             || method.getName().equals(TransformationConstants.CLASS_LOOKUP_METHOD)) {
208             return true;
209         }
210         return false;
211     }
212 }