/************1************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.annotation; import org.codehaus.aspectwerkz.definition.AspectDefinition; import org.codehaus.aspectwerkz.definition.DefinitionParserHelper; import org.codehaus.aspectwerkz.definition.AdviceDefinition; import org.codehaus.aspectwerkz.definition.DeploymentScope; import org.codehaus.aspectwerkz.exception.DefinitionException; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.FieldInfo; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.reflect.ClassInfoHelper; import org.codehaus.aspectwerkz.DeploymentModel; import org.codehaus.aspectwerkz.util.Strings; import org.codehaus.aspectwerkz.aspect.AdviceType; import java.util.Iterator; import java.util.List; /** * Extracts the aspects annotations from the class files and creates a meta-data representation of them. * <br/> * Note: we are not using reflection to loop over fields, etc, so that we do not trigger nested loading, which could be * potential target classes. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> */ public class AspectAnnotationParser { /** * The sole instance. */ private final static AspectAnnotationParser INSTANCE = new AspectAnnotationParser(); /** * Private constructor to prevent subclassing. */ private AspectAnnotationParser() { } /** * Parse the attributes and create and return a meta-data representation of them. * * @param classInfo the class to extract attributes from * @param aspectDef the aspect definition * @param loader */ public static void parse(final ClassInfo classInfo, final AspectDefinition aspectDef, final ClassLoader loader) { INSTANCE.doParse(classInfo, aspectDef, loader); } /** * Parse the attributes and create and return a meta-data representation of them. * * @param classInfo the class to extract attributes from * @param aspectDef the aspect definition * @param loader */ private void doParse(final ClassInfo classInfo, final AspectDefinition aspectDef, final ClassLoader loader) { if (classInfo == null) { throw new IllegalArgumentException("class to parse can not be null"); } Aspect aspectAnnotation = (Aspect) AsmAnnotations.getAnnotation( AnnotationConstants.ASPECT, classInfo ); String aspectName = classInfo.getName(); String deploymentModelAsString = null; if (aspectAnnotation != null) { if (aspectAnnotation.value() != null) { //@Aspect(perJVM) deploymentModelAsString = aspectAnnotation.value(); } else { if (aspectAnnotation.name() != null) { //@Aspect(name=..) aspectName = aspectAnnotation.name(); } if (aspectAnnotation.deploymentModel() != null) { //@Aspect(deploymentModel=..) deploymentModelAsString = aspectAnnotation.deploymentModel(); } } } // attribute settings override the xml settings aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelFor(deploymentModelAsString)); String className = classInfo.getName(); parseFieldAttributes(classInfo, aspectDef); parseMethodAttributes(classInfo, className, aspectName, aspectDef); } /** * Parses the field attributes and creates a meta-data representation of them. * * @param classInfo the class to extract attributes from * @param aspectDef the aspect definition */ private void parseFieldAttributes(final ClassInfo classInfo, final AspectDefinition aspectDef) { if (aspectDef == null) { throw new IllegalArgumentException("aspect definition can not be null"); } if (classInfo == null) { return; } FieldInfo[] fieldList = classInfo.getFields(); for (int i = 0; i < fieldList.length; i++) { FieldInfo field = fieldList[i]; // expression ie pointcut or deployment scopes Expression expression = (Expression) AsmAnnotations.getAnnotation(AnnotationConstants.EXPRESSION, field); if (expression != null) { if (field.getType().getName().equals(DeploymentScope.class.getName())) { DefinitionParserHelper.createAndAddDeploymentScopeDef( field.getName(), expression.value(), aspectDef.getSystemDefinition() ); } else { DefinitionParserHelper.createAndAddPointcutDefToAspectDef( field.getName(), expression.value(), aspectDef ); } } // introduce Introduce introduce = (Introduce) AsmAnnotations.getAnnotation(AnnotationConstants.INTRODUCE, field); if (introduce != null) { DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef( introduce.value(), field.getName(), field.getType().getName(), aspectDef ); } } // recursive call, next iteration based on super class parseFieldAttributes(classInfo.getSuperclass(), aspectDef); } /** * Parses the method attributes and creates a meta-data representation of them. * * @param classInfo the class * @param aspectClassName the aspect class name * @param aspectName the aspect name * @param aspectDef the aspect definition */ private void parseMethodAttributes(final ClassInfo classInfo, final String aspectClassName, final String aspectName, final AspectDefinition aspectDef) { if (classInfo == null) { throw new IllegalArgumentException("class can not be null"); } if (aspectClassName == null) { throw new IllegalArgumentException("aspect class name can not be null"); } if (aspectName == null) { throw new IllegalArgumentException("aspect name can not be null " + aspectClassName); } if (aspectDef == null) { throw new IllegalArgumentException("aspect definition can not be null"); } // get complete method list (includes inherited ones) List methodList = ClassInfoHelper.createMethodList(classInfo); // iterate first on all method to lookup @Expression Pointcut annotations so that they can be resolved parsePointcutAttributes(methodList, aspectDef); // iterate on the advice annotations for (Iterator it = methodList.iterator(); it.hasNext();) { MethodInfo method = (MethodInfo) it.next(); try { // create the advice name out of the class and method name, <classname>.<methodname> parseAroundAttributes(method, aspectName, aspectClassName, aspectDef); parseBeforeAttributes(method, aspectName, aspectClassName, aspectDef); parseAfterAttributes(method, aspectName, aspectClassName, aspectDef); } catch (DefinitionException e) { System.err.println("AW::WARNING - unable to register advice: " + e.toString()); // TODO AV - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE } } } /** * Parses the method pointcut attributes. * * @param methodList * @param aspectDef */ private void parsePointcutAttributes(final List methodList, final AspectDefinition aspectDef) { for (Iterator it = methodList.iterator(); it.hasNext();) { MethodInfo method = (MethodInfo) it.next(); // Pointcut with signature Expression annotation = (Expression) AsmAnnotations.getAnnotation(AnnotationConstants.EXPRESSION, method); if (annotation != null) { DefinitionParserHelper.createAndAddPointcutDefToAspectDef( getAdviceNameAsInSource(method), annotation.value(), aspectDef ); } } } /** * Parses the around attributes. * * @param method * @param aspectName * @param aspectClassName * @param aspectDef */ private void parseAroundAttributes(final MethodInfo method, final String aspectName, final String aspectClassName, final AspectDefinition aspectDef) { Around aroundAnnotation = (Around) AsmAnnotations.getAnnotation(AnnotationConstants.AROUND, method); if (aroundAnnotation != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.AROUND, aroundAnnotation.value(), null, aspectName, aspectClassName, method, aspectDef ); aspectDef.addAroundAdviceDefinition(adviceDef); } } /** * Parses the before attributes. * * @param method * @param aspectName * @param aspectClassName * @param aspectDef */ private void parseBeforeAttributes(final MethodInfo method, final String aspectName, final String aspectClassName, final AspectDefinition aspectDef) { Before beforeAnnotation = (Before) AsmAnnotations.getAnnotation(AnnotationConstants.BEFORE, method); if (beforeAnnotation != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.BEFORE, beforeAnnotation.value(), null, aspectName, aspectClassName, method, aspectDef ); aspectDef.addBeforeAdviceDefinition(adviceDef); } } /** * Parses the after attributes. * * @param method * @param aspectName * @param aspectClassName * @param aspectDef */ private void parseAfterAttributes(final MethodInfo method, final String aspectName, final String aspectClassName, final AspectDefinition aspectDef) { After annotationAft = (After) AsmAnnotations.getAnnotation(AnnotationConstants.AFTER, method); if (annotationAft != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.AFTER, annotationAft.value(), null, aspectName, aspectClassName, method, aspectDef ); aspectDef.addAfterAdviceDefinition(adviceDef); } AfterReturning annotationRet = (AfterReturning) AsmAnnotations.getAnnotation(AnnotationConstants.AFTER_RETURNING, method); if (annotationRet != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.AFTER_RETURNING, getExpressionElseValue(annotationRet.value(), annotationRet.pointcut()), annotationRet.type(), aspectName, aspectClassName, method, aspectDef ); aspectDef.addAfterAdviceDefinition(adviceDef); } AfterThrowing annotationThr = (AfterThrowing) AsmAnnotations.getAnnotation(AnnotationConstants.AFTER_THROWING, method); if (annotationThr != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.AFTER_THROWING, getExpressionElseValue(annotationThr.value(), annotationThr.pointcut()), annotationThr.type(), aspectName, aspectClassName, method, aspectDef ); aspectDef.addAfterAdviceDefinition(adviceDef); } AfterFinally annotationFin = (AfterFinally) AsmAnnotations.getAnnotation(AnnotationConstants.AFTER_FINALLY, method); if (annotationFin != null) { AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( getAdviceNameAsInSource(method), AdviceType.AFTER_FINALLY, annotationFin.value(), null, aspectName, aspectClassName, method, aspectDef ); aspectDef.addAfterAdviceDefinition(adviceDef); } } /** * Returns the call signature of a Pointcut or advice with signature methodName(paramType paramName, ...) [we ignore * the return type] If there is no parameters, the call signature is not "name()" but just "name" * * @param methodInfo * @return string representation (see javavadoc) */ private static String getAdviceNameAsInSource(final MethodInfo methodInfo) { StringBuffer buffer = new StringBuffer(methodInfo.getName()); if (methodInfo.getParameterNames() == null || methodInfo.getParameterNames().length != methodInfo.getParameterTypes().length || (methodInfo.getParameterNames().length > 0 && methodInfo.getParameterNames()[0] == null)) { return methodInfo.getName(); // throw new DefinitionException( // "Could not access source information for method " + methodInfo.getDeclaringType().getName() + "." + // methodInfo.getName() + // methodInfo.getSignature() + // ". Compile aspects with javac -g." // ); } if (methodInfo.getParameterNames().length > 0) { buffer.append('('); for (int i = 0; i < methodInfo.getParameterNames().length; i++) { if (i > 0) { buffer.append(", "); } String parameterName = methodInfo.getParameterNames()[i]; buffer.append(methodInfo.getParameterTypes()[i].getName()); buffer.append(' ').append(parameterName); } buffer.append(')'); } return buffer.toString(); } /** * Handles specific syntax for @AfterXXX annotation, where we can write it using the default "value" element * or instead specify the pointcut using "pointcut", and optionally a "type" element. * * @param value * @param pointcut * @return the one of value or expression which is not null. Both cannot be specified at the same time */ public static String getExpressionElseValue(String value, String pointcut) { if (!Strings.isNullOrEmpty(pointcut)) { return pointcut; } else if (!Strings.isNullOrEmpty(value)) { return value; } else { throw new DefinitionException("neither expression nor value had a valid value"); } } }