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.SystemDefinition;
13 import org.codehaus.aspectwerkz.definition.AdviceDefinition;
14 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
15 import org.codehaus.aspectwerkz.exception.DefinitionException;
16 import org.codehaus.aspectwerkz.transform.ReflectHelper;
17 import org.codehaus.aspectwerkz.aspect.AdviceType;
18 import org.codehaus.aspectwerkz.reflect.ClassInfo;
19 import org.codehaus.aspectwerkz.reflect.FieldInfo;
20 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
21 import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotations;
22
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25 import java.util.Iterator;
26 import java.util.List;
27
28 /***
29 * Extracts the aspects annotations from the class files and creates a meta-data representation of them.
30 * <br/>
31 * Note: we are not using reflection to loop over fields, etc, so that we do not trigger nested loading, which could be
32 * potential target classes.
33 *
34 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
35 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
36 */
37 public class AspectAnnotationParser {
38
39 /***
40 * Singleton is enough
41 */
42 private final static AspectAnnotationParser s_singleton = new AspectAnnotationParser();
43
44 /***
45 * Private constructor to enforce singleton
46 */
47 private AspectAnnotationParser() {
48 }
49
50 /***
51 * Parse the attributes and create and return a meta-data representation of them.
52 *
53 * @param klass the class to extract attributes from
54 * @param aspectDef the aspect definition
55 * @param definition the aspectwerkz definition
56 */
57 public static void parse(final Class klass, final AspectDefinition aspectDef, final SystemDefinition definition) {
58 s_singleton.parse0(klass, aspectDef, definition);
59 }
60
61 /***
62 * Parse the attributes and create and return a meta-data representation of them.
63 *
64 * @param klass the class to extract attributes from
65 * @param aspectDef the aspect definition
66 * @param definition the aspectwerkz definition
67 */
68 private void parse0(final Class klass, final AspectDefinition aspectDef, final SystemDefinition definition) {
69 if (klass == null) {
70 throw new IllegalArgumentException("class to parse can not be null");
71 }
72
73
74 ClassInfo classInfo = AsmClassInfo.getClassInfo(klass.getName(), klass.getClassLoader(), false);
75
76 AspectAnnotationProxy aspectAnnotation = (AspectAnnotationProxy) Annotations.getAnnotation(
77 AnnotationC.ANNOTATION_ASPECT,
78 klass
79 );
80 if (aspectAnnotation == null) {
81
82
83 aspectAnnotation = new AspectAnnotationProxy();
84 aspectAnnotation.setAspectName(klass.getName());
85 }
86
87
88 aspectDef.setDeploymentModel(aspectAnnotation.deploymentModel());
89 String className = klass.getName();
90 String aspectName = aspectAnnotation.aspectName();
91 parseFieldAttributes(klass, aspectDef);
92 parseMethodAttributes(klass, className, aspectName, aspectDef);
93 parseClassAttributes(klass, aspectDef);
94 }
95
96 /***
97 * Parses the field attributes and creates a meta-data representation of them.
98 *
99 * @param klass the class to extract attributes from
100 * @param aspectDef the aspect definition
101 */
102 private void parseFieldAttributes(final Class klass, AspectDefinition aspectDef) {
103 if (aspectDef == null) {
104 throw new IllegalArgumentException("aspect definition can not be null");
105 }
106 if (klass == null) {
107 return;
108 }
109
110
111 ClassInfo classInfo = AsmClassInfo.getClassInfo(klass.getName(), klass.getClassLoader());
112
113 FieldInfo[] fieldList = classInfo.getFields();
114 for (int i = 0; i < fieldList.length; i++) {
115 FieldInfo field = fieldList[i];
116 for (Iterator iterator = field.getAnnotations().iterator(); iterator.hasNext();) {
117 AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();
118 if (annotationInfo.getAnnotation() == null) {
119 continue;
120 }
121 if (AnnotationC.ANNOTATION_EXPRESSION.equals(annotationInfo.getName())) {
122 DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
123 field.getName(),
124 ((ExpressionAnnotationProxy)annotationInfo.getAnnotation()).expression(),
125 aspectDef
126 );
127 }
128 else if (AnnotationC.ANNOTATION_IMPLEMENTS.equals(annotationInfo.getName())) {
129 DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef(
130 ((ImplementsAnnotationProxy)annotationInfo.getAnnotation()).expression(),
131 field.getName(),
132 field.getType().getName(),
133 aspectDef
134 );
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 }
162
163
164 parseFieldAttributes(klass.getSuperclass(), aspectDef);
165 }
166
167 /***
168 * Parses the method attributes and creates a meta-data representation of them.
169 *
170 * @param klass 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 Class klass,
176 final String aspectClassName,
177 final String aspectName,
178 final AspectDefinition aspectDef) {
179
180 if (klass == null) {
181 throw new IllegalArgumentException("class can not be null");
182 }
183 if (aspectClassName == null) {
184 throw new IllegalArgumentException("aspect class name can not be null");
185 }
186 if (aspectName == null) {
187 throw new IllegalArgumentException("aspect name can not be null " + aspectClassName);
188 }
189 if (aspectDef == null) {
190 throw new IllegalArgumentException("aspect definition can not be null");
191 }
192
193 List methodList = ReflectHelper.createCompleteSortedMethodList(klass);
194
195
196 int methodIndex = 0;
197 for (Iterator it = methodList.iterator(); it.hasNext(); methodIndex++) {
198 Method method = (Method) it.next();
199
200
201 List expressionAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_EXPRESSION, method);
202 for (Iterator iterator = expressionAnnotations.iterator(); iterator.hasNext();) {
203 ExpressionAnnotationProxy annotation = (ExpressionAnnotationProxy) iterator.next();
204 if (annotation != null) {
205
206 DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
207 AspectAnnotationParser
208 .getMethodPointcutCallSignature(method.getName(), annotation), annotation.expression(), aspectDef
209 );
210 }
211 }
212 }
213
214
215 methodIndex = 0;
216 for (Iterator it = methodList.iterator(); it.hasNext(); methodIndex++) {
217 Method method = (Method) it.next();
218
219 try {
220
221 List aroundAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_AROUND, method);
222 for (Iterator iterator = aroundAnnotations.iterator(); iterator.hasNext();) {
223 AroundAnnotationProxy aroundAnnotation = (AroundAnnotationProxy) iterator.next();
224 if (aroundAnnotation != null) {
225 final String expression = aroundAnnotation.pointcut();
226 final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
227 method.getName(), aroundAnnotation
228 );
229 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
230 adviceName,
231 aroundAnnotation.getType(),
232 expression,
233 null,
234 aspectName,
235 aspectClassName,
236 method,
237 methodIndex,
238 aspectDef
239 );
240 aspectDef.addAroundAdvice(adviceDef);
241 }
242 }
243 List beforeAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_BEFORE, method);
244 for (Iterator iterator = beforeAnnotations.iterator(); iterator.hasNext();) {
245 BeforeAnnotationProxy beforeAnnotation = (BeforeAnnotationProxy) iterator.next();
246 if (beforeAnnotation != null) {
247 final String expression = beforeAnnotation.pointcut();
248 final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
249 method.getName(), beforeAnnotation
250 );
251 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
252 adviceName,
253 beforeAnnotation.getType(),
254 expression,
255 null,
256 aspectName,
257 aspectClassName,
258 method,
259 methodIndex,
260 aspectDef
261 );
262 aspectDef.addBeforeAdvice(adviceDef);
263 }
264 }
265 List afterAnnotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_AFTER, method);
266 for (Iterator iterator = afterAnnotations.iterator(); iterator.hasNext();) {
267 AfterAnnotationProxy afterAnnotation = (AfterAnnotationProxy) iterator.next();
268 if (afterAnnotation != null) {
269 final String expression = afterAnnotation.pointcut();
270 final String adviceName = AspectAnnotationParser.getMethodPointcutCallSignature(
271 method.getName(), afterAnnotation
272 );
273 AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition(
274 adviceName,
275 afterAnnotation.getType(),
276 expression,
277 afterAnnotation.getSpecialArgumentType(),
278 aspectName,
279 aspectClassName,
280 method,
281 methodIndex,
282 aspectDef
283 );
284 aspectDef.addAfterAdvice(adviceDef);
285 }
286 }
287 } catch (DefinitionException e) {
288 System.err.println("WARNING: unable to register advice: " + e.getMessage());
289
290 }
291 }
292 }
293
294 /***
295 * Looks for "@Introduce IntroduceAttribute" defined at aspect inner class level
296 *
297 * @param klass of aspect
298 * @param aspectDef
299 */
300 private void parseClassAttributes(final Class klass, AspectDefinition aspectDef) {
301 if (klass == null) {
302 throw new IllegalArgumentException("class can not be null");
303 }
304 List annotations = Annotations.getAnnotations(AnnotationC.ANNOTATION_INTRODUCE, klass);
305 for (Iterator iterator = annotations.iterator(); iterator.hasNext();) {
306 IntroduceAnnotationProxy annotation = (IntroduceAnnotationProxy) iterator.next();
307 if (annotation != null) {
308 Class mixin;
309 try {
310 mixin = klass.getClassLoader().loadClass(annotation.innerClassName());
311 } catch (ClassNotFoundException e) {
312 throw new WrappedRuntimeException(e);
313 }
314 DefinitionParserHelper.createAndAddIntroductionDefToAspectDef(
315 mixin,
316 annotation.expression(),
317 annotation.deploymentModel(),
318 aspectDef
319 );
320 }
321 }
322 }
323
324 /***
325 * Returns the call signature of a Pointcut or advice with signature methodName(paramType paramName, ...) [we ignore
326 * the return type] If there is no parameters, the call signature is not "name()" but just "name"
327 *
328 * @param methodName
329 * @return annotationProxy that contains the ordered map of call parameters
330 */
331 private static String getMethodPointcutCallSignature(final String methodName,
332 final ParameterizedAnnotationProxy annotationProxy) {
333 StringBuffer buffer = new StringBuffer(methodName);
334 if (annotationProxy.getArgumentNames().size() > 0) {
335 buffer.append('(');
336 for (Iterator it = annotationProxy.getArgumentNames().iterator(); it.hasNext();) {
337 String parameter = (String) it.next();
338 buffer.append(annotationProxy.getArgumentType(parameter));
339 buffer.append(' ').append(parameter);
340 if (it.hasNext()) {
341 buffer.append(", ");
342 }
343 }
344 buffer.append(')');
345 }
346 return buffer.toString();
347 }
348
349 }