org.springframework.aop.interceptor.AsyncExecutionInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.aop.interceptor.AsyncExecutionInterceptor.java

Source

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.interceptor;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.support.AopUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.Ordered;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;

/**
 * AOP Alliance {@code MethodInterceptor} that processes method invocations
 * asynchronously, using a given {@link org.springframework.core.task.AsyncTaskExecutor}.
 * Typically used with the {@link org.springframework.scheduling.annotation.Async} annotation.
 *
 * <p>In terms of target method signatures, any parameter types are supported.
 * However, the return type is constrained to either {@code void} or
 * {@code java.util.concurrent.Future}. In the latter case, the Future handle
 * returned from the proxy will be an actual asynchronous Future that can be used
 * to track the result of the asynchronous method execution. However, since the
 * target method needs to implement the same signature, it will have to return
 * a temporary Future handle that just passes the return value through
 * (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
 * or EJB 3.1's {@code javax.ejb.AsyncResult}).
 *
 * <p>When the return type is {@code java.util.concurrent.Future}, any exception thrown
 * during the execution can be accessed and managed by the caller. With {@code void}
 * return type however, such exceptions cannot be transmitted back. In that case an
 * {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions.
 *
 * <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
 * preferred for use due to its support for executor qualification in conjunction with
 * Spring's {@code @Async} annotation.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @author Stephane Nicoll
 * @since 3.0
 * @see org.springframework.scheduling.annotation.Async
 * @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
 * @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
 */
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {

    private final Log logger = LogFactory.getLog(getClass());

    private AsyncUncaughtExceptionHandler exceptionHandler;

    /**
     * Create a new {@code AsyncExecutionInterceptor}.
     * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
     * or {@link java.util.concurrent.ExecutorService}) to delegate to
     * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
     */
    public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
        super(defaultExecutor);
        this.exceptionHandler = exceptionHandler;
    }

    /**
     * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
     */
    public AsyncExecutionInterceptor(Executor defaultExecutor) {
        this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
    }

    /**
     * Supply the {@link AsyncUncaughtExceptionHandler} to use to handle exceptions
     * thrown by invoking asynchronous methods with a {@code void} return type.
     */
    public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }

    /**
     * Intercept the given method invocation, submit the actual calling of the method to
     * the correct task executor and return immediately to the caller.
     * @param invocation the method to intercept and make asynchronous
     * @return {@link Future} if the original method returns {@code Future}; {@code null}
     * otherwise.
     */
    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis())
                : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                    "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }

        Callable<Object> task = new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                try {
                    Object result = invocation.proceed();
                    if (result instanceof Future) {
                        return ((Future<?>) result).get();
                    }
                } catch (Throwable ex) {
                    handleError(ex, userDeclaredMethod, invocation.getArguments());
                }
                return null;
            }
        };

        Class<?> returnType = invocation.getMethod().getReturnType();
        if (ListenableFuture.class.isAssignableFrom(returnType)) {
            return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
        } else if (Future.class.isAssignableFrom(returnType)) {
            return executor.submit(task);
        } else {
            executor.submit(task);
            return null;
        }
    }

    /**
     * Handles a fatal error thrown while asynchronously invoking the specified
     * {@link Method}.
     * <p>If the return type of the method is a {@link Future} object, the original
     * exception can be propagated by just throwing it at the higher level. However,
     * for all other cases, the exception will not be transmitted back to the client.
     * In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
     * used to manage such exception.
     * @param ex the exception to handle
     * @param method the method that was invoked
     * @param params the parameters used to invoke the method
     */
    protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
        if (method.getReturnType().isAssignableFrom(Future.class)) {
            ReflectionUtils.rethrowException(ex);
        } else {
            // Could not transmit the exception to the caller with default executor
            try {
                this.exceptionHandler.handleUncaughtException(ex, method, params);
            } catch (Throwable ex2) {
                logger.error("Exception handler for async method '" + method.toGenericString()
                        + "' threw unexpected exception itself", ex2);
            }
        }
    }

    /**
     * This implementation is a no-op for compatibility in Spring 3.1.2.
     * Subclasses may override to provide support for extracting qualifier information,
     * e.g. via an annotation on the given method.
     * @return always {@code null}
     * @since 3.1.2
     * @see #determineAsyncExecutor(Method)
     */
    @Override
    protected String getExecutorQualifier(Method method) {
        return null;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

}