View Javadoc

1   /*************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.annotation;
9   
10  import org.codehaus.aspectwerkz.definition.AspectDefinition;
11  import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
12  import org.codehaus.aspectwerkz.definition.SystemDefinition;
13  import org.codehaus.aspectwerkz.definition.AdviceDefinition;
14  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
15  import org.codehaus.aspectwerkz.exception.DefinitionException;
16  import org.codehaus.aspectwerkz.transform.ReflectHelper;
17  import org.codehaus.aspectwerkz.aspect.AdviceType;
18  import org.codehaus.aspectwerkz.reflect.ClassInfo;
19  import org.codehaus.aspectwerkz.reflect.FieldInfo;
20  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
21  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotations;
22  
23  import java.lang.reflect.Field;
24  import java.lang.reflect.Method;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  /***
29   * Extracts the aspects annotations from the class files and creates a meta-data representation of them.
30   * <br/>
31   * Note: we are not using reflection to loop over fields, etc, so that we do not trigger nested loading, which could be
32   * potential target classes.
33   *
34   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
35   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
36   */
37  public class AspectAnnotationParser {
38  
39      /***
40       * Singleton is enough
41       */
42      private final static AspectAnnotationParser s_singleton = new AspectAnnotationParser();
43  
44      /***
45       * Private constructor to enforce singleton
46       */
47      private AspectAnnotationParser() {
48      }
49  
50      /***
51       * Parse the attributes and create and return a meta-data representation of them.
52       *
53       * @param klass      the class to extract attributes from
54       * @param aspectDef  the aspect definition
55       * @param definition the aspectwerkz definition
56       */
57      public static void parse(final Class klass, final AspectDefinition aspectDef, final SystemDefinition definition) {
58          s_singleton.parse0(klass, aspectDef, definition);
59      }
60  
61      /***
62       * Parse the attributes and create and return a meta-data representation of them.
63       *
64       * @param klass      the class to extract attributes from
65       * @param aspectDef  the aspect definition
66       * @param definition the aspectwerkz definition
67       */
68      private void parse0(final Class klass, final AspectDefinition aspectDef, final SystemDefinition definition) {
69          if (klass == null) {
70              throw new IllegalArgumentException("class to parse can not be null");
71          }
72  
73          // grab the classInfo now, and enfore non lazy gathering of annotations
74          ClassInfo classInfo = AsmClassInfo.getClassInfo(klass.getName(), klass.getClassLoader(), false);
75  
76          AspectAnnotationProxy aspectAnnotation = (AspectAnnotationProxy) Annotations.getAnnotation(
77                  AnnotationC.ANNOTATION_ASPECT,
78                  klass
79          );
80          if (aspectAnnotation == null) {
81              // fall back on using the class name as aspect name and let the deployment model be
82              // perJVM
83              aspectAnnotation = new AspectAnnotationProxy();
84              aspectAnnotation.setAspectName(klass.getName());
85          }
86  
87          // attribute settings override the xml settings
88          aspectDef.setDeploymentModel(aspectAnnotation.deploymentModel());
89          String className = klass.getName();
90          String aspectName = aspectAnnotation.aspectName();
91          parseFieldAttributes(klass, aspectDef);
92          parseMethodAttributes(klass, className, aspectName, aspectDef);
93          parseClassAttributes(klass, aspectDef);
94      }
95  
96      /***
97       * Parses the field attributes and creates a meta-data representation of them.
98       *
99       * @param klass     the class to extract attributes from
100      * @param aspectDef the aspect definition
101      */
102     private void parseFieldAttributes(final Class klass, AspectDefinition aspectDef) {
103         if (aspectDef == null) {
104             throw new IllegalArgumentException("aspect definition can not be null");
105         }
106         if (klass == null) {
107             return;
108         }
109 
110         // use AsmClassInfo to loop over fields, to avoid nested loading of potential target classes
111         ClassInfo classInfo = AsmClassInfo.getClassInfo(klass.getName(), klass.getClassLoader());
112 
113         FieldInfo[] fieldList = classInfo.getFields();
114         for (int i = 0; i < fieldList.length; i++) {
115             FieldInfo field = fieldList[i];
116             for (Iterator iterator = field.getAnnotations().iterator(); iterator.hasNext();) {
117                 AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();
118                 if (annotationInfo.getAnnotation() == null) {
119                     continue;
120                 }
121                 if (AnnotationC.ANNOTATION_EXPRESSION.equals(annotationInfo.getName())) {
122                     DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
123                             field.getName(),
124                             ((ExpressionAnnotationProxy)annotationInfo.getAnnotation()).expression(),
125                             aspectDef
126                     );
127                 }
128                 else if (AnnotationC.ANNOTATION_IMPLEMENTS.equals(annotationInfo.getName())) {
129                     DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef(
130                             ((ImplementsAnnotationProxy)annotationInfo.getAnnotation()).expression(),
131                             field.getName(),
132                             field.getType().getName(),
133                             aspectDef
134                     );
135                 }
136             }
137 
138 //            List expressionAnnotations = AsmAnnotations.getAnnotations(AnnotationC.ANNOTATION_EXPRESSION, field);
139 //            for (Iterator iterator = expressionAnnotations.iterator(); iterator.hasNext();) {
140 //                ExpressionAnnotationProxy annotation = (ExpressionAnnotationProxy) iterator.next();
141 //                if (annotation != null) {
142 //                    DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
143 //                            field.getName(),
144 //                            annotation.expression(),
145 //                            aspectDef
146 //                    );
147 //                }
148 //            }
149 //            List implementsAnnotations = AsmAnnotations.getAnnotations(AnnotationC.ANNOTATION_IMPLEMENTS, field);
150 //            for (Iterator iterator = implementsAnnotations.iterator(); iterator.hasNext();) {
151 //                ImplementsAnnotationProxy annotation = (ImplementsAnnotationProxy) iterator.next();
152 //                if (annotation != null) {
153 //                    DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef(
154 //                            annotation.expression(),
155 //                            field.getName(),
156 //                            field.getType().getName(),
157 //                            aspectDef
158 //                    );
159 //                }
160 //            }
161         }
162 
163         // recursive call, next iteration based on super class
164         parseFieldAttributes(klass.getSuperclass(), aspectDef);
165     }
166 
167     /***
168      * Parses the method attributes and creates a meta-data representation of them.
169      *
170      * @param klass           the class
171      * @param aspectClassName the aspect class name
172      * @param aspectName      the aspect name
173      * @param aspectDef       the aspect definition
174      */
175     private void parseMethodAttributes(final Class klass,
176                                        final String aspectClassName,
177                                        final String aspectName,
178                                        final AspectDefinition aspectDef) {
179 
180         if (klass == null) {
181             throw new IllegalArgumentException("class can not be null");
182         }
183         if (aspectClassName == null) {
184             throw new IllegalArgumentException("aspect class name can not be null");
185         }
186         if (aspectName == null) {
187             throw new IllegalArgumentException("aspect name can not be null " + aspectClassName);
188         }
189         if (aspectDef == null) {
190             throw new IllegalArgumentException("aspect definition can not be null");
191         }
192 
193         List methodList = ReflectHelper.createCompleteSortedMethodList(klass);
194 
195         // iterate first on all method to lookup @Expression Pointcut annotations so that they can be resolved
196         int methodIndex = 0;
197         for (Iterator it = methodList.iterator(); it.hasNext(); methodIndex++) {
198             Method method = (Method) it.next();
199 
200             // Pointcut with signature
201             List expressionAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_EXPRESSION, method);
202             for (Iterator iterator = expressionAnnotations.iterator(); iterator.hasNext();) {
203                 ExpressionAnnotationProxy annotation = (ExpressionAnnotationProxy) iterator.next();
204                 if (annotation != null) {
205 
206                     DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
207                             AspectAnnotationParser
208                             .getMethodPointcutCallSignature(method.getName(), annotation), annotation.expression(), aspectDef
209                     );
210                 }
211             }
212         }
213 
214         // iterate on other annotations
215         methodIndex = 0;
216         for (Iterator it = methodList.iterator(); it.hasNext(); methodIndex++) {
217             Method method = (Method) it.next();
218 
219             try {
220                 // create the advice name out of the class and method name, <classname>.<methodname>
221                 List aroundAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_AROUND, method);
222                 for (Iterator iterator = aroundAnnotations.iterator(); iterator.hasNext();) {
223                     AroundAnnotationProxy aroundAnnotation = (AroundAnnotationProxy) iterator.next();
224                     if (aroundAnnotation != null) {
225                         final String expression = aroundAnnotation.pointcut();
226                         final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
227                                 method.getName(), aroundAnnotation
228                         );
229                         AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
230                                 adviceName,
231                                 aroundAnnotation.getType(),
232                                 expression,
233                                 null,
234                                 aspectName,
235                                 aspectClassName,
236                                 method,
237                                 methodIndex,
238                                 aspectDef
239                         );
240                         aspectDef.addAroundAdvice(adviceDef);
241                     }
242                 }
243                 List beforeAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_BEFORE, method);
244                 for (Iterator iterator = beforeAnnotations.iterator(); iterator.hasNext();) {
245                     BeforeAnnotationProxy beforeAnnotation = (BeforeAnnotationProxy) iterator.next();
246                     if (beforeAnnotation != null) {
247                         final String expression = beforeAnnotation.pointcut();
248                         final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
249                                 method.getName(), beforeAnnotation
250                         );
251                         AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
252                                 adviceName,
253                                 beforeAnnotation.getType(),
254                                 expression,
255                                 null,
256                                 aspectName,
257                                 aspectClassName,
258                                 method,
259                                 methodIndex,
260                                 aspectDef
261                         );
262                         aspectDef.addBeforeAdvice(adviceDef);
263                     }
264                 }
265                 List afterAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_AFTER, method);
266                 for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
267                     AfterAnnotationProxy afterAnnotation = (AfterAnnotationProxy) iterator.next();
268                     if (afterAnnotation != null) {
269                         final String expression = afterAnnotation.pointcut();
270                         final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
271                                 method.getName(), afterAnnotation
272                         );
273                         AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
274                                 adviceName,
275                                 afterAnnotation.getType(),
276                                 expression,
277                                 afterAnnotation.getSpecialArgumentType(),
278                                 aspectName,
279                                 aspectClassName,
280                                 method,
281                                 methodIndex,
282                                 aspectDef
283                         );
284                         aspectDef.addAfterAdvice(adviceDef);
285                     }
286                 }
287             } catch (DefinitionException e) {
288                 System.err.println("WARNING: unable to register advice: " + e.getMessage());
289                 // TODO AV - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE
290             }
291         }
292     }
293 
294     /***
295      * Looks for "@Introduce IntroduceAttribute" defined at aspect inner class level
296      *
297      * @param klass     of aspect
298      * @param aspectDef
299      */
300     private void parseClassAttributes(final Class klass, AspectDefinition aspectDef) {
301         if (klass == null) {
302             throw new IllegalArgumentException("class can not be null");
303         }
304         List annotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_INTRODUCE, klass);
305         for (Iterator iterator = annotations.iterator(); iterator.hasNext();) {
306             IntroduceAnnotationProxy annotation = (IntroduceAnnotationProxy) iterator.next();
307             if (annotation != null) {
308                 Class mixin;
309                 try {
310                     mixin = klass.getClassLoader().loadClass(annotation.innerClassName());
311                 } catch (ClassNotFoundException e) {
312                     throw new WrappedRuntimeException(e);
313                 }
314                 DefinitionParserHelper.createAndAddIntroductionDefToAspectDef(
315                         mixin,
316                         annotation.expression(),
317                         annotation.deploymentModel(),
318                         aspectDef
319                 );
320             }
321         }
322     }
323 
324     /***
325      * Returns the call signature of a Pointcut or advice with signature methodName(paramType paramName, ...) [we ignore
326      * the return type] If there is no parameters, the call signature is not "name()" but just "name"
327      *
328      * @param methodName
329      * @return annotationProxy that contains the ordered map of call parameters
330      */
331     private static String getMethodPointcutCallSignature(final String methodName,
332                                                          final ParameterizedAnnotationProxy annotationProxy) {
333         StringBuffer buffer = new StringBuffer(methodName);
334         if (annotationProxy.getArgumentNames().size() > 0) {
335             buffer.append('(');
336             for (Iterator it = annotationProxy.getArgumentNames().iterator(); it.hasNext();) {
337                 String parameter = (String) it.next();
338                 buffer.append(annotationProxy.getArgumentType(parameter));
339                 buffer.append(' ').append(parameter);
340                 if (it.hasNext()) {
341                     buffer.append(", ");
342                 }
343             }
344             buffer.append(')');
345         }
346         return buffer.toString();
347     }
348 
349 }