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:
Stephane Nicoll
2014-08-18 10:51:07 +02:00
parent 0dba70fe15
commit 8fc191c95e
7 changed files with 219 additions and 59 deletions

View File

@@ -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");
}
}
}

View File

@@ -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;
}
}
}