KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > aop > aspectj > AspectJExpressionPointcut


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;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import org.aopalliance.intercept.MethodInvocation;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.aspectj.weaver.BCException;
29 import org.aspectj.weaver.reflect.ReflectionWorld;
30 import org.aspectj.weaver.tools.JoinPointMatch;
31 import org.aspectj.weaver.tools.PointcutExpression;
32 import org.aspectj.weaver.tools.PointcutParameter;
33 import org.aspectj.weaver.tools.PointcutParser;
34 import org.aspectj.weaver.tools.PointcutPrimitive;
35 import org.aspectj.weaver.tools.ShadowMatch;
36
37 import org.springframework.aop.ClassFilter;
38 import org.springframework.aop.IntroductionAwareMethodMatcher;
39 import org.springframework.aop.MethodMatcher;
40 import org.springframework.aop.ProxyMethodInvocation;
41 import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
42 import org.springframework.aop.support.AbstractExpressionPointcut;
43 import org.springframework.aop.support.AopUtils;
44 import org.springframework.util.ObjectUtils;
45 import org.springframework.util.StringUtils;
46
47 /**
48  * Spring {@link org.springframework.aop.Pointcut} implementation
49  * that uses the AspectJ weaver to evaluate a pointcut expression.
50  *
51  * <p>The pointcut expression value is an AspectJ expression. This can
52  * reference other pointcuts and use composition and other operations.
53  *
54  * <p>Naturally, as this is to be processed by Spring AOP's proxy-based model,
55  * only method execution pointcuts are supported.
56  *
57  * @author Rob Harrop
58  * @author Adrian Colyer
59  * @author Rod Johnson
60  * @author Juergen Hoeller
61  * @since 2.0
62  */

63 public class AspectJExpressionPointcut extends AbstractExpressionPointcut
64         implements ClassFilter, IntroductionAwareMethodMatcher {
65
66     private static final Set JavaDoc DEFAULT_SUPPORTED_PRIMITIVES = new HashSet JavaDoc();
67
68     static {
69         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
70         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
71         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
72         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
73         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
74         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
75         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
76         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
77         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
78         DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
79     }
80
81
82     private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class);
83
84     private final Map JavaDoc shadowMapCache = new HashMap JavaDoc();
85
86     private PointcutParser pointcutParser;
87
88     private Class JavaDoc pointcutDeclarationScope;
89
90     private String JavaDoc[] pointcutParameterNames = new String JavaDoc[0];
91
92     private Class JavaDoc[] pointcutParameterTypes = new Class JavaDoc[0];
93
94     private PointcutExpression pointcutExpression;
95
96
97     /**
98      * Create a new default AspectJExpressionPointcut.
99      */

100     public AspectJExpressionPointcut() {
101         this(DEFAULT_SUPPORTED_PRIMITIVES);
102     }
103
104     /**
105      * Create a new AspectJExpressionPointcut with the given supported primitives.
106      * @param supportedPrimitives Set of {@link org.aspectj.weaver.tools.PointcutPrimitive}
107      * instances
108      */

109     public AspectJExpressionPointcut(Set JavaDoc supportedPrimitives) {
110         this.pointcutParser =
111                 PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
112                         supportedPrimitives);
113     }
114
115     /**
116      * Create a new AspectJExpressionPointcut with the given settings.
117      * @param declarationScope the declaration scope for the pointcut
118      * @param paramNames the parameter names for the pointcut
119      * @param paramTypes the parameter types for the pointcut
120      */

121     public AspectJExpressionPointcut(Class JavaDoc declarationScope, String JavaDoc[] paramNames, Class JavaDoc[] paramTypes) {
122         this(DEFAULT_SUPPORTED_PRIMITIVES);
123         this.pointcutDeclarationScope = declarationScope;
124         if (paramNames.length != paramTypes.length) {
125             throw new IllegalStateException JavaDoc(
126                     "Number of pointcut parameter names must match number of pointcut parameter types");
127         }
128         this.pointcutParameterNames = paramNames;
129         this.pointcutParameterTypes = paramTypes;
130     }
131
132
133     /**
134      * Set the declaration scope for the pointcut.
135      */

