001/*
002 * Copyright 2002-2016 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.scheduling.annotation;
018
019import java.lang.annotation.Annotation;
020import java.util.concurrent.Executor;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
026import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
027import org.springframework.beans.factory.BeanFactory;
028import org.springframework.core.task.TaskExecutor;
029import org.springframework.util.Assert;
030
031/**
032 * Bean post-processor that automatically applies asynchronous invocation
033 * behavior to any bean that carries the {@link Async} annotation at class or
034 * method-level by adding a corresponding {@link AsyncAnnotationAdvisor} to the
035 * exposed proxy (either an existing AOP proxy or a newly generated proxy that
036 * implements all of the target's interfaces).
037 *
038 * <p>The {@link TaskExecutor} responsible for the asynchronous execution may
039 * be provided as well as the annotation type that indicates a method should be
040 * invoked asynchronously. If no annotation type is specified, this post-
041 * processor will detect both Spring's {@link Async @Async} annotation as well
042 * as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation.
043 *
044 * <p>For methods having a {@code void} return type, any exception thrown
045 * during the asynchronous method invocation cannot be accessed by the
046 * caller. An {@link AsyncUncaughtExceptionHandler} can be specified to handle
047 * these cases.
048 *
049 * <p>Note: The underlying async advisor applies before existing advisors by default,
050 * in order to switch to async execution as early as possible in the invocation chain.
051 *
052 * @author Mark Fisher
053 * @author Juergen Hoeller
054 * @author Stephane Nicoll
055 * @since 3.0
056 * @see Async
057 * @see AsyncAnnotationAdvisor
058 * @see #setBeforeExistingAdvisors
059 * @see ScheduledAnnotationBeanPostProcessor
060 */
061@SuppressWarnings("serial")
062public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
063
064        /**
065         * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
066         * <p>Note that the initial lookup happens by type; this is just the fallback
067         * in case of multiple executor beans found in the context.
068         * @since 4.2
069         * @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME
070         */
071        public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
072                        AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;
073
074
075        protected final Log logger = LogFactory.getLog(getClass());
076
077        private Class<? extends Annotation> asyncAnnotationType;
078
079        private Executor executor;
080
081        private AsyncUncaughtExceptionHandler exceptionHandler;
082
083
084        public AsyncAnnotationBeanPostProcessor() {
085                setBeforeExistingAdvisors(true);
086        }
087
088
089        /**
090         * Set the 'async' annotation type to be detected at either class or method
091         * level. By default, both the {@link Async} annotation and the EJB 3.1
092         * {@code javax.ejb.Asynchronous} annotation will be detected.
093         * <p>This setter property exists so that developers can provide their own
094         * (non-Spring-specific) annotation type to indicate that a method (or all
095         * methods of a given class) should be invoked asynchronously.
096         * @param asyncAnnotationType the desired annotation type
097         */
098        public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
099                Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
100                this.asyncAnnotationType = asyncAnnotationType;
101        }
102
103        /**
104         * Set the {@link Executor} to use when invoking methods asynchronously.
105         * <p>If not specified, default executor resolution will apply: searching for a
106         * unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
107         * bean named "taskExecutor" otherwise. If neither of the two is resolvable,
108         * a local default executor will be created within the interceptor.
109         * @see AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(Executor, AsyncUncaughtExceptionHandler)
110         * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
111         * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
112         */
113        public void setExecutor(Executor executor) {
114                this.executor = executor;
115        }
116
117        /**
118         * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
119         * exceptions thrown by asynchronous method executions.
120         * @since 4.1
121         */
122        public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
123                this.exceptionHandler = exceptionHandler;
124        }
125
126
127        @Override
128        public void setBeanFactory(BeanFactory beanFactory) {
129                super.setBeanFactory(beanFactory);
130
131                AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
132                if (this.asyncAnnotationType != null) {
133                        advisor.setAsyncAnnotationType(this.asyncAnnotationType);
134                }
135                advisor.setBeanFactory(beanFactory);
136                this.advisor = advisor;
137        }
138
139}