001/*
002 * Copyright 2002-2014 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.interceptor;
018
019import java.io.Serializable;
020
021import org.aopalliance.intercept.MethodInterceptor;
022import org.aopalliance.intercept.MethodInvocation;
023
024import org.springframework.aop.Advisor;
025import org.springframework.aop.support.DefaultPointcutAdvisor;
026import org.springframework.core.NamedThreadLocal;
027import org.springframework.core.PriorityOrdered;
028
029/**
030 * Interceptor that exposes the current {@link org.aopalliance.intercept.MethodInvocation}
031 * as a thread-local object. We occasionally need to do this; for example, when a pointcut
032 * (e.g. an AspectJ expression pointcut) needs to know the full invocation context.
033 *
034 * <p>Don't use this interceptor unless this is really necessary. Target objects should
035 * not normally know about Spring AOP, as this creates a dependency on Spring API.
036 * Target objects should be plain POJOs as far as possible.
037 *
038 * <p>If used, this interceptor will normally be the first in the interceptor chain.
039 *
040 * @author Rod Johnson
041 * @author Juergen Hoeller
042 */
043@SuppressWarnings("serial")
044public class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {
045
046        /** Singleton instance of this class */
047        public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();
048
049        /**
050         * Singleton advisor for this class. Use in preference to INSTANCE when using
051         * Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
052         */
053        public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
054                @Override
055                public String toString() {
056                        return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
057                }
058        };
059
060        private static final ThreadLocal<MethodInvocation> invocation =
061                        new NamedThreadLocal<MethodInvocation>("Current AOP method invocation");
062
063
064        /**
065         * Return the AOP Alliance MethodInvocation object associated with the current invocation.
066         * @return the invocation object associated with the current invocation
067         * @throws IllegalStateException if there is no AOP invocation in progress,
068         * or if the ExposeInvocationInterceptor was not added to this interceptor chain
069         */
070        public static MethodInvocation currentInvocation() throws IllegalStateException {
071                MethodInvocation mi = invocation.get();
072                if (mi == null)
073                        throw new IllegalStateException(
074                                        "No MethodInvocation found: Check that an AOP invocation is in progress, and that the " +
075                                        "ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
076                                        "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!");
077                return mi;
078        }
079
080
081        /**
082         * Ensures that only the canonical instance can be created.
083         */
084        private ExposeInvocationInterceptor() {
085        }
086
087        @Override
088        public Object invoke(MethodInvocation mi) throws Throwable {
089                MethodInvocation oldInvocation = invocation.get();
090                invocation.set(mi);
091                try {
092                        return mi.proceed();
093                }
094                finally {
095                        invocation.set(oldInvocation);
096                }
097        }
098
099        @Override
100        public int getOrder() {
101                return PriorityOrdered.HIGHEST_PRECEDENCE + 1;
102        }
103
104        /**
105         * Required to support serialization. Replaces with canonical instance
106         * on deserialization, protecting Singleton pattern.
107         * <p>Alternative to overriding the {@code equals} method.
108         */
109        private Object readResolve() {
110                return INSTANCE;
111        }
112
113}