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.AdviceDefinition;
    13  import org.codehaus.aspectwerkz.definition.DeploymentScope;
    14  import org.codehaus.aspectwerkz.exception.DefinitionException;
    15  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    16  import org.codehaus.aspectwerkz.reflect.FieldInfo;
    17  import org.codehaus.aspectwerkz.reflect.MethodInfo;
    18  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
    19  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotations;
    20  import org.codehaus.aspectwerkz.DeploymentModel;
    21  import org.codehaus.aspectwerkz.util.Strings;
    22  import org.codehaus.aspectwerkz.aspect.AdviceType;
    23
    24  import java.util.Iterator;
    25  import java.util.List;
    26
    27  /***
    28   * Extracts the aspects annotations from the class files and creates a meta-data representation of them.
    29   * <br/>
    30   * Note: we are not using reflection to loop over fields, etc, so that we do not trigger nested loading, which could be
    31   * potential target classes.
    32   *
    33   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    34   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
    35   */
    36  public class AspectAnnotationParser {
    37
    38      /***
    39       * The sole instance.
    40       */
    41      private final static AspectAnnotationParser INSTANCE = new AspectAnnotationParser();
    42
    43      /***
    44       * Private constructor to prevent subclassing.
    45       */
    46      private AspectAnnotationParser() {
    47      }
    48
    49      /***
    50       * Parse the attributes and create and return a meta-data representation of them.
    51       *
    52       * @param classInfo the class to extract attributes from
    53       * @param aspectDef the aspect definition
    54       * @param loader
    55       */
    56      public static void parse(final ClassInfo classInfo, final AspectDefinition aspectDef, final ClassLoader loader) {
    57          INSTANCE.doParse(classInfo, aspectDef, loader);
    58      }
    59
    60      /***
    61       * Parse the attributes and create and return a meta-data representation of them.
    62       *
    63       * @param classInfo the class to extract attributes from
    64       * @param aspectDef the aspect definition
    65       * @param loader
    66       */
    67      private void doParse(final ClassInfo classInfo, final AspectDefinition aspectDef, final ClassLoader loader) {
    68          if (classInfo == null) {
    69              throw new IllegalArgumentException("class to parse can not be null");
    70          }
    71
    72          Aspect aspectAnnotation = (Aspect) AsmAnnotations.getAnnotation(
    73                  AnnotationConstants.ASPECT,
    74                  classInfo
    75          );
    76
    77          String aspectName = classInfo.getName();
    78          String deploymentModelAsString = null;
    79
    80          if (aspectAnnotation != null) {
    81              if (aspectAnnotation.value() != null) {
    82                  //@Aspect(perJVM)
    83                  deploymentModelAsString = aspectAnnotation.value();
    84              } else {
    85                  if (aspectAnnotation.name() != null) {
    86                      //@Aspect(name=..)
    87                      aspectName = aspectAnnotation.name();
    88                  }
    89                  if (aspectAnnotation.deploymentModel() != null) {
    90                      //@Aspect(deploymentModel=..)
    91                      deploymentModelAsString = aspectAnnotation.deploymentModel();
    92                  }
    93              }
    94          }
    95
    96          // attribute settings override the xml settings
    97          aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelFor(deploymentModelAsString));
    98          String className = classInfo.getName();
    99          parseFieldAttributes(classInfo, aspectDef);
    100         parseMethodAttributes(classInfo, className, aspectName, aspectDef);
    101     }
    102
    103     /***
    104      * Parses the field attributes and creates a meta-data representation of them.
    105      *
    106      * @param classInfo the class to extract attributes from
    107      * @param aspectDef the aspect definition
    108      */
    109     private void parseFieldAttributes(final ClassInfo classInfo, final AspectDefinition aspectDef) {
    110         if (aspectDef == null) {
    111             throw new IllegalArgumentException("aspect definition can not be null");
    112         }
    113         if (classInfo == null) {
    114             return;
    115         }
    116
    117         FieldInfo[] fieldList = classInfo.getFields();
    118         for (int i = 0; i < fieldList.length; i++) {
    119             FieldInfo field = fieldList[i];
    120             for (Iterator iterator = field.getAnnotations().iterator(); iterator.hasNext();) {
    121                 AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();
    122                 if (annotationInfo.getAnnotation() == null) {
    123                     continue;
    124                 }
    125                 if (AnnotationConstants.EXPRESSION.equals(annotationInfo.getName())) {
    126                     if (field.getType().getName().equals(DeploymentScope.class.getName())) {
    127                         DefinitionParserHelper.createAndAddDeploymentScopeDef(
    128                                 field.getName(),
    129                                 ((Expression) annotationInfo.getAnnotation()).value(),
    130                                 aspectDef.getSystemDefinition()
    131                         );
    132                     } else {
    133                         DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
    134                                 field.getName(),
    135                                 ((Expression) annotationInfo.getAnnotation()).value(),
    136                                 aspectDef
    137                         );
    138                     }
    139                 } else if (AnnotationConstants.INTRODUCE.equals(annotationInfo.getName())) {
    140                     DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef(
    141                             ((Introduce) annotationInfo.getAnnotation()).value(),
    142                             field.getName(),
    143                             field.getType().getName(),
    144                             aspectDef
    145                     );
    146                 }
    147             }
    148         }
    149
    150         // recursive call, next iteration based on super class
    151         parseFieldAttributes(classInfo.getSuperclass(), aspectDef);
    152     }
    153
    154     /***
    155      * Parses the method attributes and creates a meta-data representation of them.
    156      *
    157      * @param classInfo       the class
    158      * @param aspectClassName the aspect class name
    159      * @param aspectName      the aspect name
    160      * @param aspectDef       the aspect definition
    161      */
    162     private void parseMethodAttributes(final ClassInfo classInfo,
    163                                        final String aspectClassName,
    164                                        final String aspectName,
    165                                        final AspectDefinition aspectDef) {
    166         if (classInfo == null) {
    167             throw new IllegalArgumentException("class can not be null");
    168         }
    169         if (aspectClassName == null) {
    170             throw new IllegalArgumentException("aspect class name can not be null");
    171         }
    172         if (aspectName == null) {
    173             throw new IllegalArgumentException("aspect name can not be null " + aspectClassName);
    174         }
    175         if (aspectDef == null) {
    176             throw new IllegalArgumentException("aspect definition can not be null");
    177         }
    178         // get complete method list (includes inherited ones)
    179         List methodList = ClassInfoHelper.createMethodList(classInfo);
    180
    181         // iterate first on all method to lookup @Expression Pointcut annotations so that they can be resolved
    182         parsePointcutAttributes(methodList, aspectDef);
    183
    184         // iterate on the advice annotations
    185         for (Iterator it = methodList.iterator(); it.hasNext();) {
    186             MethodInfo method = (MethodInfo) it.next();
    187             try {
    188                 // create the advice name out of the class and method name, <classname>.<methodname>
    189                 parseAroundAttributes(method, aspectName, aspectClassName, aspectDef);
    190                 parseBeforeAttributes(method, aspectName, aspectClassName, aspectDef);
    191                 parseAfterAttributes(method, aspectName, aspectClassName, aspectDef);
    192             } catch (DefinitionException e) {
    193                 System.err.println("AW::WARNING - unable to register advice: " + e.toString());
    194                 // TODO AV - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE
    195             }
    196         }
    197     }
    198
    199     /***
    200      * Parses the method pointcut attributes.
    201      *
    202      * @param methodList
    203      * @param aspectDef
    204      */
    205     private void parsePointcutAttributes(final List methodList, final AspectDefinition aspectDef) {
    206         for (Iterator it = methodList.iterator(); it.hasNext();) {
    207             MethodInfo method = (MethodInfo) it.next();
    208
    209             // Pointcut with signature
    210             List expressionAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.EXPRESSION, method);
    211             for (Iterator iterator = expressionAnnotations.iterator(); iterator.hasNext();) {
    212                 Expression annotation = (Expression) iterator.next();
    213                 if (annotation != null) {
    214                     DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
    215                             getAdviceNameAsInSource(method),
    216                             annotation.value(), aspectDef
    217                     );
    218                 }
    219             }
    220         }
    221     }
    222
    223     /***
    224      * Parses the around attributes.
    225      *
    226      * @param method
    227      * @param aspectName
    228      * @param aspectClassName
    229      * @param aspectDef
    230      */
    231     private void parseAroundAttributes(final MethodInfo method,
    232                                        final String aspectName,
    233                                        final String aspectClassName,
    234                                        final AspectDefinition aspectDef) {
    235         List aroundAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.AROUND, method);
    236         for (Iterator iterator = aroundAnnotations.iterator(); iterator.hasNext();) {
    237             Around aroundAnnotation = (Around) iterator.next();
    238             if (aroundAnnotation != null) {
    239                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    240                         getAdviceNameAsInSource(method),
    241                         AdviceType.AROUND,
    242                         aroundAnnotation.value(),
    243                         null,
    244                         aspectName,
    245                         aspectClassName,
    246                         method,
    247                         aspectDef
    248                 );
    249                 aspectDef.addAroundAdviceDefinition(adviceDef);
    250             }
    251         }
    252     }
    253
    254     /***
    255      * Parses the before attributes.
    256      *
    257      * @param method
    258      * @param aspectName
    259      * @param aspectClassName
    260      * @param aspectDef
    261      */
    262     private void parseBeforeAttributes(final MethodInfo method,
    263                                        final String aspectName,
    264                                        final String aspectClassName,
    265                                        final AspectDefinition aspectDef) {
    266         List beforeAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.BEFORE, method);
    267         for (Iterator iterator = beforeAnnotations.iterator(); iterator.hasNext();) {
    268             Before beforeAnnotation = (Before) iterator.next();
    269             if (beforeAnnotation != null) {
    270                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    271                         getAdviceNameAsInSource(method),
    272                         AdviceType.BEFORE,
    273                         beforeAnnotation.value(),
    274                         null,
    275                         aspectName,
    276                         aspectClassName,
    277                         method,
    278                         aspectDef
    279                 );
    280                 aspectDef.addBeforeAdviceDefinition(adviceDef);
    281             }
    282         }
    283     }
    284
    285     /***
    286      * Parses the after attributes.
    287      *
    288      * @param method
    289      * @param aspectName
    290      * @param aspectClassName
    291      * @param aspectDef
    292      */
    293     private void parseAfterAttributes(final MethodInfo method,
    294                                       final String aspectName,
    295                                       final String aspectClassName,
    296                                       final AspectDefinition aspectDef) {
    297         List afterAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.AFTER, method);
    298         for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
    299             After annotation = (After) iterator.next();
    300             if (annotation != null) {
    301                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    302                         getAdviceNameAsInSource(method),
    303                         AdviceType.AFTER,
    304                         annotation.value(),
    305                         null,
    306                         aspectName,
    307                         aspectClassName,
    308                         method,
    309                         aspectDef
    310                 );
    311                 aspectDef.addAfterAdviceDefinition(adviceDef);
    312             }
    313         }
    314         afterAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.AFTER_RETURNING, method);
    315         for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
    316             AfterReturning annotation = (AfterReturning) iterator.next();
    317             if (annotation != null) {
    318                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    319                         getAdviceNameAsInSource(method),
    320                         AdviceType.AFTER_RETURNING,
    321                         getExpressionElseValue(annotation.value(), annotation.pointcut()),
    322                         annotation.type(),
    323                         aspectName,
    324                         aspectClassName,
    325                         method,
    326                         aspectDef
    327                 );
    328                 aspectDef.addAfterAdviceDefinition(adviceDef);
    329             }
    330         }
    331         afterAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.AFTER_THROWING, method);
    332         for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
    333             AfterThrowing annotation = (AfterThrowing) iterator.next();
    334             if (annotation != null) {
    335                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    336                         getAdviceNameAsInSource(method),
    337                         AdviceType.AFTER_THROWING,
    338                         getExpressionElseValue(annotation.value(), annotation.pointcut()),
    339                         annotation.type(),
    340                         aspectName,
    341                         aspectClassName,
    342                         method,
    343                         aspectDef
    344                 );
    345                 aspectDef.addAfterAdviceDefinition(adviceDef);
    346             }
    347         }
    348         afterAnnotations = AsmAnnotations.getAnnotations(AnnotationConstants.AFTER_FINALLY, method);
    349         for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
    350             AfterFinally annotation = (AfterFinally) iterator.next();
    351             if (annotation != null) {
    352                 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
    353                         getAdviceNameAsInSource(method),
    354                         AdviceType.AFTER_FINALLY,
    355                         annotation.value(),
    356                         null,
    357                         aspectName,
    358                         aspectClassName,
    359                         method,
    360                         aspectDef
    361                 );
    362                 aspectDef.addAfterAdviceDefinition(adviceDef);
    363             }
    364         }
    365     }
    366
    367     /***
    368      * Returns the call signature of a Pointcut or advice with signature methodName(paramType paramName, ...) [we ignore
    369      * the return type] If there is no parameters, the call signature is not "name()" but just "name"
    370      *
    371      * @param methodInfo
    372      * @return string representation (see javavadoc)
    373      */
    374     private static String getAdviceNameAsInSource(final MethodInfo methodInfo) {
    375         StringBuffer buffer = new StringBuffer(methodInfo.getName());
    376         if (methodInfo.getParameterNames() == null
    377             || methodInfo.getParameterNames().length != methodInfo.getParameterTypes().length
    378             || (methodInfo.getParameterNames().length > 0 && methodInfo.getParameterNames()[0] == null)) {
    379             return methodInfo.getName();
    380 //            throw new DefinitionException(
    381 //                    "Could not access source information for method " + methodInfo.getDeclaringType().getName() + "." +
    382 //                    methodInfo.getName() +
    383 //                    methodInfo.getSignature() +
    384 //                    ". Compile aspects with javac -g."
    385 //            );
    386         }
    387         if (methodInfo.getParameterNames().length > 0) {
    388             buffer.append('(');
    389             for (int i = 0; i < methodInfo.getParameterNames().length; i++) {
    390                 if (i > 0) {
    391                     buffer.append(", ");
    392                 }
    393                 String parameterName = methodInfo.getParameterNames()[i];
    394                 buffer.append(methodInfo.getParameterTypes()[i].getName());
    395                 buffer.append(' ').append(parameterName);
    396             }
    397             buffer.append(')');
    398         }
    399         return buffer.toString();
    400     }
    401
    402     /***
    403      * Handles specific syntax for @AfterXXX annotation, where we can write it using the default "value" element
    404      * or instead specify the pointcut using "pointcut", and optionally a "type" element.
    405      *
    406      * @param value
    407      * @param pointcut
    408      * @return the one of value or expression which is not null. Both cannot be specified at the same time
    409      */
    410     public static String getExpressionElseValue(String value, String pointcut) {
    411         if (!Strings.isNullOrEmpty(pointcut)) {
    412             return pointcut;
    413         } else if (!Strings.isNullOrEmpty(value)) {
    414             return value;
    415         } else {
    416             throw new DefinitionException("neither expression nor value had a valid value");
    417         }
    418     }
    419
    420 }