136     public void setPointcutDeclarationScope(Class JavaDoc pointcutDeclarationScope) {
137         this.pointcutDeclarationScope = pointcutDeclarationScope;
138     }
139
140     /**
141      * Set the parameter names for the pointcut.
142      */

143     public void setParameterNames(String JavaDoc[] names) {
144         this.pointcutParameterNames = names;
145     }
146
147     /**
148      * Set the parameter types for the pointcut.
149      */

150     public void setParameterTypes(Class JavaDoc[] types) {
151         this.pointcutParameterTypes = types;
152     }
153
154
155     public ClassFilter getClassFilter() {
156         checkReadyToMatch();
157         return this;
158     }
159
160     public MethodMatcher getMethodMatcher() {
161         checkReadyToMatch();
162         return this;
163     }
164
165
166     /**
167      * Check whether this pointcut is ready to match,
168      * lazily building the underlying AspectJ pointcut expression.
169      */

170     private void checkReadyToMatch() {
171         if (getExpression() == null) {
172             throw new IllegalStateException JavaDoc("Must set property 'expression' before attempting to match");
173         }
174         if (this.pointcutExpression == null) {
175             this.pointcutExpression = buildPointcutExpression();
176         }
177     }
178
179     /**
180      * Build the underlying AspectJ pointcut expression.
181      */

