001: /************1**************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.annotation;
008:
009: import org.codehaus.aspectwerkz.definition.AspectDefinition;
010: import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
011: import org.codehaus.aspectwerkz.definition.AdviceDefinition;
012: import org.codehaus.aspectwerkz.definition.DeploymentScope;
013: import org.codehaus.aspectwerkz.exception.DefinitionException;
014: import org.codehaus.aspectwerkz.reflect.ClassInfo;
015: import org.codehaus.aspectwerkz.reflect.FieldInfo;
016: import org.codehaus.aspectwerkz.reflect.MethodInfo;
017: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
018: import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotations;
019: import org.codehaus.aspectwerkz.DeploymentModel;
020: import org.codehaus.aspectwerkz.util.Strings;
021: import org.codehaus.aspectwerkz.aspect.AdviceType;
022:
023: import java.util.Iterator;
024: import java.util.List;
025:
026: /**
027: * Extracts the aspects annotations from the class files and creates a meta-data representation of them.
028: * <br/>
029: * Note: we are not using reflection to loop over fields, etc, so that we do not trigger nested loading, which could be
030: * potential target classes.
031: *
032: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
033: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
034: */
035: public class AspectAnnotationParser {
036:
037: /**
038: * The sole instance.
039: */
040: private final static AspectAnnotationParser INSTANCE = new AspectAnnotationParser();
041:
042: /**
043: * Private constructor to prevent subclassing.
044: */
045: private AspectAnnotationParser() {
046: }
047:
048: /**
049: * Parse the attributes and create and return a meta-data representation of them.
050: *
051: * @param classInfo the class to extract attributes from
052: * @param aspectDef the aspect definition
053: * @param loader
054: */
055: public static void parse(final ClassInfo classInfo,
056: final AspectDefinition aspectDef, final ClassLoader loader) {
057: INSTANCE.doParse(classInfo, aspectDef, loader);
058: }
059:
060: /**
061: * Parse the attributes and create and return a meta-data representation of them.
062: *
063: * @param classInfo the class to extract attributes from
064: * @param aspectDef the aspect definition
065: * @param loader
066: */
067: private void doParse(final ClassInfo classInfo,
068: final AspectDefinition aspectDef, final ClassLoader loader) {
069: if (classInfo == null) {
070: throw new IllegalArgumentException(
071: "class to parse can not be null");
072: }
073:
074: Aspect aspectAnnotation = (Aspect) AsmAnnotations
075: .getAnnotation(AnnotationConstants.ASPECT, classInfo);
076:
077: String aspectName = classInfo.getName();
078: String deploymentModelAsString = null;
079:
080: if (aspectAnnotation != null) {
081: if (aspectAnnotation.value() != null) {
082: //@Aspect(perJVM)
083: deploymentModelAsString = aspectAnnotation.value();
084: } else {
085: if (aspectAnnotation.name() != null) {
086: //@Aspect(name=..)
087: aspectName = aspectAnnotation.name();
088: }
089: if (aspectAnnotation.deploymentModel() != null) {
090: //@Aspect(deploymentModel=..)
091: deploymentModelAsString = aspectAnnotation
092: .deploymentModel();
093: }
094: }
095: }
096:
097: // attribute settings override the xml settings
098: aspectDef.setDeploymentModel(DeploymentModel
099: .getDeploymentModelFor(deploymentModelAsString));
100: String className = classInfo.getName();
101: parseFieldAttributes(classInfo, aspectDef);
102: parseMethodAttributes(classInfo, className, aspectName,
103: aspectDef);
104: }
105:
106: /**
107: * Parses the field attributes and creates a meta-data representation of them.
108: *
109: * @param classInfo the class to extract attributes from
110: * @param aspectDef the aspect definition
111: */
112: private void parseFieldAttributes(final ClassInfo classInfo,
113: final AspectDefinition aspectDef) {
114: if (aspectDef == null) {
115: throw new IllegalArgumentException(
116: "aspect definition can not be null");
117: }
118: if (classInfo == null) {
119: return;
120: }
121:
122: FieldInfo[] fieldList = classInfo.getFields();
123: for (int i = 0; i < fieldList.length; i++) {
124: FieldInfo field = fieldList[i];
125: for (Iterator iterator = field.getAnnotations().iterator(); iterator
126: .hasNext();) {
127: AnnotationInfo annotationInfo = (AnnotationInfo) iterator
128: .next();
129: if (annotationInfo.getAnnotation() == null) {
130: continue;
131: }
132: if (AnnotationConstants.EXPRESSION
133: .equals(annotationInfo.getName())) {
134: if (field.getType().getName().equals(
135: DeploymentScope.class.getName())) {
136: DefinitionParserHelper
137: .createAndAddDeploymentScopeDef(field
138: .getName(),
139: ((Expression) annotationInfo
140: .getAnnotation())
141: .value(), aspectDef
142: .getSystemDefinition());
143: } else {
144: DefinitionParserHelper
145: .createAndAddPointcutDefToAspectDef(
146: field.getName(),
147: ((Expression) annotationInfo
148: .getAnnotation())
149: .value(), aspectDef);
150: }
151: } else if (AnnotationConstants.INTRODUCE
152: .equals(annotationInfo.getName())) {
153: DefinitionParserHelper
154: .createAndAddInterfaceIntroductionDefToAspectDef(
155: ((Introduce) annotationInfo
156: .getAnnotation()).value(),
157: field.getName(), field.getType()
158: .getName(), aspectDef);
159: }
160: }
161: }
162:
163: // recursive call, next iteration based on super class
164: parseFieldAttributes(classInfo.getSuperclass(), aspectDef);
165: }
166:
167: /**
168: * Parses the method attributes and creates a meta-data representation of them.
169: *
170: * @param classInfo 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 ClassInfo classInfo,
176: final String aspectClassName, final String aspectName,
177: final AspectDefinition aspectDef) {
178: if (classInfo == null) {
179: throw new IllegalArgumentException("class can not be null");
180: }
181: if (aspectClassName == null) {
182: throw new IllegalArgumentException(
183: "aspect class name can not be null");
184: }
185: if (aspectName == null) {
186: throw new IllegalArgumentException(
187: "aspect name can not be null " + aspectClassName);
188: }
189: if (aspectDef == null) {
190: throw new IllegalArgumentException(
191: "aspect definition can not be null");
192: }
193: // get complete method list (includes inherited ones)
194: List methodList = ClassInfoHelper.createMethodList(classInfo);
195:
196: // iterate first on all method to lookup @Expression Pointcut annotations so that they can be resolved
197: parsePointcutAttributes(methodList, aspectDef);
198:
199: // iterate on the advice annotations
200: for (Iterator it = methodList.iterator(); it.hasNext();) {
201: MethodInfo method = (MethodInfo) it.next();
202: try {
203: // create the advice name out of the class and method name, <classname>.<methodname>
204: parseAroundAttributes(method, aspectName,
205: aspectClassName, aspectDef);
206: parseBeforeAttributes(method, aspectName,
207: aspectClassName, aspectDef);
208: parseAfterAttributes(method, aspectName,
209: aspectClassName, aspectDef);
210: } catch (DefinitionException e) {
211: System.err
212: .println("AW::WARNING - unable to register advice: "
213: + e.toString());
214: // TODO AV - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE
215: }
216: }
217: }
218:
219: /**
220: * Parses the method pointcut attributes.
221: *
222: * @param methodList
223: * @param aspectDef
224: */
225: private void parsePointcutAttributes(final List methodList,
226: final AspectDefinition aspectDef) {
227: for (Iterator it = methodList.iterator(); it.hasNext();) {
228: MethodInfo method = (MethodInfo) it.next();
229:
230: // Pointcut with signature
231: List expressionAnnotations = AsmAnnotations.getAnnotations(
232: AnnotationConstants.EXPRESSION, method);
233: for (Iterator iterator = expressionAnnotations.iterator(); iterator
234: .hasNext();) {
235: Expression annotation = (Expression) iterator.next();
236: if (annotation != null) {
237: DefinitionParserHelper
238: .createAndAddPointcutDefToAspectDef(
239: getAdviceNameAsInSource(method),
240: annotation.value(), aspectDef);
241: }
242: }
243: }
244: }
245:
246: /**
247: * Parses the around attributes.
248: *
249: * @param method
250: * @param aspectName
251: * @param aspectClassName
252: * @param aspectDef
253: */
254: private void parseAroundAttributes(final MethodInfo method,
255: final String aspectName, final String aspectClassName,
256: final AspectDefinition aspectDef) {
257: List aroundAnnotations = AsmAnnotations.getAnnotations(
258: AnnotationConstants.AROUND, method);
259: for (Iterator iterator = aroundAnnotations.iterator(); iterator
260: .hasNext();) {
261: Around aroundAnnotation = (Around) iterator.next();
262: if (aroundAnnotation != null) {
263: AdviceDefinition adviceDef = DefinitionParserHelper
264: .createAdviceDefinition(
265: getAdviceNameAsInSource(method),
266: AdviceType.AROUND, aroundAnnotation
267: .value(), null, aspectName,
268: aspectClassName, method, aspectDef);
269: aspectDef.addAroundAdviceDefinition(adviceDef);
270: }
271: }
272: }
273:
274: /**
275: * Parses the before attributes.
276: *
277: * @param method
278: * @param aspectName
279: * @param aspectClassName
280: * @param aspectDef
281: */
282: private void parseBeforeAttributes(final MethodInfo method,
283: final String aspectName, final String aspectClassName,
284: final AspectDefinition aspectDef) {
285: List beforeAnnotations = AsmAnnotations.getAnnotations(
286: AnnotationConstants.BEFORE, method);
287: for (Iterator iterator = beforeAnnotations.iterator(); iterator
288: .hasNext();) {
289: Before beforeAnnotation = (Before) iterator.next();
290: if (beforeAnnotation != null) {
291: AdviceDefinition adviceDef = DefinitionParserHelper
292: .createAdviceDefinition(
293: getAdviceNameAsInSource(method),
294: AdviceType.BEFORE, beforeAnnotation
295: .value(), null, aspectName,
296: aspectClassName, method, aspectDef);
297: aspectDef.addBeforeAdviceDefinition(adviceDef);
298: }
299: }
300: }
301:
302: /**
303: * Parses the after attributes.
304: *
305: * @param method
306: * @param aspectName
307: * @param aspectClassName
308: * @param aspectDef
309: */
310: private void parseAfterAttributes(final MethodInfo method,
311: final String aspectName, final String aspectClassName,
312: final AspectDefinition aspectDef) {
313: List afterAnnotations = AsmAnnotations.getAnnotations(
314: AnnotationConstants.AFTER, method);
315: for (Iterator iterator = afterAnnotations.iterator(); iterator
316: .hasNext();) {
317: After annotation = (After) iterator.next();
318: if (annotation != null) {
319: AdviceDefinition adviceDef = DefinitionParserHelper
320: .createAdviceDefinition(
321: getAdviceNameAsInSource(method),
322: AdviceType.AFTER, annotation.value(),
323: null, aspectName, aspectClassName,
324: method, aspectDef);
325: aspectDef.addAfterAdviceDefinition(adviceDef);
326: }
327: }
328: afterAnnotations = AsmAnnotations.getAnnotations(
329: AnnotationConstants.AFTER_RETURNING, method);
330: for (Iterator iterator = afterAnnotations.iterator(); iterator
331: .hasNext();) {
332: AfterReturning annotation = (AfterReturning) iterator
333: .next();
334: if (annotation != null) {
335: AdviceDefinition adviceDef = DefinitionParserHelper
336: .createAdviceDefinition(
337: getAdviceNameAsInSource(method),
338: AdviceType.AFTER_RETURNING,
339: getExpressionElseValue(annotation
340: .value(), annotation.pointcut()),
341: annotation.type(), aspectName,
342: aspectClassName, method, aspectDef);
343: aspectDef.addAfterAdviceDefinition(adviceDef);
344: }
345: }
346: afterAnnotations = AsmAnnotations.getAnnotations(
347: AnnotationConstants.AFTER_THROWING, method);
348: for (Iterator iterator = afterAnnotations.iterator(); iterator
349: .hasNext();) {
350: AfterThrowing annotation = (AfterThrowing) iterator.next();
351: if (annotation != null) {
352: AdviceDefinition adviceDef = DefinitionParserHelper
353: .createAdviceDefinition(
354: getAdviceNameAsInSource(method),
355: AdviceType.AFTER_THROWING,
356: getExpressionElseValue(annotation
357: .value(), annotation.pointcut()),
358: annotation.type(), aspectName,
359: aspectClassName, method, aspectDef);
360: aspectDef.addAfterAdviceDefinition(adviceDef);
361: }
362: }
363: afterAnnotations = AsmAnnotations.getAnnotations(
364: AnnotationConstants.AFTER_FINALLY, method);
365: for (Iterator iterator = afterAnnotations.iterator(); iterator
366: .hasNext();) {
367: AfterFinally annotation = (AfterFinally) iterator.next();
368: if (annotation != null) {
369: AdviceDefinition adviceDef = DefinitionParserHelper
370: .createAdviceDefinition(
371: getAdviceNameAsInSource(method),
372: AdviceType.AFTER_FINALLY, annotation
373: .value(), null, aspectName,
374: aspectClassName, method, aspectDef);
375: aspectDef.addAfterAdviceDefinition(adviceDef);
376: }
377: }
378: }
379:
380: /**
381: * Returns the call signature of a Pointcut or advice with signature methodName(paramType paramName, ...) [we ignore
382: * the return type] If there is no parameters, the call signature is not "name()" but just "name"
383: *
384: * @param methodInfo
385: * @return string representation (see javavadoc)
386: */
387: private static String getAdviceNameAsInSource(
388: final MethodInfo methodInfo) {
389: StringBuffer buffer = new StringBuffer(methodInfo.getName());
390: if (methodInfo.getParameterNames() == null
391: || methodInfo.getParameterNames().length != methodInfo
392: .getParameterTypes().length
393: || (methodInfo.getParameterNames().length > 0 && methodInfo
394: .getParameterNames()[0] == null)) {
395: return methodInfo.getName();
396: // throw new DefinitionException(
397: // "Could not access source information for method " + methodInfo.getDeclaringType().getName() + "." +
398: // methodInfo.getName() +
399: // methodInfo.getSignature() +
400: // ". Compile aspects with javac -g."
401: // );
402: }
403: if (methodInfo.getParameterNames().length > 0) {
404: buffer.append('(');
405: for (int i = 0; i < methodInfo.getParameterNames().length; i++) {
406: if (i > 0) {
407: buffer.append(", ");
408: }
409: String parameterName = methodInfo.getParameterNames()[i];
410: buffer.append(methodInfo.getParameterTypes()[i]
411: .getName());
412: buffer.append(' ').append(parameterName);
413: }
414: buffer.append(')');
415: }
416: return buffer.toString();
417: }
418:
419: /**
420: * Handles specific syntax for @AfterXXX annotation, where we can write it using the default "value" element
421: * or instead specify the pointcut using "pointcut", and optionally a "type" element.
422: *
423: * @param value
424: * @param pointcut
425: * @return the one of value or expression which is not null. Both cannot be specified at the same time
426: */
427: public static String getExpressionElseValue(String value,
428: String pointcut) {
429: if (!Strings.isNullOrEmpty(pointcut)) {
430: return pointcut;
431: } else if (!Strings.isNullOrEmpty(value)) {
432: return value;
433: } else {
434: throw new DefinitionException(
435: "neither expression nor value had a valid value");
436: }
437: }
438:
439: }
|