Expose all exception causes as provided handler method arguments
Closes gh-26317
This commit is contained in:
parent
dd8ea89bea
commit
b587a16d46
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
@ -410,24 +410,27 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
|
||||
ArrayList<Throwable> exceptions = new ArrayList<>();
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
|
||||
}
|
||||
Throwable cause = exception.getCause();
|
||||
if (cause != null) {
|
||||
// Expose cause as provided argument as well
|
||||
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
|
||||
}
|
||||
else {
|
||||
// Otherwise, just the given exception as-is
|
||||
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
|
||||
// Expose causes as provided arguments as well
|
||||
Throwable exToExpose = exception;
|
||||
while (exToExpose != null) {
|
||||
exceptions.add(exToExpose);
|
||||
Throwable cause = exToExpose.getCause();
|
||||
exToExpose = (cause != exToExpose ? cause : null);
|
||||
}
|
||||
Object[] arguments = new Object[exceptions.size() + 1];
|
||||
exceptions.toArray(arguments); // efficient arraycopy call in ArrayList
|
||||
arguments[arguments.length - 1] = handlerMethod;
|
||||
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
|
||||
}
|
||||
catch (Throwable invocationEx) {
|
||||
// Any other than the original exception (or its cause) is unintended here,
|
||||
// Any other than the original exception (or a cause) is unintended here,
|
||||
// probably an accident (e.g. failed assertion or the like).
|
||||
if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
|
||||
if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
|
||||
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
|
||||
}
|
||||
// Continue with default processing of the original exception...
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
@ -66,6 +66,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* @author Arjen Poutsma
|
||||
* @author Kazuki Shimizu
|
||||
* @author Brian Clozel
|
||||
* @author Rodolphe Lecocq
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -189,6 +190,18 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
assertThat(this.response.getContentAsString()).isEqualTo("IllegalArgumentException");
|
||||
}
|
||||
|
||||
@Test // gh-26317
|
||||
void resolveExceptionResponseBodyMatchingCauseLevel2() throws UnsupportedEncodingException, NoSuchMethodException {
|
||||
Exception ex = new Exception(new Exception(new IllegalArgumentException()));
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
|
||||
this.resolver.afterPropertiesSet();
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).isNotNull();
|
||||
assertThat(mav.isEmpty()).isTrue();
|
||||
assertThat(this.response.getContentAsString()).isEqualTo("IllegalArgumentException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveExceptionResponseWriter() throws Exception {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
|
@ -257,6 +270,21 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
assertThat(this.response.getContentAsString()).isEqualTo("TestExceptionResolver: IllegalStateException");
|
||||
}
|
||||
|
||||
@Test // gh-26317
|
||||
void resolveExceptionGlobalHandlerOrderedMatchingCauseLevel2() throws Exception {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
|
||||
this.resolver.setApplicationContext(ctx);
|
||||
this.resolver.afterPropertiesSet();
|
||||
|
||||
Exception ex = new Exception(new Exception(new IllegalStateException()));
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).as("Exception was not handled").isNotNull();
|
||||
assertThat(mav.isEmpty()).isTrue();
|
||||
assertThat(this.response.getContentAsString()).isEqualTo("TestExceptionResolver: IllegalStateException");
|
||||
}
|
||||
|
||||
@Test // SPR-12605
|
||||
void resolveExceptionWithHandlerMethodArg() throws Exception {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
|
||||
|
@ -294,14 +322,15 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
this.resolver.setApplicationContext(ctx);
|
||||
this.resolver.afterPropertiesSet();
|
||||
|
||||
AssertionError err = new AssertionError("argh");
|
||||
FatalBeanException ex = new FatalBeanException("wrapped", err);
|
||||
AssertionError rootCause = new AssertionError("argh");
|
||||
FatalBeanException cause = new FatalBeanException("wrapped", rootCause);
|
||||
Exception ex = new Exception(cause); // gh-26317
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).as("Exception was not handled").isNotNull();
|
||||
assertThat(mav.isEmpty()).isTrue();
|
||||
assertThat(this.response.getContentAsString()).isEqualTo(err.toString());
|
||||
assertThat(this.response.getContentAsString()).isEqualTo(rootCause.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -319,6 +348,21 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
assertThat(this.response.getContentAsString()).isEqualTo("BasePackageTestExceptionResolver: IllegalStateException");
|
||||
}
|
||||
|
||||
@Test // gh-26317
|
||||
void resolveExceptionControllerAdviceHandlerMatchingCauseLevel2() throws Exception {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
|
||||
this.resolver.setApplicationContext(ctx);
|
||||
this.resolver.afterPropertiesSet();
|
||||
|
||||
Exception ex = new Exception(new IllegalStateException());
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).as("Exception was not handled").isNotNull();
|
||||
assertThat(mav.isEmpty()).isTrue();
|
||||
assertThat(this.response.getContentAsString()).isEqualTo("BasePackageTestExceptionResolver: IllegalStateException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveExceptionControllerAdviceNoHandler() throws Exception {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
|
||||
|
|
Loading…
Reference in New Issue