1 /*
2  * Copyright 2002-2009 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.framework;
18 
19 import java.lang.reflect.AccessibleObject;
20 import java.lang.reflect.Method;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 
25 import org.aopalliance.intercept.MethodInterceptor;
26 import org.aopalliance.intercept.MethodInvocation;
27 
28 import org.springframework.aop.ProxyMethodInvocation;
29 import org.springframework.aop.support.AopUtils;
30 import org.springframework.core.BridgeMethodResolver;
31 
32 /**
33  * Spring's implementation of the AOP Alliance
34  * {@link org.aopalliance.intercept.MethodInvocation} interface,
35  * implementing the extended
36  * {@link org.springframework.aop.ProxyMethodInvocation} interface.
37  *
38  * <p>Invokes the target object using reflection. Subclasses can override the
39  * {@link #invokeJoinpoint()} method to change this behavior, so this is also
40  * a useful base class for more specialized MethodInvocation implementations.
41  *
42  * <p>It is possible to clone an invocation, to invoke {@link #proceed()}
43  * repeatedly (once per clone), using the {@link #invocableClone()} method.
44  * It is also possible to attach custom attributes to the invocation,
45  * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods.
46  *
47  * <p><b>NOTE:</b> This class is considered internal and should not be
48  * directly accessed. The sole reason for it being public is compatibility
49  * with existing framework integrations (e.g. Pitchfork). For any other
50  * purposes, use the {@link ProxyMethodInvocation} interface instead.
51  *
52  * @author Rod Johnson
53  * @author Juergen Hoeller
54  * @author Adrian Colyer
55  * @see #invokeJoinpoint
56  * @see #proceed
57  * @see #invocableClone
58  * @see #setUserAttribute
59  * @see #getUserAttribute
60  */
61 public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
62 
63 	protected final Object proxy;
64 
65 	protected final Object target;
66 
67 	protected final Method method;
68 
69 	protected Object[] arguments;
70 
71 	private final Class targetClass;
72 
73 	/**
74 	 * Lazily initialized map of user-specific attributes for this invocation.
75 	 */
76 	private Map<String, Object> userAttributes;
77 
78 	/**
79 	 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
80 	 * that need dynamic checks.
81 	 */
82 	protected final List interceptorsAndDynamicMethodMatchers;
83 
84 	/**
85 	 * Index from 0 of the current interceptor we're invoking.
86 	 * -1 until we invoke: then the current interceptor.
87 	 */
88 	private int currentInterceptorIndex = -1;
89 
90 
91 	/**
92 	 * Construct a new ReflectiveMethodInvocation with the given arguments.
93 	 * @param proxy the proxy object that the invocation was made on
94 	 * @param target the target object to invoke
95 	 * @param method the method to invoke
96 	 * @param arguments the arguments to invoke the method with
97 	 * @param targetClass the target class, for MethodMatcher invocations
98 	 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
99 	 * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
100 	 * MethodMatchers included in this struct must already have been found to have matched
101 	 * as far as was possibly statically. Passing an array might be about 10% faster,
102 	 * but would complicate the code. And it would work only for static pointcuts.
103 	 */
ReflectiveMethodInvocation( Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers)104 	protected ReflectiveMethodInvocation(
105 			Object proxy, Object target, Method method, Object[] arguments,
106 			Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
107 
108 		this.proxy = proxy;
109 		this.target = target;
110 		this.targetClass = targetClass;
111 		this.method = BridgeMethodResolver.findBridgedMethod(method);
112 		this.arguments = arguments;
113 		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
114 	}
115 
116 
getProxy()117 	public final Object getProxy() {
118 		return this.proxy;
119 	}
120 
getThis()121 	public final Object getThis() {
122 		return this.target;
123 	}
124 
getStaticPart()125 	public final AccessibleObject getStaticPart() {
126 		return this.method;
127 	}
128 
129 	/**
130 	 * Return the method invoked on the proxied interface.
131 	 * May or may not correspond with a method invoked on an underlying
132 	 * implementation of that interface.
133 	 */
getMethod()134 	public final Method getMethod() {
135 		return this.method;
136 	}
137 
getArguments()138 	public final Object[] getArguments() {
139 		return (this.arguments != null ? this.arguments : new Object[0]);
140 	}
141 
setArguments(Object[] arguments)142 	public void setArguments(Object[] arguments) {
143 		this.arguments = arguments;
144 	}
145 
146 
proceed()147 	public Object proceed() throws Throwable {
148 		//	We start with an index of -1 and increment early.
149 		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
150 			return invokeJoinpoint();
151 		}
152 
153 		Object interceptorOrInterceptionAdvice =
154 		    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
155 		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
156 			// Evaluate dynamic method matcher here: static part will already have
157 			// been evaluated and found to match.
158 			InterceptorAndDynamicMethodMatcher dm =
159 			    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
160 			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
161 				return dm.interceptor.invoke(this);
162 			}
163 			else {
164 				// Dynamic matching failed.
165 				// Skip this interceptor and invoke the next in the chain.
166 				return proceed();
167 			}
168 		}
169 		else {
170 			// It's an interceptor, so we just invoke it: The pointcut will have
171 			// been evaluated statically before this object was constructed.
172 			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
173 		}
174 	}
175 
176 	/**
177 	 * Invoke the joinpoint using reflection.
178 	 * Subclasses can override this to use custom invocation.
179 	 * @return the return value of the joinpoint
180 	 * @throws Throwable if invoking the joinpoint resulted in an exception
181 	 */
invokeJoinpoint()182 	protected Object invokeJoinpoint() throws Throwable {
183 		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
184 	}
185 
186 
187 	/**
188 	 * This implementation returns a shallow copy of this invocation object,
189 	 * including an independent copy of the original arguments array.
190 	 * <p>We want a shallow copy in this case: We want to use the same interceptor
191 	 * chain and other object references, but we want an independent value for the
192 	 * current interceptor index.
193 	 * @see java.lang.Object#clone()
194 	 */
invocableClone()195 	public MethodInvocation invocableClone() {
196 		Object[] cloneArguments = null;
197 		if (this.arguments != null) {
198 			// Build an independent copy of the arguments array.
199 			cloneArguments = new Object[this.arguments.length];
200 			System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
201 		}
202 		return invocableClone(cloneArguments);
203 	}
204 
205 	/**
206 	 * This implementation returns a shallow copy of this invocation object,
207 	 * using the given arguments array for the clone.
208 	 * <p>We want a shallow copy in this case: We want to use the same interceptor
209 	 * chain and other object references, but we want an independent value for the
210 	 * current interceptor index.
211 	 * @see java.lang.Object#clone()
212 	 */
invocableClone(Object[] arguments)213 	public MethodInvocation invocableClone(Object[] arguments) {
214 		// Force initialization of the user attributes Map,
215 		// for having a shared Map reference in the clone.
216 		if (this.userAttributes == null) {
217 			this.userAttributes = new HashMap<String, Object>();
218 		}
219 
220 		// Create the MethodInvocation clone.
221 		try {
222 			ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
223 			clone.arguments = arguments;
224 			return clone;
225 		}
226 		catch (CloneNotSupportedException ex) {
227 			throw new IllegalStateException(
228 					"Should be able to clone object of type [" + getClass() + "]: " + ex);
229 		}
230 	}
231 
232 
setUserAttribute(String key, Object value)233 	public void setUserAttribute(String key, Object value) {
234 		if (value != null) {
235 			if (this.userAttributes == null) {
236 				this.userAttributes = new HashMap<String, Object>();
237 			}
238 			this.userAttributes.put(key, value);
239 		}
240 		else {
241 			if (this.userAttributes != null) {
242 				this.userAttributes.remove(key);
243 			}
244 		}
245 	}
246 
getUserAttribute(String key)247 	public Object getUserAttribute(String key) {
248 		return (this.userAttributes != null ? this.userAttributes.get(key) : null);
249 	}
250 
251 	/**
252 	 * Return user attributes associated with this invocation.
253 	 * This method provides an invocation-bound alternative to a ThreadLocal.
254 	 * <p>This map is initialized lazily and is not used in the AOP framework itself.
255 	 * @return any user attributes associated with this invocation
256 	 * (never <code>null</code>)
257 	 */
getUserAttributes()258 	public Map<String, Object> getUserAttributes() {
259 		if (this.userAttributes == null) {
260 			this.userAttributes = new HashMap<String, Object>();
261 		}
262 		return this.userAttributes;
263 	}
264 
265 
266 	@Override
toString()267 	public String toString() {
268 		// Don't do toString on target, it may be proxied.
269 		StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
270 		sb.append(this.method).append("; ");
271 		if (this.target == null) {
272 			sb.append("target is null");
273 		}
274 		else {
275 			sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
276 		}
277 		return sb.toString();
278 	}
279 
280 }
281