KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > aop > aspectj > annotation > AbstractAspectJAdvisorFactory


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.aop.aspectj.annotation;
18
19 import java.lang.annotation.Annotation JavaDoc;
20 import java.lang.reflect.Constructor JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.StringTokenizer JavaDoc;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.aspectj.lang.annotation.After;
30 import org.aspectj.lang.annotation.AfterReturning;
31 import org.aspectj.lang.annotation.AfterThrowing;
32 import org.aspectj.lang.annotation.Around;
33 import org.aspectj.lang.annotation.Aspect;
34 import org.aspectj.lang.annotation.Before;
35 import org.aspectj.lang.annotation.Pointcut;
36 import org.aspectj.lang.reflect.AjType;
37 import org.aspectj.lang.reflect.AjTypeSystem;
38 import org.aspectj.lang.reflect.PerClauseKind;
39
40 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
41 import org.springframework.aop.framework.AopConfigException;
42 import org.springframework.core.ParameterNameDiscoverer;
43 import org.springframework.core.PrioritizedParameterNameDiscoverer;
44 import org.springframework.core.annotation.AnnotationUtils;
45 import org.springframework.util.StringUtils;
46
47 /**
48  * Abstract base class for factories that can create Spring AOP Advisors
49  * given AspectJ classes from classes honoring the AspectJ 5 annotation syntax.
50  *
51  * <p>This class handles annotation parsing and validation functionality.
52  * It does not actually generate Spring AOP Advisors, which is deferred to subclasses.
53  *
54  * @author Rod Johnson
55  * @author Adrian Colyer
56  * @author Juergen Hoeller
57  * @since 2.0
58  */

59 public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
60     
61     protected static final ParameterNameDiscoverer ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER =
62             new AspectJAnnotationParameterNameDiscoverer();
63
64
65     /**
66      * Find and return the first AspectJ annotation on the given method
67      * (there <i>should</i> only be one anyway...)
68      */

69     protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method JavaDoc aMethod) {
70         Class JavaDoc<? extends Annotation JavaDoc>[] classesToLookFor = (Class JavaDoc<? extends Annotation JavaDoc>[]) new Class JavaDoc[] {
71                     Before.class,
72                     Around.class,
73                     After.class,
74                     AfterReturning.class,
75                     AfterThrowing.class,
76                     Pointcut.class
77                 };
78         for (Class JavaDoc<? extends Annotation JavaDoc> c : classesToLookFor) {
79             AspectJAnnotation foundAnnotation = findAnnotation(aMethod, c);
80             if (foundAnnotation != null) {
81                 return foundAnnotation;
82             }
83         }
84         return null;
85     }
86
87     private static <A extends Annotation JavaDoc> AspectJAnnotation<A> findAnnotation(Method JavaDoc method, Class JavaDoc<A> toLookFor) {
88         A result = AnnotationUtils.findAnnotation(method, toLookFor);
89         if (result != null) {
90             return new AspectJAnnotation<A>(result);
91         }
92         else {
93             return null;
94         }
95     }
96
97
98     /** Logger available to subclasses */
99     protected final Log logger = LogFactory.getLog(getClass());
100     
101     protected final ParameterNameDiscoverer parameterNameDiscoverer;
102     
103     
104     protected AbstractAspectJAdvisorFactory() {
105         PrioritizedParameterNameDiscoverer prioritizedParameterNameDiscoverer = new PrioritizedParameterNameDiscoverer();
106         prioritizedParameterNameDiscoverer.addDiscoverer(ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER);
107         this.parameterNameDiscoverer = prioritizedParameterNameDiscoverer;
108     }
109
110     public boolean isAspect(Class JavaDoc<?> clazz) {
111         return (AjTypeSystem.getAjType(clazz).isAspect() && clazz.getAnnotation(Aspect.class) != null);
112     }
113
114     public void validate(Class JavaDoc<?> aspectClass) throws AopConfigException {
115         // If the parent has the annotation and isn't abstract it's an error
116
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
117                 !Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
118             throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
119                     aspectClass.getSuperclass().getName() + "]");
120         }
121
122         AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
123         if (!ajType.isAspect()) {
124             throw new NotAnAtAspectException(aspectClass);
125         }
126         if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) {
127             throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " +
128                     "This is not supported in Spring AOP.");
129         }
130         if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) {
131             throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " +
132                     "This is not supported in Spring AOP.");
133         }
134     }
135
136     /**
137      * The pointcut and advice annotations both have an "argNames" member which contains a
138      * comma-separated list of the argument names. We use this (if non-empty) to build the
139      * formal parameters for the pointcut.
140      */

141     protected AspectJExpressionPointcut createPointcutExpression(
142             Method JavaDoc annotatedMethod, Class JavaDoc declarationScope, String JavaDoc[] pointcutParameterNames) {
143
144         Class JavaDoc<?> [] pointcutParameterTypes = new Class JavaDoc<?>[0];
145         if (pointcutParameterNames != null) {
146             pointcutParameterTypes = extractPointcutParameterTypes(pointcutParameterNames,annotatedMethod);
147         }
148         
149         AspectJExpressionPointcut ajexp =
150                 new AspectJExpressionPointcut(declarationScope,pointcutParameterNames,pointcutParameterTypes);
151         ajexp.setLocation(annotatedMethod.toString());
152         return ajexp;
153     }
154     
155     /**
156      * Create the pointcut parameters needed by aspectj based on the given argument names
157      * and the argument types that are available from the adviceMethod. Needs to take into
158      * account (ignore) any JoinPoint based arguments as these are not pointcut context but
159      * rather part of the advice execution context (thisJoinPoint, thisJoinPointStaticPart)
160      */

