diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
index 3da0d2e3d0..45df368430 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -23,6 +23,8 @@ 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;
@@ -46,12 +48,18 @@ import org.springframework.util.ReflectionUtils;
* (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
* or EJB 3.1's {@code javax.ejb.AsyncResult}).
*
+ *
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.
+ *
*
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
@@ -60,15 +68,35 @@ import org.springframework.util.ReflectionUtils;
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
implements MethodInterceptor, Ordered {
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private AsyncUncaughtExceptionHandler exceptionHandler;
+
/**
* Create a new {@code AsyncExecutionInterceptor}.
- * @param executor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
+ * @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 executor) {
- super(executor);
+ 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
@@ -80,8 +108,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
@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);
- specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
+ Method tmp = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
+ final Method specificMethod = BridgeMethodResolver.findBridgedMethod(tmp);
AsyncTaskExecutor executor = determineAsyncExecutor(specificMethod);
if (executor == null) {
@@ -100,7 +128,7 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
}
}
catch (Throwable ex) {
- ReflectionUtils.rethrowException(ex);
+ handleError(ex, specificMethod, invocation.getArguments());
}
return null;
}
@@ -114,6 +142,34 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
}
}
+ /**
+ * Handles a fatal error thrown while asynchronously invoking the specified
+ * {@link Method}.
+ *
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 {
+ exceptionHandler.handleUncaughtException(ex, method, params);
+ }
+ catch (Exception e) {
+ logger.error("exception handler has thrown an unexpected " +
+ "exception while invoking '" + method.toGenericString() + "'", e);
+ }
+ }
+ }
+
/**
* This implementation is a no-op for compatibility in Spring 3.1.2.
* Subclasses may override to provide support for extracting qualifier information,
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java
new file mode 100644
index 0000000000..1d54b0da0b
--- /dev/null
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+/**
+ * A strategy for handling uncaught exception thrown by asynchronous methods.
+ *
+ *
An asynchronous method usually returns a {@link java.util.concurrent.Future}
+ * instance that gives access to the underlying exception. When the method
+ * does not provide that return type, this handler can be used to managed such
+ * uncaught exceptions.
+ *
+ * @author Stephane Nicoll
+ * @since 4.1
+ */
+public interface AsyncUncaughtExceptionHandler {
+
+ /**
+ * Handle the given uncaught error thrown while processing
+ * an asynchronous method.
+ * @param ex the exception thrown by invoking the async operation
+ * @param method the async operation
+ * @param params the parameters used to invoked the method
+ */
+ void handleUncaughtException(Throwable ex, Method method, Object... params);
+}
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java
new file mode 100644
index 0000000000..984a7ec245
--- /dev/null
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A default {@link AsyncUncaughtExceptionHandler} that simply logs the exception.
+ *
+ * @author Stephane Nicoll
+ * @since 4.1
+ */
+public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
+
+ private final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
+
+ @Override
+ public void handleUncaughtException(Throwable ex, Method method, Object... params) {
+ if (logger.isErrorEnabled()) {
+ logger.error(String.format("Unexpected error occurred invoking async " +
+ "method '%s'.", method), ex);
+ }
+ }
+}
diff --git a/spring-beans/src/main/resources/META-INF/spring.schemas b/spring-beans/src/main/resources/META-INF/spring.schemas
index 7fe3564dc4..e09ad7e0d5 100644
--- a/spring-beans/src/main/resources/META-INF/spring.schemas
+++ b/spring-beans/src/main/resources/META-INF/spring.schemas
@@ -4,14 +4,16 @@ http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springfram
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd
-http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd
+http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd
+http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd
-http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd
+http\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd
+http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd
new file mode 100644
index 0000000000..78ef2dcc10
--- /dev/null
+++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd
@@ -0,0 +1,1185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element.
+ ]]>
+
+
+
+
+
+
+
+ and other elements, typically the root element in the document.
+ Allows the definition of default values for all nested bean definitions. May itself
+ be nested for the purpose of defining a subset of beans with certain default values or
+ to be registered only when certain profile(s) are active. Any such nested element
+ must be declared as the last element in the document.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element should be parsed. Multiple profiles
+ can be separated by spaces, commas, or semi-colons.
+
+ If one or more of the specified profiles are active at time of parsing, the
+ element will be parsed, and all of its elements registered, <import>
+ elements followed, etc. If none of the specified profiles are active at time of
+ parsing, then the entire element and its contents will be ignored.
+
+ If a profile is prefixed with the NOT operator '!', e.g.
+
+
+
+ indicates that the element should be parsed if profile "p1" is active or
+ if profile "p2" is not active.
+
+ Profiles are activated in one of two ways:
+ Programmatic:
+ ConfigurableEnvironment#setActiveProfiles(String...)
+ ConfigurableEnvironment#setDefaultProfiles(String...)
+
+ Properties (typically through -D system properties, environment variables, or
+ servlet context init params):
+ spring.profiles.active=p1,p2
+ spring.profiles.default=p1,p2
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element (or "ref"
+ attribute). We recommend this in most cases as it makes documentation
+ more explicit.
+
+ Note that this default mode also allows for annotation-driven autowiring,
+ if activated. "no" refers to externally driven autowiring only, not
+ affecting any autowiring demands that the bean class itself expresses.
+
+ 2. "byName"
+ Autowiring by property name. If a bean of class Cat exposes a "dog"
+ property, Spring will try to set this to the value of the bean "dog"
+ in the current container. If there is no matching bean by name, nothing
+ special happens.
+
+ 3. "byType"
+ Autowiring if there is exactly one bean of the property type in the
+ container. If there is more than one, a fatal error is raised, and
+ you cannot use byType autowiring for that bean. If there is none,
+ nothing special happens.
+
+ 4. "constructor"
+ Analogous to "byType" for constructor arguments. If there is not exactly
+ one bean of the constructor argument type in the bean factory, a fatal
+ error is raised.
+
+ Note that explicit dependencies, i.e. "property" and "constructor-arg"
+ elements, always override autowiring.
+
+ Note: This attribute will not be inherited by child bean definitions.
+ Hence, it needs to be specified per concrete bean definition.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ " element.
+ ]]>
+
+
+
+
+ ..." element.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ".
+ ]]>
+
+
+
+
+ ..." element.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ".
+ ]]>
+
+
+
+
+ ..."
+ element.
+ ]]>
+
+
+
+
+ ".
+ ]]>
+
+
+
+
+ ..." element.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.1.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.1.xsd
new file mode 100644
index 0000000000..9d84906ade
--- /dev/null
+++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.1.xsd
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java
index 95ba8a99ef..78cf5691fa 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -19,6 +19,7 @@ package org.springframework.scheduling.annotation;
import java.util.Collection;
import java.util.concurrent.Executor;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
@@ -32,6 +33,7 @@ import org.springframework.util.CollectionUtils;
* Spring's asynchronous method execution capability.
*
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.1
* @see EnableAsync
*/
@@ -41,6 +43,7 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
protected AnnotationAttributes enableAsync;
protected Executor executor;
+ protected AsyncUncaughtExceptionHandler exceptionHandler;
@Override
@@ -64,6 +67,7 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
}
AsyncConfigurer configurer = configurers.iterator().next();
this.executor = configurer.getAsyncExecutor();
+ this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
}
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
index 542aa15e32..61ea21a0d8 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -20,6 +20,8 @@ import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.core.annotation.AnnotationUtils;
/**
@@ -30,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils;
* declaring class level. See {@link #getExecutorQualifier(Method)} for details.
*
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.1.2
* @see org.springframework.scheduling.annotation.Async
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
@@ -40,9 +43,23 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor.
* @param defaultExecutor the executor to be used by default if no more specific
* executor has been qualified at the method level using {@link Async#value()}
+ * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
+ * handle exceptions thrown by asynchronous method executions with {@code void}
+ * return type
+ */
+ public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor,
+ AsyncUncaughtExceptionHandler exceptionHandler) {
+ super(defaultExecutor, exceptionHandler);
+ }
+
+ /**
+ * Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor
+ * and a simple {@link AsyncUncaughtExceptionHandler}.
+ * @param defaultExecutor the executor to be used by default if no more specific
+ * executor has been qualified at the method level using {@link Async#value()}
*/
public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) {
- super(defaultExecutor);
+ this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
}
/**
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
index 11cffe2542..ba66479ba2 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -25,6 +25,8 @@ import java.util.concurrent.Executor;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
@@ -53,6 +55,8 @@ import org.springframework.util.Assert;
@SuppressWarnings("serial")
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
+ private AsyncUncaughtExceptionHandler exceptionHandler;
+
private Advice advice;
private Pointcut pointcut;
@@ -62,15 +66,17 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
* Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
*/
public AsyncAnnotationAdvisor() {
- this(new SimpleAsyncTaskExecutor());
+ this(null, null);
}
/**
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
* @param executor the task executor to use for asynchronous methods
+ * @param exceptionHandler the {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler} to use to
+ * handle unexpected exception thrown by asynchronous method executions
*/
@SuppressWarnings("unchecked")
- public AsyncAnnotationAdvisor(Executor executor) {
+ public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
Set> asyncAnnotationTypes = new LinkedHashSet>(2);
asyncAnnotationTypes.add(Async.class);
ClassLoader cl = AsyncAnnotationAdvisor.class.getClassLoader();
@@ -80,7 +86,15 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
- this.advice = buildAdvice(executor);
+ if (executor == null) {
+ executor = new SimpleAsyncTaskExecutor();
+ }
+ if (exceptionHandler != null) {
+ this.exceptionHandler = exceptionHandler;
+ } else {
+ this.exceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
+ }
+ this.advice = buildAdvice(executor, this.exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
@@ -89,7 +103,7 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
* Specify the default task executor to use for asynchronous methods.
*/
public void setTaskExecutor(Executor executor) {
- this.advice = buildAdvice(executor);
+ this.advice = buildAdvice(executor, exceptionHandler);
}
/**
@@ -130,8 +144,8 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
}
- protected Advice buildAdvice(Executor executor) {
- return new AnnotationAsyncExecutionInterceptor(executor);
+ protected Advice buildAdvice(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
+ return new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler);
}
/**
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
index 202f2ae47d..f0a743951a 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.util.concurrent.Executor;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.task.TaskExecutor;
@@ -38,11 +39,17 @@ import org.springframework.util.Assert;
* processor will detect both Spring's {@link Async @Async} annotation as well
* as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation.
*
+ *
For methods having a {@code void} return type, any exception thrown
+ * during the asynchronous method invocation cannot be accessed by the
+ * caller. An {@link AsyncUncaughtExceptionHandler} can be specified to handle
+ * these cases.
+ *
*
Note: The underlying async advisor applies before existing advisors by default,
* in order to switch to async execution as early as possible in the invocation chain.
*
* @author Mark Fisher
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @since 3.0
* @see Async
* @see AsyncAnnotationAdvisor
@@ -55,6 +62,7 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
private Class extends Annotation> asyncAnnotationType;
private Executor executor;
+ private AsyncUncaughtExceptionHandler exceptionHandler;
public AsyncAnnotationBeanPostProcessor() {
@@ -82,10 +90,17 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
this.executor = executor;
}
+ /**
+ * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
+ * exceptions thrown by asynchronous method executions.
+ */
+ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
@Override
public void setBeanFactory(BeanFactory beanFactory) {
- AsyncAnnotationAdvisor advisor = (this.executor != null ?
- new AsyncAnnotationAdvisor(this.executor) : new AsyncAnnotationAdvisor());
+ AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java
index c57ba71e40..81d1e0afb4 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * 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.
@@ -18,17 +18,28 @@ package org.springframework.scheduling.annotation;
import java.util.concurrent.Executor;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+
/**
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
* Configuration} classes annotated with @{@link EnableAsync} that wish to customize the
- * {@link Executor} instance used when processing async method invocations.
+ * {@link Executor} instance used when processing async method invocations or the
+ * {@link AsyncUncaughtExceptionHandler} instance used to process exception thrown from
+ * async method with {@code void} return type.
+ *
+ *
Consider using {@link AsyncConfigurerSupport} providing default implementations for
+ * both methods if only one element needs to be customized. Furthermore, backward compatibility
+ * of this interface will be insured in case new customization options are introduced
+ * in the future.
*
*
See @{@link EnableAsync} for usage examples.
*
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.1
* @see AbstractAsyncConfiguration
* @see EnableAsync
+ * @see AsyncConfigurerSupport
*/
public interface AsyncConfigurer {
@@ -38,4 +49,11 @@ public interface AsyncConfigurer {
*/
Executor getAsyncExecutor();
+ /**
+ * The {@link AsyncUncaughtExceptionHandler} instance to be used
+ * when an exception is thrown during an asynchronous method execution
+ * with {@code void} return type.
+ */
+ AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler();
+
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurerSupport.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurerSupport.java
new file mode 100644
index 0000000000..27a953379f
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurerSupport.java
@@ -0,0 +1,42 @@
+/*
+ * 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.scheduling.annotation;
+
+import java.util.concurrent.Executor;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+
+/**
+ * A convenience {@link AsyncConfigurer} that implements all methods
+ * so that the defaults are used. Provides a backward compatible alternative
+ * of implementing {@link AsyncConfigurer} directly.
+ *
+ * @author Stephane Nicoll
+ * @since 4.1
+ */
+public class AsyncConfigurerSupport implements AsyncConfigurer {
+
+ @Override
+ public Executor getAsyncExecutor() {
+ return null;
+ }
+
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return null;
+ }
+}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
index 8d88bf85dd..e02ce9dd5c 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * 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.
@@ -56,10 +56,20 @@ import org.springframework.core.Ordered;
* {@code spring-aspects} module JAR must be present on the classpath.
*
*
By default, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor
- * SimpleAsyncTaskExecutor} will be used to process async method invocations. To
- * customize this behavior, implement {@link AsyncConfigurer} and
- * provide your own {@link java.util.concurrent.Executor Executor} through the
- * {@link AsyncConfigurer#getAsyncExecutor() getExecutor()} method.
+ * SimpleAsyncTaskExecutor} will be used to process async method invocations. Besides,
+ * annotated methods having a {@code void} return type cannot transmit any exception
+ * back to the caller. By default, such uncaught exceptions are only logged.
+ *
+ *
To customize all this, implement {@link AsyncConfigurer} and
+ * provide:
+ *
+ *
your own {@link java.util.concurrent.Executor Executor} through the
+ * {@link AsyncConfigurer#getAsyncExecutor() getAsyncExecutor()} method, and
+ *
your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler
+ * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler()
+ * getAsyncUncaughtExceptionHandler()}
+ * method.
If only one item needs to be customized, {@code null} can be returned to
+ * keep the default settings. Consider also extending from {@link AsyncConfigurerSupport}
+ * when possible.
+ *
*
For reference, the example above can be compared to the following Spring XML
* configuration:
*
* {@code
*
- *
+ *
*
*
+ *
*
* }
- * the examples are equivalent save the setting of the thread name prefix of the
+ * the examples are equivalent except the setting of the thread name prefix of the
* Executor; this is because the the {@code task:} namespace {@code executor} element does
* not expose such an attribute. This demonstrates how the code-based approach allows for
* maximum configurability through direct access to actual componentry.
@@ -105,6 +125,7 @@ import org.springframework.core.Ordered;
* automatically when the bean is initialized.
*
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.1
* @see Async
* @see AsyncConfigurer
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java
index 19595dfb48..c6f7443717 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * 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.
@@ -31,6 +31,7 @@ import org.springframework.util.Assert;
* to enable proxy-based asynchronous method execution.
*
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.1
* @see EnableAsync
* @see AsyncConfigurationSelector
@@ -50,6 +51,9 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
if (this.executor != null) {
bpp.setExecutor(this.executor);
}
+ if (this.exceptionHandler != null) {
+ bpp.setExceptionHandler(this.exceptionHandler);
+ }
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.getNumber("order"));
return bpp;
diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java
index 7900b9988e..ac4a16f59a 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * 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.
@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @author Chris Beams
+ * @author Stephane Nicoll
* @since 3.0
*/
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
@@ -99,6 +100,10 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse
if (StringUtils.hasText(executor)) {
builder.addPropertyReference("executor", executor);
}
+ String exceptionHandler = element.getAttribute("exception-handler");
+ if (StringUtils.hasText(exceptionHandler)) {
+ builder.addPropertyReference("exceptionHandler", exceptionHandler);
+ }
if (Boolean.valueOf(element.getAttribute(AopNamespaceUtils.PROXY_TARGET_CLASS_ATTRIBUTE))) {
builder.addPropertyValue("proxyTargetClass", true);
}
diff --git a/spring-context/src/main/resources/META-INF/spring.schemas b/spring-context/src/main/resources/META-INF/spring.schemas
index 6559107273..b246784089 100644
--- a/spring-context/src/main/resources/META-INF/spring.schemas
+++ b/spring-context/src/main/resources/META-INF/spring.schemas
@@ -22,7 +22,8 @@ http\://www.springframework.org/schema/task/spring-task-3.0.xsd=org/springframew
http\://www.springframework.org/schema/task/spring-task-3.1.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd
http\://www.springframework.org/schema/task/spring-task-3.2.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd
http\://www.springframework.org/schema/task/spring-task-4.0.xsd=org/springframework/scheduling/config/spring-task-4.0.xsd
-http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-4.0.xsd
+http\://www.springframework.org/schema/task/spring-task-4.1.xsd=org/springframework/scheduling/config/spring-task-4.1.xsd
+http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-4.1.xsd
http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd
http\://www.springframework.org/schema/cache/spring-cache-3.2.xsd=org/springframework/cache/config/spring-cache-3.2.xsd
http\://www.springframework.org/schema/cache/spring-cache-4.0.xsd=org/springframework/cache/config/spring-cache-4.0.xsd
diff --git a/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-4.1.xsd b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-4.1.xsd
new file mode 100644
index 0000000000..57f6296c02
--- /dev/null
+++ b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-4.1.xsd
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
index 8611615193..faed2eb6df 100644
--- a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
+++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * 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.
@@ -16,34 +16,38 @@
package org.springframework.scheduling.annotation;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.util.ReflectionUtils;
/**
* @author Mark Fisher
* @author Juergen Hoeller
+ * @author Stephane Nicoll
*/
public class AsyncAnnotationBeanPostProcessorTests {
@Test
public void proxyCreated() {
- StaticApplicationContext context = new StaticApplicationContext();
- BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
- BeanDefinition targetDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
- context.registerBeanDefinition("postProcessor", processorDefinition);
- context.registerBeanDefinition("target", targetDefinition);
- context.refresh();
+ ConfigurableApplicationContext context = initContext(
+ new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class));
Object target = context.getBean("target");
assertTrue(AopUtils.isAopProxy(target));
context.close();
@@ -51,12 +55,8 @@ public class AsyncAnnotationBeanPostProcessorTests {
@Test
public void invokedAsynchronously() {
- StaticApplicationContext context = new StaticApplicationContext();
- BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
- BeanDefinition targetDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
- context.registerBeanDefinition("postProcessor", processorDefinition);
- context.registerBeanDefinition("target", targetDefinition);
- context.refresh();
+ ConfigurableApplicationContext context = initContext(
+ new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class));
ITestBean testBean = (ITestBean) context.getBean("target");
testBean.test();
Thread mainThread = Thread.currentThread();
@@ -68,16 +68,12 @@ public class AsyncAnnotationBeanPostProcessorTests {
@Test
public void threadNamePrefix() {
- StaticApplicationContext context = new StaticApplicationContext();
BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("testExecutor");
executor.afterPropertiesSet();
processorDefinition.getPropertyValues().add("executor", executor);
- BeanDefinition targetDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
- context.registerBeanDefinition("postProcessor", processorDefinition);
- context.registerBeanDefinition("target", targetDefinition);
- context.refresh();
+ ConfigurableApplicationContext context = initContext(processorDefinition);
ITestBean testBean = (ITestBean) context.getBean("target");
testBean.test();
testBean.await(3000);
@@ -96,9 +92,86 @@ public class AsyncAnnotationBeanPostProcessorTests {
testBean.await(3000);
Thread asyncThread = testBean.getThread();
assertTrue(asyncThread.getName().startsWith("testExecutor"));
+
+ TestableAsyncUncaughtExceptionHandler exceptionHandler = (TestableAsyncUncaughtExceptionHandler)
+ context.getBean("exceptionHandler");
+ assertFalse("handler should not have been called yet", exceptionHandler.isCalled());
+
+ testBean.failWithVoid();
+ exceptionHandler.await(3000);
+ Method m = ReflectionUtils.findMethod(TestBean.class, "failWithVoid");
+ exceptionHandler.assertCalledWith(m, UnsupportedOperationException.class);
context.close();
}
+ @Test
+ public void handleExceptionWithFuture() {
+ ConfigurableApplicationContext context = initContext(
+ new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class));
+ ITestBean testBean = context.getBean("target", ITestBean.class);
+ final Future