@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2015 the original author or authors .
* Copyright 2002 - 2016 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 .
@ -31,9 +31,12 @@ import org.apache.commons.logging.LogFactory;
@@ -31,9 +31,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory ;
import org.springframework.beans.factory.BeanFactoryAware ;
import org.springframework.beans.factory.NoSuchBeanDefinitionException ;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException ;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils ;
import org.springframework.core.task.AsyncListenableTaskExecutor ;
import org.springframework.core.task.AsyncTaskExecutor ;
import org.springframework.core.task.TaskExecutor ;
import org.springframework.core.task.support.TaskExecutorAdapter ;
import org.springframework.lang.UsesJava8 ;
import org.springframework.util.ClassUtils ;
@ -58,6 +61,15 @@ import org.springframework.util.concurrent.ListenableFuture;
@@ -58,6 +61,15 @@ import org.springframework.util.concurrent.ListenableFuture;
* /
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/ * *
* The default name of the { @link TaskExecutor } bean to pick up : "taskExecutor" .
* < p > Note that the initial lookup happens by type ; this is just the fallback
* in case of multiple executor beans found in the context .
* @since 4 . 2 . 6
* /
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor" ;
// Java 8's CompletableFuture type present?
private static final boolean completableFuturePresent = ClassUtils . isPresent (
"java.util.concurrent.CompletableFuture" , AsyncExecutionInterceptor . class . getClassLoader ( ) ) ;
@ -67,7 +79,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -67,7 +79,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
private final Map < Method , AsyncTaskExecutor > executors = new ConcurrentHashMap < Method , AsyncTaskExecutor > ( 16 ) ;
private Executor defaultExecutor ;
private volatile Executor defaultExecutor ;
private AsyncUncaughtExceptionHandler exceptionHandler ;
@ -75,34 +87,39 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -75,34 +87,39 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/ * *
* Create a new { @link AsyncExecutionAspectSupport } , using the provided default
* executor unless individual async methods indicate via qualifier that a more
* specific executor should be used .
* @param defaultExecutor the executor to use when executing asynchronous methods
* @param exceptionHandler the { @link AsyncUncaughtExceptionHandler } to use
* Create a new instance with a default { @link AsyncUncaughtExceptionHandler } .
* @param defaultExecutor the { @code Executor } ( typically a Spring { @code AsyncTaskExecutor }
* or { @link java . util . concurrent . ExecutorService } ) to delegate to , unless a more specific
* executor has been requested via a qualifier on the async method , in which case the
* executor will be looked up at invocation time against the enclosing bean factory
* /
public AsyncExecutionAspectSupport ( Executor defaultExecutor , AsyncUncaughtExceptionHandler exceptionHandler ) {
this . defaultExecutor = defaultExecutor ;
this . exceptionHandler = exceptionHandler ;
public AsyncExecutionAspectSupport ( Executor defaultExecutor ) {
this ( defaultExecutor , new SimpleAsyncUncaughtExceptionHandler ( ) ) ;
}
/ * *
* Create a new instance with a default { @link AsyncUncaughtExceptionHandler } .
* Create a new { @link AsyncExecutionAspectSupport } with the given exception handler .
* @param defaultExecutor the { @code Executor } ( typically a Spring { @code AsyncTaskExecutor }
* or { @link java . util . concurrent . ExecutorService } ) to delegate to , unless a more specific
* executor has been requested via a qualifier on the async method , in which case the
* executor will be looked up at invocation time against the enclosing bean factory
* @param exceptionHandler the { @link AsyncUncaughtExceptionHandler } to use
* /
public AsyncExecutionAspectSupport ( Executor defaultExecutor ) {
this ( defaultExecutor , new SimpleAsyncUncaughtExceptionHandler ( ) ) ;
public AsyncExecutionAspectSupport ( Executor defaultExecutor , AsyncUncaughtExceptionHandler exceptionHandler ) {
this . defaultExecutor = defaultExecutor ;
this . exceptionHandler = exceptionHandler ;
}
/ * *
* Supply the executor to be used when executing async methods .
* @param defaultExecutor the { @code Executor } ( typically a Spring { @code
* AsyncTaskExecutor } or { @link java . util . concurrent . ExecutorService } ) to delegate to
* unless a more specific executor has been requested via a qualifier on the async
* method , in which case the executor will be looked up at invocation time against the
* enclosing bean factory .
* @see # getExecutorQualifier
* @param defaultExecutor the { @code Executor } ( typically a Spring { @code AsyncTaskExecutor }
* or { @link java . util . concurrent . ExecutorService } ) to delegate to , unless a more specific
* executor has been requested via a qualifier on the async method , in which case the
* executor will be looked up at invocation time against the enclosing bean factory
* @see # getExecutorQualifier ( Method )
* @see # setBeanFactory ( BeanFactory )
* @see # getDefaultExecutor ( BeanFactory )
* /
public void setExecutor ( Executor defaultExecutor ) {
this . defaultExecutor = defaultExecutor ;
@ -117,7 +134,10 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -117,7 +134,10 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
}
/ * *
* Set the { @link BeanFactory } to be used when looking up executors by qualifier .
* Set the { @link BeanFactory } to be used when looking up executors by qualifier
* or when relying on the default executor lookup algorithm .
* @see # findQualifiedExecutor ( BeanFactory , String )
* @see # getDefaultExecutor ( BeanFactory )
* /
@Override
public void setBeanFactory ( BeanFactory beanFactory ) {
@ -128,26 +148,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -128,26 +148,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/ * *
* Determine the specific executor to use when executing the given method .
* Should preferably return an { @link AsyncListenableTaskExecutor } implementation .
* @return the executor to use ( or { @code null } , but just if no default executor has been set )
* @return the executor to use ( or { @code null } , but just if no default executor is available )
* /
protected AsyncTaskExecutor determineAsyncExecutor ( Method method ) {
AsyncTaskExecutor executor = this . executors . get ( method ) ;
if ( executor = = null ) {
Executor executorToUse = this . defaul tExecutor;
Executor targe tExecutor;
String qualifier = getExecutorQualifier ( method ) ;
if ( StringUtils . hasLength ( qualifier ) ) {
if ( this . beanFactory = = null ) {
throw new IllegalStateException ( "BeanFactory must be set on " + getClass ( ) . getSimpleName ( ) +
" to access qualified executor '" + qualifier + "'" ) ;
targetExecutor = findQualifiedExecutor ( this . beanFactory , qualifier ) ;
}
else {
targetExecutor = this . defaultExecutor ;
if ( targetExecutor = = null ) {
synchronized ( this . executors ) {
if ( this . defaultExecutor = = null ) {
this . defaultExecutor = getDefaultExecutor ( this . beanFactory ) ;
}
targetExecutor = this . defaultExecutor ;
}
}
executorToUse = BeanFactoryAnnotationUtils . qualifiedBeanOfType (
this . beanFactory , Executor . class , qualifier ) ;
}
else if ( executorToUse = = null ) {
if ( targetExecutor = = null ) {
return null ;
}
executor = ( executorToUse instanceof AsyncListenableTaskExecutor ?
( AsyncListenableTaskExecutor ) executorToUse : new TaskExecutorAdapter ( executorToUse ) ) ;
executor = ( targ etE xecutor instanceof AsyncListenableTaskExecutor ?
( AsyncListenableTaskExecutor ) targ etE xecutor : new TaskExecutorAdapter ( targ etE xecutor) ) ;
this . executors . put ( method , executor ) ;
}
return executor ;
@ -160,11 +186,69 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -160,11 +186,69 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
* been specified and that the { @linkplain # setExecutor ( Executor ) default executor }
* should be used .
* @param method the method to inspect for executor qualifier metadata
* @return the qualifier if specified , otherwise empty s tring or { @code null }
* @return the qualifier if specified , otherwise empty S tring or { @code null }
* @see # determineAsyncExecutor ( Method )
* @see # findQualifiedExecutor ( BeanFactory , String )
* /
protected abstract String getExecutorQualifier ( Method method ) ;
/ * *
* Retrieve a target executor for the given qualifier .
* @param qualifier the qualifier to resolve
* @return the target executor , or { @code null } if none available
* @since 4 . 2 . 6
* @see # getExecutorQualifier ( Method )
* /
protected Executor findQualifiedExecutor ( BeanFactory beanFactory , String qualifier ) {
if ( beanFactory = = null ) {
throw new IllegalStateException ( "BeanFactory must be set on " + getClass ( ) . getSimpleName ( ) +
" to access qualified executor '" + qualifier + "'" ) ;
}
return BeanFactoryAnnotationUtils . qualifiedBeanOfType ( beanFactory , Executor . class , qualifier ) ;
}
/ * *
* Retrieve or build a default executor for this advice instance .
* An executor returned from here will be cached for further use .
* < p > The default implementation searches for a unique { @link TaskExecutor } bean
* in the context , or for an { @link Executor } bean named "taskExecutor" otherwise .
* If neither of the two is resolvable , this implementation will return { @code null } .
* @param beanFactory the BeanFactory to use for a default executor lookup
* @return the default executor , or { @code null } if none available
* @since 4 . 2 . 6
* @see # findQualifiedExecutor ( BeanFactory , String )
* @see # DEFAULT_TASK_EXECUTOR_BEAN_NAME
* /
protected Executor getDefaultExecutor ( BeanFactory beanFactory ) {
if ( beanFactory ! = null ) {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return beanFactory . getBean ( TaskExecutor . class ) ;
}
catch ( NoUniqueBeanDefinitionException ex ) {
try {
return beanFactory . getBean ( DEFAULT_TASK_EXECUTOR_BEAN_NAME , Executor . class ) ;
}
catch ( NoSuchBeanDefinitionException ex2 ) {
if ( logger . isInfoEnabled ( ) ) {
logger . info ( "More than one TaskExecutor bean found within the context, and none is named " +
"'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
"as an alias) in order to use it for async processing." ) ;
}
}
}
catch ( NoSuchBeanDefinitionException ex ) {
logger . debug ( "Could not find default TaskExecutor bean" , ex ) ;
// Giving up -> either using local default executor or none at all...
logger . info ( "No TaskExecutor bean found for async processing" ) ;
}
}
return null ;
}
/ * *
* Delegate for actually executing the given task with the chosen executor .
* @param task the task to execute