161     private Class JavaDoc<?>[] extractPointcutParameterTypes(String JavaDoc[] argNames, Method JavaDoc adviceMethod) {
162         Class JavaDoc<?>[] ret = new Class JavaDoc<?>[argNames.length];
163         Class JavaDoc<?>[] paramTypes = adviceMethod.getParameterTypes();
164         if (argNames.length > paramTypes.length) {
165             throw new IllegalStateException JavaDoc("Expecting at least " + argNames.length +
166                     " arguments in the advice declaration, but only found " + paramTypes.length);
167         }
168         // Make the simplifying assumption for now that all of the JoinPoint based arguments
169
// come first in the advice declaration.
170
int typeOffset = paramTypes.length - argNames.length;
171         for (int i = 0; i < ret.length; i++) {
172             ret[i] = paramTypes[i+typeOffset];
173         }
174         return ret;
175     }
176
177
178     protected enum AspectJAnnotationType {
179         AtPointcut,
180         AtBefore,
181         AtAfter,
182         AtAfterReturning,
183         AtAfterThrowing,
184         AtAround
185     };
186
187
188     /**
189      * Class modelling an AspectJ annotation, exposing its type enumeration and
190      * pointcut String.
191      */

192     protected static class AspectJAnnotation<A extends Annotation JavaDoc> {
193
194         private static Map JavaDoc<Class JavaDoc,AspectJAnnotationType> annotationTypes = new HashMap JavaDoc<Class JavaDoc,AspectJAnnotationType>();
195
196         private static final String JavaDoc[] EXPRESSION_PROPERTIES = new String JavaDoc[]{"value", "pointcut"};
197
198         static {
199             annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
200             annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
201             annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
202             annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
203             annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
204             annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
205         }
206
207         private final A annotation;
208         private AspectJAnnotationType annotationType;
209         private final String JavaDoc expression;
210         private final String JavaDoc argNames;
211
212         public AspectJAnnotation(A aspectjAnnotation) {
213             this.annotation = aspectjAnnotation;
214             for (Class JavaDoc type : annotationTypes.keySet()) {
215                 if (type.isInstance(this.annotation)) {
216                     this.annotationType = annotationTypes.get(type);
217                     break;
218                 }
219             }
220             if (this.annotationType == null) {
221                 throw new IllegalStateException JavaDoc("Unknown annotation type: " + this.annotation.toString());
222             }
223
224             // We know these methods exist with the same name on each object,
225
// but need to invoke them reflectively as there isn't a common interface.
226
try {
227                 this.expression = resolveExpression();
228                 this.argNames = (String JavaDoc)
229                         this.annotation.getClass().getMethod("argNames", (Class JavaDoc[]) null).invoke(this.annotation);
230             }
231             catch (Exception JavaDoc ex) {
232                 throw new IllegalArgumentException JavaDoc(aspectjAnnotation + " cannot be an AspectJ annotation", ex);
233             }
234         }
235
236         private String JavaDoc resolveExpression() throws Exception JavaDoc {
237             String JavaDoc expression = null;
238             for (int i = 0; i < EXPRESSION_PROPERTIES.length; i++) {
239                 String JavaDoc methodName = EXPRESSION_PROPERTIES[i];
240                 Method JavaDoc method;
241                 try {
242                     method = this.annotation.getClass().getDeclaredMethod(methodName);
243                 }
244                 catch (NoSuchMethodException JavaDoc ex) {
245                     method = null;
246                 }
247                 if (method != null) {
248                     String JavaDoc candidate = (String JavaDoc) method.invoke(this.annotation);
249                     if (StringUtils.hasText(candidate)) {
250                         expression = candidate;
251                     }
252                 }
253             }
254             return expression;
255         }
256
257         public AspectJAnnotationType getAnnotationType() {
258             return this.annotationType;
259         }
260
261         public A getAnnotation() {
262             return this.annotation;
263         }
264
265         public String JavaDoc getPointcutExpression() {
266             return this.expression;
267         }
268
269         public String JavaDoc getArgNames() {
270             return this.argNames;
271         }
272
273         public String JavaDoc toString() {
274             return this.annotation.toString();
275         }
276     }
277
278
279     /**
280      * ParameterNameDiscoverer implementation that analyzes the arg names
281      * specified at the AspectJ annotation level.
282      */

283     private static class AspectJAnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {
284
285         public String JavaDoc[] getParameterNames(Method JavaDoc method) {
286             if (method.getParameterTypes().length == 0) {
287                 return new String JavaDoc[0];
288             }
289             AspectJAnnotation annotation = findAspectJAnnotationOnMethod(method);
290             if (annotation == null) {
291                 return null;
292             }
293             StringTokenizer JavaDoc strTok = new StringTokenizer JavaDoc(annotation.getArgNames(), ",");
294             if (strTok.countTokens() > 0) {
295                 String JavaDoc[] names = new String JavaDoc[strTok.countTokens()];
296                 for (int i = 0; i < names.length; i++) {
297                     names[i] = strTok.nextToken();
298                 }
299                 return names;
300             }
301             else {
302                 return null;
303             }
304         }
305         
306         public String JavaDoc[] getParameterNames(Constructor JavaDoc ctor) {
307             throw new UnsupportedOperationException JavaDoc("Spring AOP cannot handle constructor advice");
308         }
309     }
310
311 }
312
Popular Tags