Apply AsyncUncaughtExceptionHandler to AspectJ
Prior to this commit, only @Async annotated methods with proxy style had their custom uncaught exception handler applied. This commit harmonizes the configuration so that AspectJ applies that behaviour as well. Issue: SPR-12090
This commit is contained in:
@@ -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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.scheduling.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -23,6 +24,8 @@ import java.util.concurrent.Future;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
@@ -31,6 +34,7 @@ import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.tests.Assume;
|
||||
import org.springframework.tests.TestGroup;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
@@ -40,6 +44,7 @@ import static org.junit.Assert.*;
|
||||
* Unit tests for {@link AnnotationAsyncExecutionAspect}.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AnnotationAsyncExecutionAspectTests {
|
||||
|
||||
@@ -47,6 +52,8 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
|
||||
private CountingExecutor executor;
|
||||
|
||||
private AsyncUncaughtExceptionHandler defaultExceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Assume.group(TestGroup.PERFORMANCE);
|
||||
@@ -134,6 +141,46 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertThat(e1Thread.get().getName(), startsWith("e1-"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptionHandlerCalled() {
|
||||
Method m = ReflectionUtils.findMethod(ClassWithException.class, "failWithVoid");
|
||||
TestableAsyncUncaughtExceptionHandler exceptionHandler = new TestableAsyncUncaughtExceptionHandler();
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExceptionHandler(exceptionHandler);
|
||||
try {
|
||||
assertFalse("Handler should not have been called", exceptionHandler.isCalled());
|
||||
ClassWithException obj = new ClassWithException();
|
||||
obj.failWithVoid();
|
||||
exceptionHandler.await(3000);
|
||||
exceptionHandler.assertCalledWith(m, UnsupportedOperationException.class);
|
||||
}
|
||||
finally {
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExceptionHandler(defaultExceptionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptionHandlerNeverThrowsUnexpectedException() {
|
||||
Method m = ReflectionUtils.findMethod(ClassWithException.class, "failWithVoid");
|
||||
TestableAsyncUncaughtExceptionHandler exceptionHandler = new TestableAsyncUncaughtExceptionHandler(true);
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExceptionHandler(exceptionHandler);
|
||||
try {
|
||||
assertFalse("Handler should not have been called", exceptionHandler.isCalled());
|
||||
ClassWithException obj = new ClassWithException();
|
||||
try {
|
||||
obj.failWithVoid();
|
||||
exceptionHandler.await(3000);
|
||||
exceptionHandler.assertCalledWith(m, UnsupportedOperationException.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
fail("No unexpected exception should have been received but got " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExceptionHandler(defaultExceptionHandler);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class CountingExecutor extends SimpleAsyncTaskExecutor {
|
||||
@@ -224,4 +271,12 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithException {
|
||||
|
||||
@Async
|
||||
public void failWithVoid() {
|
||||
throw new UnsupportedOperationException("failWithVoid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* A {@link AsyncUncaughtExceptionHandler} implementation used for testing purposes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TestableAsyncUncaughtExceptionHandler
|
||||
implements AsyncUncaughtExceptionHandler {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
private UncaughtExceptionDescriptor descriptor;
|
||||
|
||||
private final boolean throwUnexpectedException;
|
||||
|
||||
TestableAsyncUncaughtExceptionHandler() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
TestableAsyncUncaughtExceptionHandler(boolean throwUnexpectedException) {
|
||||
this.throwUnexpectedException = throwUnexpectedException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
|
||||
descriptor = new UncaughtExceptionDescriptor(ex, method);
|
||||
this.latch.countDown();
|
||||
if (throwUnexpectedException) {
|
||||
throw new IllegalStateException("Test exception");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCalled() {
|
||||
return descriptor != null;
|
||||
}
|
||||
|
||||
public void assertCalledWith(Method expectedMethod, Class<? extends Throwable> expectedExceptionType) {
|
||||
assertNotNull("Handler not called", descriptor);
|
||||
assertEquals("Wrong exception type", expectedExceptionType, descriptor.ex.getClass());
|
||||
assertEquals("Wrong method", expectedMethod, descriptor.method);
|
||||
}
|
||||
|
||||
public void await(long timeout) {
|
||||
try {
|
||||
this.latch.await(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UncaughtExceptionDescriptor {
|
||||
private final Throwable ex;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private UncaughtExceptionDescriptor(Throwable ex, Method method) {
|
||||
this.ex = ex;
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user