View Javadoc
1   /*
2    * Copyright 2002-2012 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;
20  import java.lang.reflect.Modifier;
21  
22  import org.aspectj.lang.JoinPoint;
23  import org.aspectj.lang.ProceedingJoinPoint;
24  import org.aspectj.lang.Signature;
25  import org.aspectj.lang.reflect.MethodSignature;
26  import org.aspectj.lang.reflect.SourceLocation;
27  import org.aspectj.runtime.internal.AroundClosure;
28  
29  import org.springframework.aop.ProxyMethodInvocation;
30  import org.springframework.core.DefaultParameterNameDiscoverer;
31  import org.springframework.core.ParameterNameDiscoverer;
32  import org.springframework.util.Assert;
33  
34  /**
35   * Implementation of AspectJ ProceedingJoinPoint interface
36   * wrapping an AOP Alliance MethodInvocation.
37   *
38   * <p><b>Note</b>: the {@code getThis()} method returns the current Spring AOP proxy.
39   * The {@code getTarget()} method returns the current Spring AOP target (which may be
40   * {@code null} if there is no target), and is a plain POJO without any advice.
41   * <b>If you want to call the object and have the advice take effect, use
42   * {@code getThis()}.</b> A common example is casting the object to an
43   * introduced interface in the implementation of an introduction.
44   *
45   * <p>Of course there is no such distinction between target and proxy in AspectJ.
46   *
47   * @author Rod Johnson
48   * @author Juergen Hoeller
49   * @author Adrian Colyer
50   * @author Ramnivas Laddad
51   * @since 2.0
52   */
53  public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
54  
55  	private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
56  
57  	private final ProxyMethodInvocation methodInvocation;
58  
59  	private Object[] defensiveCopyOfArgs;
60  
61  	/** Lazily initialized signature object */
62  	private Signature signature;
63  
64  	/** Lazily initialized source location object */
65  	private SourceLocation sourceLocation;
66  
67  
68  	/**
69  	 * Create a new MethodInvocationProceedingJoinPoint, wrapping the given
70  	 * Spring ProxyMethodInvocation object.
71  	 * @param methodInvocation the Spring ProxyMethodInvocation object
72  	 */
73  	public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
74  		Assert.notNull(methodInvocation, "MethodInvocation must not be null");
75  		this.methodInvocation = methodInvocation;
76  	}
77  
78  	@Override
79  	public void set$AroundClosure(AroundClosure aroundClosure) {
80  		throw new UnsupportedOperationException();
81  	}
82  
83  	@Override
84  	public Object proceed() throws Throwable {
85  		return this.methodInvocation.invocableClone().proceed();
86  	}
87  
88  	@Override
89  	public Object proceed(Object[] arguments) throws Throwable {
90  		Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
91  		if (arguments.length != this.methodInvocation.getArguments().length) {
92  			throw new IllegalArgumentException("Expecting " +
93  					this.methodInvocation.getArguments().length + " arguments to proceed, " +
94  					"but was passed " + arguments.length + " arguments");
95  		}
96  		this.methodInvocation.setArguments(arguments);
97  		return this.methodInvocation.invocableClone(arguments).proceed();
98  	}
99  
100 	/**
101 	 * Returns the Spring AOP proxy. Cannot be {@code null}.
102 	 */
103 	@Override
104 	public Object getThis() {
105 		return this.methodInvocation.getProxy();
106 	}
107 
108 	/**
109 	 * Returns the Spring AOP target. May be {@code null} if there is no target.
110 	 */
111 	@Override
112 	public Object getTarget() {
113 		return this.methodInvocation.getThis();
114 	}
115 
116 	@Override
117 	public Object[] getArgs() {
118 		if (this.defensiveCopyOfArgs == null) {
119 			Object[] argsSource = this.methodInvocation.getArguments();
120 			this.defensiveCopyOfArgs = new Object[argsSource.length];
121 			System.arraycopy(argsSource, 0, this.defensiveCopyOfArgs, 0, argsSource.length);
122 		}
123 		return this.defensiveCopyOfArgs;
124 	}
125 
126 	@Override
127 	public Signature getSignature() {
128 		if (this.signature == null) {
129 			this.signature = new MethodSignatureImpl();
130 		}
131 		return signature;
132 	}
133 
134 	@Override
135 	public SourceLocation getSourceLocation() {
136 		if (this.sourceLocation == null) {
137 			this.sourceLocation = new SourceLocationImpl();
138 		}
139 		return this.sourceLocation;
140 	}
141 
142 	@Override
143 	public String getKind() {
144 		return ProceedingJoinPoint.METHOD_EXECUTION;
145 	}
146 
147 	@Override
148 	public int getId() {
149 		// TODO: It's just an adapter but returning 0 might still have side effects...
150 		return 0;
151 	}
152 
153 	@Override
154 	public JoinPoint.StaticPart getStaticPart() {
155 		return this;
156 	}
157 
158 	@Override
159 	public String toShortString() {
160 		return "execution(" + getSignature().toShortString() + ")";
161 	}
162 
163 	@Override
164 	public String toLongString() {
165 		return "execution(" + getSignature().toLongString() + ")";
166 	}
167 
168 	@Override
169 	public String toString() {
170 		return "execution(" + getSignature().toString() + ")";
171 	}
172 
173 
174 	/**
175 	 * Lazily initialized MethodSignature.
176 	 */
177 	private class MethodSignatureImpl implements MethodSignature {
178 
179 		private volatile String[] parameterNames;
180 
181 		@Override
182 		public String getName() {
183 			return methodInvocation.getMethod().getName();
184 		}
185 
186 		@Override
187 		public int getModifiers() {
188 			return methodInvocation.getMethod().getModifiers();
189 		}
190 
191 		@Override
192 		public Class<?> getDeclaringType() {
193 			return methodInvocation.getMethod().getDeclaringClass();
194 		}
195 
196 		@Override
197 		public String getDeclaringTypeName() {
198 			return methodInvocation.getMethod().getDeclaringClass().getName();
199 		}
200 
201 		@Override
202 		public Class<?> getReturnType() {
203 			return methodInvocation.getMethod().getReturnType();
204 		}
205 
206 		@Override
207 		public Method getMethod() {
208 			return methodInvocation.getMethod();
209 		}
210 
211 		@Override
212 		public Class<?>[] getParameterTypes() {
213 			return methodInvocation.getMethod().getParameterTypes();
214 		}
215 
216 		@Override
217 		public String[] getParameterNames() {
218 			if (this.parameterNames == null) {
219 				this.parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
220 			}
221 			return this.parameterNames;
222 		}
223 
224 		@Override
225 		public Class<?>[] getExceptionTypes() {
226 			return methodInvocation.getMethod().getExceptionTypes();
227 		}
228 
229 		@Override
230 		public String toShortString() {
231 			return toString(false, false, false, false);
232 		}
233 
234 		@Override
235 		public String toLongString() {
236 			return toString(true, true, true, true);
237 		}
238 
239 		@Override
240 		public String toString() {
241 			return toString(false, true, false, true);
242 		}
243 
244 		private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
245 				boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
246 			StringBuilder sb = new StringBuilder();
247 			if (includeModifier) {
248 				sb.append(Modifier.toString(getModifiers()));
249 				sb.append(" ");
250 			}
251 			if (includeReturnTypeAndArgs) {
252 				appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
253 				sb.append(" ");
254 			}
255 			appendType(sb, getDeclaringType(), useLongTypeName);
256 			sb.append(".");
257 			sb.append(getMethod().getName());
258 			sb.append("(");
259 			Class<?>[] parametersTypes = getParameterTypes();
260 			appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
261 			sb.append(")");
262 			return sb.toString();
263 		}
264 
265 		private void appendTypes(StringBuilder sb, Class<?>[] types,
266 				boolean includeArgs, boolean useLongReturnAndArgumentTypeName) {
267 			if (includeArgs) {
268 				for (int size = types.length, i = 0; i < size; i++) {
269 					appendType(sb, types[i], useLongReturnAndArgumentTypeName);
270 					if (i < size - 1) {
271 						sb.append(",");
272 					}
273 				}
274 			}
275 			else {
276 				if (types.length != 0) {
277 					sb.append("..");
278 				}
279 			}
280 		}
281 
282 		private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {
283 			if (type.isArray()) {
284 				appendType(sb, type.getComponentType(), useLongTypeName);
285 				sb.append("[]");
286 			}
287 			else {
288 				sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
289 			}
290 		}
291 	}
292 
293 
294 	/**
295 	 * Lazily initialized SourceLocation.
296 	 */
297 	private class SourceLocationImpl implements SourceLocation {
298 
299 		@Override
300 		public Class<?> getWithinType() {
301 			if (methodInvocation.getThis() == null) {
302 				throw new UnsupportedOperationException("No source location joinpoint available: target is null");
303 			}
304 			return methodInvocation.getThis().getClass();
305 		}
306 
307 		@Override
308 		public String getFileName() {
309 			throw new UnsupportedOperationException();
310 		}
311 
312 		@Override
313 		public int getLine() {
314 			throw new UnsupportedOperationException();
315 		}
316 
317 		@Override
318 		@Deprecated
319 		public int getColumn() {
320 			throw new UnsupportedOperationException();
321 		}
322 	}
323 
324 }