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