Browse Source

Expose all exception causes as provided handler method arguments

Closes gh-26317
pull/26385/head
Juergen Hoeller 4 years ago
parent
commit
b587a16d46
  1. 25
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java
  2. 52
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java

25
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java

@ -1,5 +1,5 @@ @@ -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 @@ -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...

52
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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…
Cancel
Save