001/*
002 * Copyright 2002-2018 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.lang.reflect.Method;
020import java.util.concurrent.Callable;
021import java.util.concurrent.ExecutionException;
022import java.util.concurrent.Executor;
023import java.util.concurrent.Future;
024
025import org.aopalliance.intercept.MethodInterceptor;
026import org.aopalliance.intercept.MethodInvocation;
027
028import org.springframework.aop.support.AopUtils;
029import org.springframework.beans.factory.BeanFactory;
030import org.springframework.core.BridgeMethodResolver;
031import org.springframework.core.Ordered;
032import org.springframework.core.task.AsyncTaskExecutor;
033import org.springframework.core.task.SimpleAsyncTaskExecutor;
034import org.springframework.lang.Nullable;
035import org.springframework.util.ClassUtils;
036
037/**
038 * AOP Alliance {@code MethodInterceptor} that processes method invocations
039 * asynchronously, using a given {@link org.springframework.core.task.AsyncTaskExecutor}.
040 * Typically used with the {@link org.springframework.scheduling.annotation.Async} annotation.
041 *
042 * <p>In terms of target method signatures, any parameter types are supported.
043 * However, the return type is constrained to either {@code void} or
044 * {@code java.util.concurrent.Future}. In the latter case, the Future handle
045 * returned from the proxy will be an actual asynchronous Future that can be used
046 * to track the result of the asynchronous method execution. However, since the
047 * target method needs to implement the same signature, it will have to return
048 * a temporary Future handle that just passes the return value through
049 * (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
050 * or EJB 3.1's {@code javax.ejb.AsyncResult}).
051 *
052 * <p>When the return type is {@code java.util.concurrent.Future}, any exception thrown
053 * during the execution can be accessed and managed by the caller. With {@code void}
054 * return type however, such exceptions cannot be transmitted back. In that case an
055 * {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions.
056 *
057 * <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
058 * preferred for use due to its support for executor qualification in conjunction with
059 * Spring's {@code @Async} annotation.
060 *
061 * @author Juergen Hoeller
062 * @author Chris Beams
063 * @author Stephane Nicoll
064 * @since 3.0
065 * @see org.springframework.scheduling.annotation.Async
066 * @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
067 * @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
068 */
069public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
070
071        /**
072         * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
073         * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
074         * or {@link java.util.concurrent.ExecutorService}) to delegate to;
075         * as of 4.2.6, a local executor for this interceptor will be built otherwise
076         */
077        public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
078                super(defaultExecutor);
079        }
080
081        /**
082         * Create a new {@code AsyncExecutionInterceptor}.
083         * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
084         * or {@link java.util.concurrent.ExecutorService}) to delegate to;
085         * as of 4.2.6, a local executor for this interceptor will be built otherwise
086         * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
087         */
088        public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
089                super(defaultExecutor, exceptionHandler);
090        }
091
092
093        /**
094         * Intercept the given method invocation, submit the actual calling of the method to
095         * the correct task executor and return immediately to the caller.
096         * @param invocation the method to intercept and make asynchronous
097         * @return {@link Future} if the original method returns {@code Future}; {@code null}
098         * otherwise.
099         */
100        @Override
101        @Nullable
102        public Object invoke(final MethodInvocation invocation) throws Throwable {
103                Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
104                Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
105                final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
106
107                AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
108                if (executor == null) {
109                        throw new IllegalStateException(
110                                        "No executor specified and no default executor set on AsyncExecutionInterceptor either");
111                }
112
113                Callable<Object> task = () -> {
114                        try {
115                                Object result = invocation.proceed();
116                                if (result instanceof Future) {
117                                        return ((Future<?>) result).get();
118                                }
119                        }
120                        catch (ExecutionException ex) {
121                                handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
122                        }
123                        catch (Throwable ex) {
124                                handleError(ex, userDeclaredMethod, invocation.getArguments());
125                        }
126                        return null;
127                };
128
129                return doSubmit(task, executor, invocation.getMethod().getReturnType());
130        }
131
132        /**
133         * This implementation is a no-op for compatibility in Spring 3.1.2.
134         * Subclasses may override to provide support for extracting qualifier information,
135         * e.g. via an annotation on the given method.
136         * @return always {@code null}
137         * @since 3.1.2
138         * @see #determineAsyncExecutor(Method)
139         */
140        @Override
141        @Nullable
142        protected String getExecutorQualifier(Method method) {
143                return null;
144        }
145
146        /**
147         * This implementation searches for a unique {@link org.springframework.core.task.TaskExecutor}
148         * bean in the context, or for an {@link Executor} bean named "taskExecutor" otherwise.
149         * If neither of the two is resolvable (e.g. if no {@code BeanFactory} was configured at all),
150         * this implementation falls back to a newly created {@link SimpleAsyncTaskExecutor} instance
151         * for local use if no default could be found.
152         * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
153         */
154        @Override
155        @Nullable
156        protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
157                Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
158                return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
159        }
160
161        @Override
162        public int getOrder() {
163                return Ordered.HIGHEST_PRECEDENCE;
164        }
165
166}