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
83 deploymentModelAsString = aspectAnnotation.value();
84 } else {
85 if (aspectAnnotation.name() != null) {
86
87 aspectName = aspectAnnotation.name();
88 }
89 if (aspectAnnotation.deploymentModel() != null) {
90
91 deploymentModelAsString = aspectAnnotation.deploymentModel();
92 }
93 }
94 }
95
96
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
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
179 List methodList = ClassInfoHelper.createMethodList(classInfo);
180
181
182 parsePointcutAttributes(methodList, aspectDef);
183
184
185 for (Iterator it = methodList.iterator(); it.hasNext();) {
186 MethodInfo method = (MethodInfo) it.next();
187 try {
188
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
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
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
381
382
383
384
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 }