182     private PointcutExpression buildPointcutExpression() {
183         PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
184         for (int i = 0; i < pointcutParameters.length; i++) {
185             pointcutParameters[i] = this.pointcutParser.createPointcutParameter(
186                     this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
187         }
188         return this.pointcutParser.parsePointcutExpression(
189                 replaceBooleanOperators(getExpression()), this.pointcutDeclarationScope, pointcutParameters);
190     }
191
192     /**
193      * If a pointcut expression has been specified in XML, the user cannot
194      * write <code>and</code> as "&&" (though &amp;&amp; will work).
195      * We also allow <code>and</code> between two pointcut sub-expressions.
196      * <p>This method converts back to <code>&&</code> for the AspectJ pointcut parser.
197      */

198     private String JavaDoc replaceBooleanOperators(String JavaDoc pcExpr) {
199         pcExpr = StringUtils.replace(pcExpr," and "," && ");
200         pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
201         pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
202         return pcExpr;
203     }
204
205     /**
206      * Return the underlying AspectJ pointcut expression.
207      */

208     public PointcutExpression getPointcutExpression() {
209         checkReadyToMatch();
210         return this.pointcutExpression;
211     }
212
213
214     public boolean matches(Class JavaDoc targetClass) {
215         checkReadyToMatch();
216         try {
217             return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
218         }
219         catch (BCException ex) {
220             logger.debug("PointcutExpression matching rejected target class", ex);
221             return false;
222         }
223     }
224
225     public boolean matches(Method JavaDoc method, Class JavaDoc targetClass, boolean beanHasIntroductions) {
226         checkReadyToMatch();
227         Method JavaDoc targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
228         ShadowMatch shadowMatch = null;
229         try {
230             shadowMatch = getShadowMatch(targetMethod, method);
231         }
232         catch (ReflectionWorld.ReflectionWorldException ex) {
233             // Could neither introspect the target class nor the proxy class ->
234
// let's simply consider this method as non-matching.
235
return false;
236         }
237
238         // Special handling for this, target, @this, @target, @annotation
239
// in Spring - we can optimize since we know we have exactly this class,
240
// and there will never be matching subclass at runtime.
241
if (shadowMatch.alwaysMatches()) {
242             return true;
243         }
244         else if (shadowMatch.neverMatches()) {
245             return false;
246         }
247         else {
248           // the maybe case
249
return (beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch));
250         }
251     }
252
253     public boolean matches(Method JavaDoc method, Class JavaDoc targetClass) {
254         return matches(method, targetClass, false);
255     }
256
257     public boolean isRuntime() {
258         checkReadyToMatch();
259         return this.pointcutExpression.mayNeedDynamicTest();
260     }
261
262     public boolean matches(Method JavaDoc method, Class JavaDoc targetClass, Object JavaDoc[] args) {
263         checkReadyToMatch();
264         Method JavaDoc targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
265         ShadowMatch shadowMatch = null;
266         try {
267             shadowMatch = getShadowMatch(targetMethod, method);
268         }
269         catch (ReflectionWorld.ReflectionWorldException ex) {
270             // Could neither introspect the target class nor the proxy class ->
271
// let's simply consider this method as non-matching.
272
return false;
273         }
274
275         // Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
276
// consistent with return of MethodInvocationProceedingJoinPoint
277
ProxyMethodInvocation pmi = null;
278         Object JavaDoc targetObject = null;
279         Object JavaDoc thisObject = null;
280         try {
281             MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
282             targetObject = mi.getThis();
283             if (!(mi instanceof ProxyMethodInvocation)) {
284                 throw new IllegalStateException JavaDoc("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
285             }
286             pmi = (ProxyMethodInvocation) mi;
287             thisObject = pmi.getProxy();
288         }
289         catch (IllegalStateException JavaDoc ex) {
290             // No current invocation...
291
// TODO: Should we really proceed here?
292
logger.debug("Couldn't access current invocation - matching with limited context: " + ex);
293         }
294
295         JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
296         if (joinPointMatch.matches() && pmi != null) {
297             bindParameters(pmi, joinPointMatch);
298         }
299         return joinPointMatch.matches();
300     }
301
302
303     /**
304      * A match test returned maybe - if there are any subtype sensitive variables
305      * involved in the test (this, target, at_this, at_target, at_annotation) then
306      * we say this is not a match as in Spring there will never be a different
307      * runtime subtype.
308      */

309     private boolean matchesIgnoringSubtypes(ShadowMatch shadowMatch) {
310         return !(new RuntimeTestWalker(shadowMatch).testsSubtypeSensitiveVars());
311     }
312
313     private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
314         // Note: Can't use JoinPointMatch.getClass().getName() as the key, since
315
// Spring AOP does all the matching at a join point, and then all the invocations
316
// under this scenario, if we just use JoinPointMatch as the key, then
317
// 'last man wins' which is not what we want at all.
318
// Using the expression is guaranteed to be safe, since 2 identical expressions
319
// are guaranteed to bind in exactly the same way.
320
invocation.setUserAttribute(getExpression(), jpm);
321     }
322
323     private ShadowMatch getShadowMatch(Method JavaDoc targetMethod, Method JavaDoc originalMethod) {
324         synchronized (this.shadowMapCache) {
325             ShadowMatch shadowMatch = (ShadowMatch) this.shadowMapCache.get(targetMethod);
326             if (shadowMatch == null) {
327                 try {
328                     shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
329                 }
330                 catch (ReflectionWorld.ReflectionWorldException ex) {
331                     // Failed to introspect target method, probably because it has been loaded
332
// in a special ClassLoader. Let's try the original method instead...
333
if (targetMethod == originalMethod) {
334                         throw ex;
335                     }
336                     shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
337                 }
338                 this.shadowMapCache.put(targetMethod, shadowMatch);
339             }
340             return shadowMatch;
341         }
342     }
343
344
345     public boolean equals(Object JavaDoc other) {
346         if (this == other) {
347             return true;
348         }
349         if (!(other instanceof AspectJExpressionPointcut)) {
350             return false;
351         }
352         AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
353         return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
354                 ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
355                 ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
356                 ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
357     }
358
359     public int hashCode() {
360         int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
361         hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
362         hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
363         hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
364         return hashCode;
365     }
366
367     public String JavaDoc toString() {
368         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
369         sb.append("AspectJExpressionPointcut: ");
370         if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
371             sb.append("(");
372             for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
373                 sb.append(this.pointcutParameterTypes[i].getName());
374                 sb.append(" ");
375                 sb.append(this.pointcutParameterNames[i]);
376                 if ((i+1) < this.pointcutParameterTypes.length) {
377                     sb.append(", ");
378                 }
379             }
380             sb.append(")");
381         }
382         sb.append(" ");
383         if (getExpression() != null) {
384             sb.append(getExpression());
385         }
386         else {
387             sb.append("<pointcut expression not set>");
388         }
389         return sb.toString();
390     }
391
392 }
393
Popular Tags