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.support;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Modifier;
022: import java.lang.reflect.Proxy;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Set;
028:
029: import org.springframework.aop.Advisor;
030: import org.springframework.aop.AopInvocationException;
031: import org.springframework.aop.IntroductionAdvisor;
032: import org.springframework.aop.IntroductionAwareMethodMatcher;
033: import org.springframework.aop.MethodMatcher;
034: import org.springframework.aop.Pointcut;
035: import org.springframework.aop.PointcutAdvisor;
036: import org.springframework.aop.SpringProxy;
037: import org.springframework.aop.TargetClassAware;
038: import org.springframework.util.Assert;
039: import org.springframework.util.ClassUtils;
040:
041: /**
042: * Utility methods for AOP support code.
043: * Mainly for internal use within Spring's AOP support.
044: *
045: * <p>See {@link org.springframework.aop.framework.AopProxyUtils} for a
046: * collection of framework-specific AOP utility methods which depend
047: * on internals of Spring's AOP framework implementation.
048: *
049: * @author Rod Johnson
050: * @author Juergen Hoeller
051: * @author Rob Harrop
052: * @see org.springframework.aop.framework.AopProxyUtils
053: */
054: public abstract class AopUtils {
055:
056: /**
057: * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
058: * @param object the object to check
059: * @see #isJdkDynamicProxy
060: * @see #isCglibProxy
061: */
062: public static boolean isAopProxy(Object object) {
063: return (object instanceof SpringProxy && (Proxy
064: .isProxyClass(object.getClass()) || isCglibProxyClass(object
065: .getClass())));
066: }
067:
068: /**
069: * Check whether the given object is a JDK dynamic proxy.
070: * @param object the object to check
071: * @see java.lang.reflect.Proxy#isProxyClass
072: */
073: public static boolean isJdkDynamicProxy(Object object) {
074: return (object instanceof SpringProxy && Proxy
075: .isProxyClass(object.getClass()));
076: }
077:
078: /**
079: * Check whether the given object is a CGLIB proxy.
080: * @param object the object to check
081: */
082: public static boolean isCglibProxy(Object object) {
083: return (object instanceof SpringProxy && isCglibProxyClass(object
084: .getClass()));
085: }
086:
087: /**
088: * Check whether the specified class is a CGLIB-generated class.
089: * @param clazz the class to check
090: */
091: public static boolean isCglibProxyClass(Class clazz) {
092: return (clazz != null && clazz.getName().indexOf(
093: ClassUtils.CGLIB_CLASS_SEPARATOR) != -1);
094: }
095:
096: /**
097: * Determine the target class of the given bean instance,
098: * which might be an AOP proxy.
099: * <p>Returns the target class for an AOP proxy and the plain class else.
100: * @param candidate the instance to check (might be an AOP proxy)
101: * @return the target class (or the plain class of the given object as fallback)
102: * @see org.springframework.aop.TargetClassAware#getTargetClass()
103: */
104: public static Class getTargetClass(Object candidate) {
105: Assert.notNull(candidate, "Candidate object must not be null");
106: if (candidate instanceof TargetClassAware) {
107: return ((TargetClassAware) candidate).getTargetClass();
108: }
109: if (isCglibProxyClass(candidate.getClass())) {
110: return candidate.getClass().getSuperclass();
111: }
112: return candidate.getClass();
113: }
114:
115: /**
116: * Determine whether the given method is an "equals" method.
117: * @see java.lang.Object#equals
118: */
119: public static boolean isEqualsMethod(Method method) {
120: return (method != null && method.getName().equals("equals")
121: && method.getParameterTypes().length == 1 && method
122: .getParameterTypes()[0] == Object.class);
123: }
124:
125: /**
126: * Determine whether the given method is a "hashCode" method.
127: * @see java.lang.Object#hashCode
128: */
129: public static boolean isHashCodeMethod(Method method) {
130: return (method != null && method.getName().equals("hashCode") && method
131: .getParameterTypes().length == 0);
132: }
133:
134: /**
135: * Determine whether the given method is a "toString" method.
136: * @see java.lang.Object#toString()
137: */
138: public static boolean isToStringMethod(Method method) {
139: return (method != null && method.getName().equals("toString") && method
140: .getParameterTypes().length == 0);
141: }
142:
143: /**
144: * Given a method, which may come from an interface, and a target class used
145: * in the current AOP invocation, find the corresponding target method if there
146: * is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
147: * may be <code>DefaultFoo</code>. In this case, the method may be
148: * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
149: * @param method the method to be invoked, which may come from an interface
150: * @param targetClass the target class for the current invocation.
151: * May be <code>null</code> or may not even implement the method.
152: * @return the specific target method, or the original method if the
153: * <code>targetClass</code> doesn't implement it or is <code>null</code>
154: * @see org.springframework.util.ClassUtils#getMostSpecificMethod
155: */
156: public static Method getMostSpecificMethod(Method method,
157: Class targetClass) {
158: return ClassUtils.getMostSpecificMethod(method, targetClass);
159: }
160:
161: /**
162: * Can the given pointcut apply at all on the given class?
163: * <p>This is an important test as it can be used to optimize
164: * out a pointcut for a class.
165: * @param pc the static or dynamic pointcut to check
166: * @param targetClass the class to test
167: * @return whether the pointcut can apply on any method
168: */
169: public static boolean canApply(Pointcut pc, Class targetClass) {
170: return canApply(pc, targetClass, false);
171: }
172:
173: /**
174: * Can the given pointcut apply at all on the given class?
175: * <p>This is an important test as it can be used to optimize
176: * out a pointcut for a class.
177: * @param pc the static or dynamic pointcut to check
178: * @param targetClass the class to test
179: * @param hasIntroductions whether or not the advisor chain
180: * for this bean includes any introductions
181: * @return whether the pointcut can apply on any method
182: */
183: public static boolean canApply(Pointcut pc, Class targetClass,
184: boolean hasIntroductions) {
185: if (!pc.getClassFilter().matches(targetClass)) {
186: return false;
187: }
188:
189: MethodMatcher methodMatcher = pc.getMethodMatcher();
190: IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
191: if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
192: introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
193: }
194:
195: Set classes = new HashSet(ClassUtils
196: .getAllInterfacesForClassAsSet(targetClass));
197: classes.add(targetClass);
198: for (Iterator it = classes.iterator(); it.hasNext();) {
199: Class clazz = (Class) it.next();
200: Method[] methods = clazz.getMethods();
201: for (int j = 0; j < methods.length; j++) {
202: if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher
203: .matches(methods[j], targetClass,
204: hasIntroductions))
205: || methodMatcher.matches(methods[j],
206: targetClass)) {
207: return true;
208: }
209: }
210: }
211:
212: return false;
213: }
214:
215: /**
216: * Can the given advisor apply at all on the given class?
217: * This is an important test as it can be used to optimize
218: * out a advisor for a class.
219: * @param advisor the advisor to check
220: * @param targetClass class we're testing
221: * @return whether the pointcut can apply on any method
222: */
223: public static boolean canApply(Advisor advisor, Class targetClass) {
224: return canApply(advisor, targetClass, false);
225: }
226:
227: /**
228: * Can the given advisor apply at all on the given class?
229: * This is an important test as it can be used to optimize
230: * out a advisor for a class.
231: * This version also takes into account introductions, for
232: * IntroductionAwareMethodMatchers
233: * @param advisor the advisor to check
234: * @param targetClass class we're testing
235: * @param hasIntroductions whether or not the advisor chain for this bean includes
236: * any introductions
237: * @return whether the pointcut can apply on any method
238: */
239: public static boolean canApply(Advisor advisor, Class targetClass,
240: boolean hasIntroductions) {
241: if (advisor instanceof IntroductionAdvisor) {
242: return ((IntroductionAdvisor) advisor).getClassFilter()
243: .matches(targetClass);
244: } else if (advisor instanceof PointcutAdvisor) {
245: PointcutAdvisor pca = (PointcutAdvisor) advisor;
246: return canApply(pca.getPointcut(), targetClass,
247: hasIntroductions);
248: } else {
249: // It doesn't have a pointcut so we assume it applies
250: return true;
251: }
252: }
253:
254: /**
255: * Determine the sublist of the <code>candidateAdvisors</code> list
256: * that is applicable to the given class.
257: * @param candidateAdvisors the Advisors to evaluate
258: * @param clazz the target class
259: * @return sublist of Advisors that can apply to an object of the given class
260: * (may be the incoming List as-is)
261: */
262: public static List findAdvisorsThatCanApply(List candidateAdvisors,
263: Class clazz) {
264: if (candidateAdvisors.isEmpty()) {
265: return candidateAdvisors;
266: }
267: List eligibleAdvisors = new LinkedList();
268: for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
269: Advisor candidate = (Advisor) it.next();
270: if (candidate instanceof IntroductionAdvisor
271: && canApply(candidate, clazz)) {
272: eligibleAdvisors.add(candidate);
273: }
274: }
275: boolean hasIntroductions = !eligibleAdvisors.isEmpty();
276: for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
277: Advisor candidate = (Advisor) it.next();
278: if (candidate instanceof IntroductionAdvisor) {
279: // already processed
280: continue;
281: }
282: if (canApply(candidate, clazz, hasIntroductions)) {
283: eligibleAdvisors.add(candidate);
284: }
285: }
286: return eligibleAdvisors;
287: }
288:
289: /**
290: * Invoke the given target via reflection, as part of an AOP method invocation.
291: * @param target the target object
292: * @param method the method to invoke
293: * @param args the arguments for the method
294: * @return the invocation result, if any
295: * @throws Throwable if thrown by the target method
296: * @throws org.springframework.aop.AopInvocationException in case of a reflection error
297: */
298: public static Object invokeJoinpointUsingReflection(Object target,
299: Method method, Object[] args) throws Throwable {
300:
301: // Use reflection to invoke the method.
302: try {
303: if (!Modifier.isPublic(method.getModifiers())
304: || !Modifier.isPublic(method.getDeclaringClass()
305: .getModifiers())) {
306: method.setAccessible(true);
307: }
308: return method.invoke(target, args);
309: } catch (InvocationTargetException ex) {
310: // Invoked method threw a checked exception.
311: // We must rethrow it. The client won't see the interceptor.
312: throw ex.getTargetException();
313: } catch (IllegalArgumentException ex) {
314: throw new AopInvocationException(
315: "AOP configuration seems to be invalid: tried calling method ["
316: + method + "] on target [" + target + "]",
317: ex);
318: } catch (IllegalAccessException ex) {
319: throw new AopInvocationException(
320: "Could not access method [" + method + "]", ex);
321: }
322: }
323:
324: }
|