001/*
002 * Copyright 2002-2020 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 *      https://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
017package org.springframework.aop.aspectj;
018
019import java.lang.reflect.Method;
020import java.lang.reflect.Modifier;
021
022import org.aspectj.lang.JoinPoint;
023import org.aspectj.lang.ProceedingJoinPoint;
024import org.aspectj.lang.Signature;
025import org.aspectj.lang.reflect.MethodSignature;
026import org.aspectj.lang.reflect.SourceLocation;
027import org.aspectj.runtime.internal.AroundClosure;
028
029import org.springframework.aop.ProxyMethodInvocation;
030import org.springframework.core.DefaultParameterNameDiscoverer;
031import org.springframework.core.ParameterNameDiscoverer;
032import org.springframework.lang.Nullable;
033import org.springframework.util.Assert;
034
035/**
036 * An implementation of the AspectJ {@link ProceedingJoinPoint} interface
037 * wrapping an AOP Alliance {@link org.aopalliance.intercept.MethodInvocation}.
038 *
039 * <p><b>Note</b>: The {@code getThis()} method returns the current Spring AOP proxy.
040 * The {@code getTarget()} method returns the current Spring AOP target (which may be
041 * {@code null} if there is no target instance) as a plain POJO without any advice.
042 * <b>If you want to call the object and have the advice take effect, use {@code getThis()}.</b>
043 * A common example is casting the object to an introduced interface in the implementation of
044 * an introduction. There is no such distinction between target and proxy in AspectJ itself.
045 *
046 * @author Rod Johnson
047 * @author Juergen Hoeller
048 * @author Adrian Colyer
049 * @author Ramnivas Laddad
050 * @since 2.0
051 */
052public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
053
054        private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
055
056        private final ProxyMethodInvocation methodInvocation;
057
058        @Nullable
059        private Object[] args;
060
061        /** Lazily initialized signature object. */
062        @Nullable
063        private Signature signature;
064
065        /** Lazily initialized source location object. */
066        @Nullable
067        private SourceLocation sourceLocation;
068
069
070        /**
071         * Create a new MethodInvocationProceedingJoinPoint, wrapping the given
072         * Spring ProxyMethodInvocation object.
073         * @param methodInvocation the Spring ProxyMethodInvocation object
074         */
075        public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
076                Assert.notNull(methodInvocation, "MethodInvocation must not be null");
077                this.methodInvocation = methodInvocation;
078        }
079
080
081        @Override
082        public void set$AroundClosure(AroundClosure aroundClosure) {
083                throw new UnsupportedOperationException();
084        }
085
086        @Override
087        public Object proceed() throws Throwable {
088                return this.methodInvocation.invocableClone().proceed();
089        }
090
091        @Override
092        public Object proceed(Object[] arguments) throws Throwable {
093                Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
094                if (arguments.length != this.methodInvocation.getArguments().length) {
095                        throw new IllegalArgumentException("Expecting " +
096                                        this.methodInvocation.getArguments().length + " arguments to proceed, " +
097                                        "but was passed " + arguments.length + " arguments");
098                }
099                this.methodInvocation.setArguments(arguments);
100                return this.methodInvocation.invocableClone(arguments).proceed();
101        }
102
103        /**
104         * Returns the Spring AOP proxy. Cannot be {@code null}.
105         */
106        @Override
107        public Object getThis() {
108                return this.methodInvocation.getProxy();
109        }
110
111        /**
112         * Returns the Spring AOP target. May be {@code null} if there is no target.
113         */
114        @Override
115        @Nullable
116        public Object getTarget() {
117                return this.methodInvocation.getThis();
118        }
119
120        @Override
121        public Object[] getArgs() {
122                if (this.args == null) {
123                        this.args = this.methodInvocation.getArguments().clone();
124                }
125                return this.args;
126        }
127
128        @Override
129        public Signature getSignature() {
130                if (this.signature == null) {
131                        this.signature = new MethodSignatureImpl();
132                }
133                return this.signature;
134        }
135
136        @Override
137        public SourceLocation getSourceLocation() {
138                if (this.sourceLocation == null) {
139                        this.sourceLocation = new SourceLocationImpl();
140                }
141                return this.sourceLocation;
142        }
143
144        @Override
145        public String getKind() {
146                return ProceedingJoinPoint.METHOD_EXECUTION;
147        }
148
149        @Override
150        public int getId() {
151                // TODO: It's just an adapter but returning 0 might still have side effects...
152                return 0;
153        }
154
155        @Override
156        public JoinPoint.StaticPart getStaticPart() {
157                return this;
158        }
159
160        @Override
161        public String toShortString() {
162                return "execution(" + getSignature().toShortString() + ")";
163        }
164
165        @Override
166        public String toLongString() {
167                return "execution(" + getSignature().toLongString() + ")";
168        }
169
170        @Override
171        public String toString() {
172                return "execution(" + getSignature().toString() + ")";
173        }
174
175
176        /**
177         * Lazily initialized MethodSignature.
178         */
179        private class MethodSignatureImpl implements MethodSignature {
180
181                @Nullable
182                private volatile String[] parameterNames;
183
184                @Override
185                public String getName() {
186                        return methodInvocation.getMethod().getName();
187                }
188
189                @Override
190                public int getModifiers() {
191                        return methodInvocation.getMethod().getModifiers();
192                }
193
194                @Override
195                public Class<?> getDeclaringType() {
196                        return methodInvocation.getMethod().getDeclaringClass();
197                }
198
199                @Override
200                public String getDeclaringTypeName() {
201                        return methodInvocation.getMethod().getDeclaringClass().getName();
202                }
203
204                @Override
205                public Class<?> getReturnType() {
206                        return methodInvocation.getMethod().getReturnType();
207                }
208
209                @Override
210                public Method getMethod() {
211                        return methodInvocation.getMethod();
212                }
213
214                @Override
215                public Class<?>[] getParameterTypes() {
216                        return methodInvocation.getMethod().getParameterTypes();
217                }
218
219                @Override
220                @Nullable
221                public String[] getParameterNames() {
222                        String[] parameterNames = this.parameterNames;
223                        if (parameterNames == null) {
224                                parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
225                                this.parameterNames = parameterNames;
226                        }
227                        return parameterNames;
228                }
229
230                @Override
231                public Class<?>[] getExceptionTypes() {
232                        return methodInvocation.getMethod().getExceptionTypes();
233                }
234
235                @Override
236                public String toShortString() {
237                        return toString(false, false, false, false);
238                }
239
240                @Override
241                public String toLongString() {
242                        return toString(true, true, true, true);
243                }
244
245                @Override
246                public String toString() {
247                        return toString(false, true, false, true);
248                }
249
250                private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
251                                boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
252
253                        StringBuilder sb = new StringBuilder();
254                        if (includeModifier) {
255                                sb.append(Modifier.toString(getModifiers()));
256                                sb.append(" ");
257                        }
258                        if (includeReturnTypeAndArgs) {
259                                appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
260                                sb.append(" ");
261                        }
262                        appendType(sb, getDeclaringType(), useLongTypeName);
263                        sb.append(".");
264                        sb.append(getMethod().getName());
265                        sb.append("(");
266                        Class<?>[] parametersTypes = getParameterTypes();
267                        appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
268                        sb.append(")");
269                        return sb.toString();
270                }
271
272                private void appendTypes(StringBuilder sb, Class<?>[] types, boolean includeArgs,
273                                boolean useLongReturnAndArgumentTypeName) {
274
275                        if (includeArgs) {
276                                for (int size = types.length, i = 0; i < size; i++) {
277                                        appendType(sb, types[i], useLongReturnAndArgumentTypeName);
278                                        if (i < size - 1) {
279                                                sb.append(",");
280                                        }
281                                }
282                        }
283                        else {
284                                if (types.length != 0) {
285                                        sb.append("..");
286                                }
287                        }
288                }
289
290                private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {
291                        if (type.isArray()) {
292                                appendType(sb, type.getComponentType(), useLongTypeName);
293                                sb.append("[]");
294                        }
295                        else {
296                                sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
297                        }
298                }
299        }
300
301
302        /**
303         * Lazily initialized SourceLocation.
304         */
305        private class SourceLocationImpl implements SourceLocation {
306
307                @Override
308                public Class<?> getWithinType() {
309                        if (methodInvocation.getThis() == null) {
310                                throw new UnsupportedOperationException("No source location joinpoint available: target is null");
311                        }
312                        return methodInvocation.getThis().getClass();
313                }
314
315                @Override
316                public String getFileName() {
317                        throw new UnsupportedOperationException();
318                }
319
320                @Override
321                public int getLine() {
322                        throw new UnsupportedOperationException();
323                }
324
325                @Override
326                @Deprecated
327                public int getColumn() {
328                        throw new UnsupportedOperationException();
329                }
330        }
331
332}