001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.aspectj;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.aopalliance.aop.Advice;
025: import org.aopalliance.intercept.MethodInvocation;
026: import org.aspectj.lang.JoinPoint;
027: import org.aspectj.lang.ProceedingJoinPoint;
028: import org.aspectj.weaver.tools.JoinPointMatch;
029: import org.aspectj.weaver.tools.PointcutParameter;
030:
031: import org.springframework.aop.AopInvocationException;
032: import org.springframework.aop.MethodMatcher;
033: import org.springframework.aop.Pointcut;
034: import org.springframework.aop.ProxyMethodInvocation;
035: import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
036: import org.springframework.aop.support.ComposablePointcut;
037: import org.springframework.aop.support.MethodMatchers;
038: import org.springframework.aop.support.StaticMethodMatcher;
039: import org.springframework.core.JdkVersion;
040: import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
041: import org.springframework.core.ParameterNameDiscoverer;
042: import org.springframework.core.PrioritizedParameterNameDiscoverer;
043: import org.springframework.util.Assert;
044: import org.springframework.util.ClassUtils;
045: import org.springframework.util.CollectionUtils;
046: import org.springframework.util.ReflectionUtils;
047: import org.springframework.util.StringUtils;
048:
049: /**
050: * Base class for AOP Alliance {@link org.aopalliance.aop.Advice} classes
051: * wrapping an AspectJ aspect or an AspectJ-annotated advice method.
052: *
053: * @author Rod Johnson
054: * @author Adrian Colyer
055: * @author Juergen Hoeller
056: * @author Ramnivas Laddad
057: * @since 2.0
058: */
059: public abstract class AbstractAspectJAdvice implements Advice,
060: AspectJPrecedenceInformation {
061:
062: /**
063: * Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint.
064: */
065: protected static final String JOIN_POINT_KEY = JoinPoint.class
066: .getName();
067:
068: /**
069: * Lazily instantiate joinpoint for the current invocation.
070: * Requires MethodInvocation to be bound with ExposeInvocationInterceptor.
071: * <p>Do not use if access is available to the current ReflectiveMethodInvocation
072: * (in an around advice).
073: * @return current AspectJ joinpoint, or through an exception if we're not in a
074: * Spring AOP invocation.
075: */
076: public static JoinPoint currentJoinPoint() {
077: MethodInvocation mi = ExposeInvocationInterceptor
078: .currentInvocation();
079: if (!(mi instanceof ProxyMethodInvocation)) {
080: throw new IllegalStateException(
081: "MethodInvocation is not a Spring ProxyMethodInvocation: "
082: + mi);
083: }
084: ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
085: JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
086: if (jp == null) {
087: jp = new MethodInvocationProceedingJoinPoint(pmi);
088: pmi.setUserAttribute(JOIN_POINT_KEY, jp);
089: }
090: return jp;
091: }
092:
093: protected final Method aspectJAdviceMethod;
094:
095: /** The total number of arguments we have to populate on advice dispatch */
096: private final int adviceInvocationArgumentCount;
097:
098: private final AspectJExpressionPointcut pointcut;
099:
100: private final AspectInstanceFactory aspectInstanceFactory;
101:
102: /**
103: * The name of the aspect (ref bean) in which this advice was defined (used
104: * when determining advice precedence so that we can determine
105: * whether two pieces of advice come from the same aspect).
106: */
107: private String aspectName;
108:
109: /**
110: * The order of declaration of this advice within the aspect.
111: */
112: private int declarationOrder;
113:
114: /**
115: * This will be non-null if the creator of this advice object knows the argument names
116: * and sets them explicitly
117: */
118: private String[] argumentNames = null;
119:
120: /** Non-null if after throwing advice binds the thrown value */
121: private String throwingName = null;
122:
123: /** Non-null if after returning advice binds the return value */
124: private String returningName = null;
125:
126: private Class discoveredReturningType = Object.class;
127:
128: private Class discoveredThrowingType = Object.class;
129:
130: /**
131: * Index for thisJoinPoint argument (currently only
132: * supported at index 0 if present at all)
133: */
134: private int joinPointArgumentIndex = -1;
135:
136: /**
137: * Index for thisJoinPointStaticPart argument (currently only
138: * supported at index 0 if present at all)
139: */
140: private int joinPointStaticPartArgumentIndex = -1;
141:
142: private Map argumentBindings = null;
143:
144: private boolean argumentsIntrospected = false;
145:
146: // The actual type is java.lang.reflect.Type,
147: // but for JDK 1.4 compatibility we use Object as the static type.
148: private Object discoveredReturningGenericType;
149:
150: // Note: Unlike return type, no such generic information is needed for the throwing type,
151: // since Java doesn't allow exception types to be parameterized.
152:
153: /**
154: * Create a new AbstractAspectJAdvice for the given advice method.
155: * @param aspectJAdviceMethod the AspectJ-style advice method
156: * @param pointcut the AspectJ expression pointcut
157: * @param aspectInstanceFactory the factory for aspect instances
158: */
159: public AbstractAspectJAdvice(Method aspectJAdviceMethod,
160: AspectJExpressionPointcut pointcut,
161: AspectInstanceFactory aspectInstanceFactory) {
162:
163: Assert.notNull(aspectJAdviceMethod,
164: "Advice method must not be null");
165: this .aspectJAdviceMethod = aspectJAdviceMethod;
166: this .adviceInvocationArgumentCount = this .aspectJAdviceMethod
167: .getParameterTypes().length;
168: this .pointcut = pointcut;
169: this .aspectInstanceFactory = aspectInstanceFactory;
170: }
171:
172: /**
173: * Return the AspectJ-style advice method.
174: */
175: public final Method getAspectJAdviceMethod() {
176: return this .aspectJAdviceMethod;
177: }
178:
179: /**
180: * Return the AspectJ expression pointcut.
181: */
182: public final AspectJExpressionPointcut getPointcut() {
183: calculateArgumentBindings();
184: return this .pointcut;
185: }
186:
187: /**
188: * Build a 'safe' pointcut that excludes the AspectJ advice method itself.
189: * @return a composable pointcut that builds on the original AspectJ expression pointcut
190: * @see #getPointcut()
191: */
192: public final Pointcut buildSafePointcut() {
193: Pointcut pc = getPointcut();
194: MethodMatcher safeMethodMatcher = MethodMatchers.intersection(
195: new AdviceExcludingMethodMatcher(
196: this .aspectJAdviceMethod), pc
197: .getMethodMatcher());
198: return new ComposablePointcut(pc.getClassFilter(),
199: safeMethodMatcher);
200: }
201:
202: /**
203: * Return the factory for aspect instances.
204: */
205: public final AspectInstanceFactory getAspectInstanceFactory() {
206: return this .aspectInstanceFactory;
207: }
208:
209: /**
210: * Return the ClassLoader for aspect instances.
211: */
212: public final ClassLoader getAspectClassLoader() {
213: return this .aspectInstanceFactory.getAspectClassLoader();
214: }
215:
216: public int getOrder() {
217: return this .aspectInstanceFactory.getOrder();
218: }
219:
220: public void setAspectName(String name) {
221: this .aspectName = name;
222: }
223:
224: public String getAspectName() {
225: return this .aspectName;
226: }
227:
228: /**
229: * Sets the <b>declaration order</b> of this advice within the aspect
230: */
231: public void setDeclarationOrder(int order) {
232: this .declarationOrder = order;
233: }
234:
235: public int getDeclarationOrder() {
236: return this .declarationOrder;
237: }
238:
239: /**
240: * Set by creator of this advice object if the argument names are known.
241: * <p>This could be for example because they have been explicitly specified in XML,
242: * or in an advice annotation.
243: * @param argNames comma delimited list of arg names
244: */
245: public void setArgumentNames(String argNames) {
246: String[] tokens = StringUtils
247: .commaDelimitedListToStringArray(argNames);
248: setArgumentNamesFromStringArray(tokens);
249: }
250:
251: public void setArgumentNamesFromStringArray(String[] args) {
252: this .argumentNames = new String[args.length];
253: for (int i = 0; i < args.length; i++) {
254: this .argumentNames[i] = StringUtils.trimWhitespace(args[i]);
255: if (!isVariableName(this .argumentNames[i])) {
256: throw new IllegalArgumentException(
257: "'argumentNames' property of AbstractAspectJAdvice contains an argument name '"
258: + this .argumentNames[i]
259: + "' that is not a valid Java identifier");
260: }
261: }
262: if (argumentNames != null) {
263: if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) {
264: // May need to add implicit join point arg name...
265: Class firstArgType = aspectJAdviceMethod
266: .getParameterTypes()[0];
267: if (firstArgType == JoinPoint.class
268: || firstArgType == ProceedingJoinPoint.class
269: || firstArgType == JoinPoint.StaticPart.class) {
270: String[] oldNames = argumentNames;
271: argumentNames = new String[oldNames.length + 1];
272: argumentNames[0] = "THIS_JOIN_POINT";
273: System.arraycopy(oldNames, 0, argumentNames, 1,
274: oldNames.length);
275: }
276: }
277: }
278: }
279:
280: public void setReturningName(String name) {
281: throw new UnsupportedOperationException(
282: "Only afterReturning advice can be used to bind a return value");
283: }
284:
285: /**
286: * We need to hold the returning name at this level for argument binding calculations,
287: * this method allows the afterReturning advice subclass to set the name.
288: */
289: protected void setReturningNameNoCheck(String name) {
290: // name could be a variable or a type...
291: if (isVariableName(name)) {
292: this .returningName = name;
293: } else {
294: // assume a type
295: try {
296: this .discoveredReturningType = ClassUtils.forName(name,
297: getAspectClassLoader());
298: } catch (Throwable ex) {
299: throw new IllegalArgumentException(
300: "Returning name '"
301: + name
302: + "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. "
303: + "Root cause: " + ex);
304: }
305: }
306: }
307:
308: protected Class getDiscoveredReturningType() {
309: return this .discoveredReturningType;
310: }
311:
312: protected Object getDiscoveredReturningGenericType() {
313: return this .discoveredReturningGenericType;
314: }
315:
316: public void setThrowingName(String name) {
317: throw new UnsupportedOperationException(
318: "Only afterThrowing advice can be used to bind a thrown exception");
319: }
320:
321: /**
322: * We need to hold the throwing name at this level for argument binding calculations,
323: * this method allows the afterThrowing advice subclass to set the name.
324: */
325: protected void setThrowingNameNoCheck(String name) {
326: // name could be a variable or a type...
327: if (isVariableName(name)) {
328: this .throwingName = name;
329: } else {
330: // assume a type
331: try {
332: this .discoveredThrowingType = ClassUtils.forName(name,
333: getAspectClassLoader());
334: } catch (Throwable ex) {
335: throw new IllegalArgumentException(
336: "Throwing name '"
337: + name
338: + "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. "
339: + "Root cause: " + ex);
340: }
341: }
342: }
343:
344: protected Class getDiscoveredThrowingType() {
345: return this .discoveredThrowingType;
346: }
347:
348: private boolean isVariableName(String name) {
349: char[] chars = name.toCharArray();
350: if (!Character.isJavaIdentifierStart(chars[0])) {
351: return false;
352: }
353: for (int i = 1; i < chars.length; i++) {
354: if (!Character.isJavaIdentifierPart(chars[i])) {
355: return false;
356: }
357: }
358: return true;
359: }
360:
361: /**
362: * Do as much work as we can as part of the set-up so that argument binding
363: * on subsequent advice invocations can be as fast as possible.
364: * <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we
365: * pass a JoinPoint in that position (ProceedingJoinPoint for around advice).
366: * <p>If the first argument is of type <code>JoinPoint.StaticPart</code>
367: * then we pass a <code>JoinPoint.StaticPart</code> in that position.
368: * <p>Remaining arguments have to be bound by pointcut evaluation at
369: * a given join point. We will get back a map from argument name to
370: * value. We need to calculate which advice parameter needs to be bound
371: * to which argument name. There are multiple strategies for determining
372: * this binding, which are arranged in a ChainOfResponsibility.
373: */
374: public synchronized final void calculateArgumentBindings() {
375: // The simple case... nothing to bind.
376: if (this .argumentsIntrospected
377: || this .adviceInvocationArgumentCount == 0) {
378: return;
379: }
380:
381: int numUnboundArgs = this .adviceInvocationArgumentCount;
382: Class[] parameterTypes = this .aspectJAdviceMethod
383: .getParameterTypes();
384: if (maybeBindJoinPoint(parameterTypes[0])
385: || maybeBindProceedingJoinPoint(parameterTypes[0])) {
386: numUnboundArgs--;
387: } else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
388: numUnboundArgs--;
389: }
390:
391: if (numUnboundArgs > 0) {
392: // need to bind arguments by name as returned from the pointcut match
393: bindArgumentsByName(numUnboundArgs);
394: }
395:
396: this .argumentsIntrospected = true;
397: }
398:
399: private boolean maybeBindJoinPoint(Class candidateParameterType) {
400: if (candidateParameterType.equals(JoinPoint.class)) {
401: this .joinPointArgumentIndex = 0;
402: return true;
403: } else {
404: return false;
405: }
406: }
407:
408: private boolean maybeBindProceedingJoinPoint(
409: Class candidateParameterType) {
410: if (candidateParameterType.equals(ProceedingJoinPoint.class)) {
411: if (!supportsProceedingJoinPoint()) {
412: throw new IllegalArgumentException(
413: "ProceedingJoinPoint is only supported for around advice");
414: }
415: this .joinPointArgumentIndex = 0;
416: return true;
417: } else {
418: return false;
419: }
420: }
421:
422: protected boolean supportsProceedingJoinPoint() {
423: return false;
424: }
425:
426: private boolean maybeBindJoinPointStaticPart(
427: Class candidateParameterType) {
428: if (candidateParameterType.equals(JoinPoint.StaticPart.class)) {
429: this .joinPointStaticPartArgumentIndex = 0;
430: return true;
431: } else {
432: return false;
433: }
434: }
435:
436: private void bindArgumentsByName(int numArgumentsExpectingToBind) {
437: if (this .argumentNames == null) {
438: this .argumentNames = createParameterNameDiscoverer()
439: .getParameterNames(this .aspectJAdviceMethod);
440: }
441: if (this .argumentNames != null) {
442: // We have been able to determine the arg names.
443: bindExplicitArguments(numArgumentsExpectingToBind);
444: } else {
445: throw new IllegalStateException(
446: "Advice method ["
447: + this .aspectJAdviceMethod.getName()
448: + "] "
449: + "requires "
450: + numArgumentsExpectingToBind
451: + " arguments to be bound by name, but "
452: + "the argument names were not specified and could not be discovered.");
453: }
454: }
455:
456: /**
457: * Create a ParameterNameDiscoverer to be used for argument binding.
458: * <p>The default implementation creates a {@link PrioritizedParameterNameDiscoverer}
459: * containing a {@link LocalVariableTableParameterNameDiscoverer} and an
460: * {@link AspectJAdviceParameterNameDiscoverer}.
461: */
462: protected ParameterNameDiscoverer createParameterNameDiscoverer() {
463: // We need to discover them, or if that fails, guess,
464: // and if we can't guess with 100% accuracy, fail.
465: PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer();
466: discoverer
467: .addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
468: AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer = new AspectJAdviceParameterNameDiscoverer(
469: this .pointcut.getExpression());
470: adviceParameterNameDiscoverer
471: .setReturningName(this .returningName);
472: adviceParameterNameDiscoverer
473: .setThrowingName(this .throwingName);
474: // Last in chain, so if we're called and we fail, that's bad...
475: adviceParameterNameDiscoverer.setRaiseExceptions(true);
476: discoverer.addDiscoverer(adviceParameterNameDiscoverer);
477: return discoverer;
478: }
479:
480: private void bindExplicitArguments(int numArgumentsLeftToBind) {
481: this .argumentBindings = new HashMap();
482:
483: int numExpectedArgumentNames = this .aspectJAdviceMethod
484: .getParameterTypes().length;
485: if (this .argumentNames.length != numExpectedArgumentNames) {
486: throw new IllegalStateException(
487: "Expecting to find "
488: + numExpectedArgumentNames
489: + " arguments to bind by name in advice, but actually found "
490: + this .argumentNames.length + " arguments.");
491: }
492:
493: // So we match in number...
494: int argumentIndexOffset = this .adviceInvocationArgumentCount
495: - numArgumentsLeftToBind;
496: for (int i = argumentIndexOffset; i < this .argumentNames.length; i++) {
497: this .argumentBindings.put(this .argumentNames[i],
498: new Integer(i));
499: }
500:
501: // Check that returning and throwing were in the argument names list if
502: // specified, and find the discovered argument types.
503: if (this .returningName != null) {
504: if (!this .argumentBindings.containsKey(this .returningName)) {
505: throw new IllegalStateException(
506: "Returning argument name '"
507: + this .returningName
508: + "' was not bound in advice arguments");
509: } else {
510: Integer index = (Integer) this .argumentBindings
511: .get(this .returningName);
512: this .discoveredReturningType = this .aspectJAdviceMethod
513: .getParameterTypes()[index.intValue()];
514: if (JdkVersion.isAtLeastJava15()) {
515: this .discoveredReturningGenericType = this .aspectJAdviceMethod
516: .getGenericParameterTypes()[index
517: .intValue()];
518: }
519: }
520: }
521: if (this .throwingName != null) {
522: if (!this .argumentBindings.containsKey(this .throwingName)) {
523: throw new IllegalStateException(
524: "Throwing argument name '" + this .throwingName
525: + "' was not bound in advice arguments");
526: } else {
527: Integer index = (Integer) this .argumentBindings
528: .get(this .throwingName);
529: this .discoveredThrowingType = this .aspectJAdviceMethod
530: .getParameterTypes()[index.intValue()];
531: }
532: }
533:
534: // configure the pointcut expression accordingly.
535: configurePointcutParameters(argumentIndexOffset);
536: }
537:
538: /**
539: * All parameters from argumentIndexOffset onwards are candidates for
540: * pointcut parameters - but returning and throwing vars are handled differently
541: * and must be removed from the list if present.
542: */
543: private void configurePointcutParameters(int argumentIndexOffset) {
544: int numParametersToRemove = argumentIndexOffset;
545: if (this .returningName != null) {
546: numParametersToRemove++;
547: }
548: if (this .throwingName != null) {
549: numParametersToRemove++;
550: }
551: String[] pointcutParameterNames = new String[this .argumentNames.length
552: - numParametersToRemove];
553: Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length];
554: Class[] methodParameterTypes = this .aspectJAdviceMethod
555: .getParameterTypes();
556:
557: int index = 0;
558: for (int i = 0; i < this .argumentNames.length; i++) {
559: if (i < argumentIndexOffset) {
560: continue;
561: }
562: if (this .argumentNames[i].equals(this .returningName)
563: || this .argumentNames[i].equals(this .throwingName)) {
564: continue;
565: }
566: pointcutParameterNames[index] = this .argumentNames[i];
567: pointcutParameterTypes[index] = methodParameterTypes[i];
568: index++;
569: }
570:
571: this .pointcut.setParameterNames(pointcutParameterNames);
572: this .pointcut.setParameterTypes(pointcutParameterTypes);
573: }
574:
575: /**
576: * Take the arguments at the method execution join point and output a set of arguments
577: * to the advice method
578: * @param jp the current JoinPoint
579: * @param jpMatch the join point match that matched this execution join point
580: * @param returnValue the return value from the method execution (may be null)
581: * @param ex the exception thrown by the method execution (may be null)
582: * @return the empty array if there are no arguments
583: */
584: protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch,
585: Object returnValue, Throwable ex) {
586: calculateArgumentBindings();
587:
588: // AMC start
589: Object[] adviceInvocationArgs = new Object[this .adviceInvocationArgumentCount];
590: int numBound = 0;
591:
592: if (this .joinPointArgumentIndex != -1) {
593: adviceInvocationArgs[this .joinPointArgumentIndex] = jp;
594: numBound++;
595: } else if (this .joinPointStaticPartArgumentIndex != -1) {
596: adviceInvocationArgs[this .joinPointStaticPartArgumentIndex] = jp
597: .getStaticPart();
598: numBound++;
599: }
600:
601: if (!CollectionUtils.isEmpty(this .argumentBindings)) {
602: // binding from pointcut match
603: if (jpMatch != null) {
604: PointcutParameter[] parameterBindings = jpMatch
605: .getParameterBindings();
606: for (int i = 0; i < parameterBindings.length; i++) {
607: PointcutParameter parameter = parameterBindings[i];
608: String name = parameter.getName();
609: Integer index = (Integer) this .argumentBindings
610: .get(name);
611: adviceInvocationArgs[index.intValue()] = parameter
612: .getBinding();
613: numBound++;
614: }
615: }
616: // binding from returning clause
617: if (this .returningName != null) {
618: Integer index = (Integer) this .argumentBindings
619: .get(this .returningName);
620: adviceInvocationArgs[index.intValue()] = returnValue;
621: numBound++;
622: }
623: // binding from thrown exception
624: if (this .throwingName != null) {
625: Integer index = (Integer) this .argumentBindings
626: .get(this .throwingName);
627: adviceInvocationArgs[index.intValue()] = ex;
628: numBound++;
629: }
630: }
631:
632: if (numBound != this .adviceInvocationArgumentCount) {
633: throw new IllegalStateException("Required to bind "
634: + this .adviceInvocationArgumentCount
635: + " arguments, but only bound " + numBound
636: + " (JoinPointMatch "
637: + (jpMatch == null ? "was NOT" : "WAS")
638: + " bound in invocation)");
639: }
640:
641: return adviceInvocationArgs;
642: }
643:
644: /**
645: * Invoke the advice method.
646: * @param jpMatch the JoinPointMatch that matched this execution join point
647: * @param returnValue the return value from the method execution (may be null)
648: * @param ex the exception thrown by the method execution (may be null)
649: * @return the invocation result
650: * @throws Throwable in case of invocation failure
651: */
652: protected Object invokeAdviceMethod(JoinPointMatch jpMatch,
653: Object returnValue, Throwable ex) throws Throwable {
654: return invokeAdviceMethodWithGivenArgs(argBinding(
655: getJoinPoint(), jpMatch, returnValue, ex));
656: }
657:
658: // As above, but in this case we are given the join point.
659: protected Object invokeAdviceMethod(JoinPoint jp,
660: JoinPointMatch jpMatch, Object returnValue, Throwable t)
661: throws Throwable {
662:
663: return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch,
664: returnValue, t));
665: }
666:
667: protected Object invokeAdviceMethodWithGivenArgs(Object[] args)
668: throws Throwable {
669: Object[] actualArgs = args;
670: if (this .aspectJAdviceMethod.getParameterTypes().length == 0) {
671: actualArgs = null;
672: }
673: try {
674: ReflectionUtils.makeAccessible(this .aspectJAdviceMethod);
675: // TODO AopUtils.invokeJoinpointUsingReflection
676: return this .aspectJAdviceMethod.invoke(
677: this .aspectInstanceFactory.getAspectInstance(),
678: actualArgs);
679: } catch (IllegalArgumentException ex) {
680: throw new AopInvocationException(
681: "Mismatch on arguments to advice method ["
682: + this .aspectJAdviceMethod
683: + "]; pointcut expression ["
684: + this .pointcut.getPointcutExpression()
685: + "]", ex);
686: } catch (InvocationTargetException ex) {
687: throw ex.getTargetException();
688: }
689: }
690:
691: /**
692: * Overridden in around advice to return proceeding join point.
693: */
694: protected JoinPoint getJoinPoint() {
695: return currentJoinPoint();
696: }
697:
698: /**
699: * Get the current join point match at the join point we are being dispatched on.
700: */
701: protected JoinPointMatch getJoinPointMatch() {
702: MethodInvocation mi = ExposeInvocationInterceptor
703: .currentInvocation();
704: if (!(mi instanceof ProxyMethodInvocation)) {
705: throw new IllegalStateException(
706: "MethodInvocation is not a Spring ProxyMethodInvocation: "
707: + mi);
708: }
709: return getJoinPointMatch((ProxyMethodInvocation) mi);
710: }
711:
712: // Note: We can't use JoinPointMatch.getClass().getName() as the key, since
713: // Spring AOP does all the matching at a join point, and then all the invocations.
714: // Under this scenario, if we just use JoinPointMatch as the key, then
715: // 'last man wins' which is not what we want at all.
716: // Using the expression is guaranteed to be safe, since 2 identical expressions
717: // are guaranteed to bind in exactly the same way.
718: protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
719: return (JoinPointMatch) pmi.getUserAttribute(this .pointcut
720: .getExpression());
721: }
722:
723: public String toString() {
724: return getClass().getName() + ": advice method ["
725: + this .aspectJAdviceMethod + "]; " + "aspect name '"
726: + this .aspectName + "'";
727: }
728:
729: /**
730: * MethodMatcher that excludes the specified advice method.
731: * @see AbstractAspectJAdvice#buildSafePointcut()
732: */
733: private static class AdviceExcludingMethodMatcher extends
734: StaticMethodMatcher {
735:
736: private final Method adviceMethod;
737:
738: public AdviceExcludingMethodMatcher(Method adviceMethod) {
739: this .adviceMethod = adviceMethod;
740: }
741:
742: public boolean matches(Method method, Class targetClass) {
743: return !this.adviceMethod.equals(method);
744: }
745: }
746:
747: }
|