Skip to content

Commit b30f4d7

Browse files
committed
Exposes all root causes to ExceptionHandler methods
Closes gh-28155
1 parent b6b03f3 commit b30f4d7

File tree

3 files changed

+25
-14
lines changed

3 files changed

+25
-14
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.reactive.result.method.annotation;
1818

19+
import java.util.ArrayList;
1920
import java.util.Collections;
2021
import java.util.List;
2122
import java.util.function.Function;
@@ -213,21 +214,30 @@ private Mono<HandlerResult> handleException(Throwable exception, HandlerMethod h
213214

214215
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod);
215216
if (invocable != null) {
217+
ArrayList<Throwable> exceptions = new ArrayList<>();
216218
try {
217219
if (logger.isDebugEnabled()) {
218220
logger.debug(exchange.getLogPrefix() + "Using @ExceptionHandler " + invocable);
219221
}
220222
bindingContext.getModel().asMap().clear();
221-
Throwable cause = exception.getCause();
222-
if (cause != null) {
223-
return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod);
224-
}
225-
else {
226-
return invocable.invoke(exchange, bindingContext, exception, handlerMethod);
223+
224+
// Expose causes as provided arguments as well
225+
Throwable exToExpose = exception;
226+
while (exToExpose != null) {
227+
exceptions.add(exToExpose);
228+
Throwable cause = exToExpose.getCause();
229+
exToExpose = (cause != exToExpose ? cause : null);
227230
}
231+
Object[] arguments = new Object[exceptions.size() + 1];
232+
exceptions.toArray(arguments); // efficient arraycopy call in ArrayList
233+
arguments[arguments.length - 1] = handlerMethod;
234+
235+
return invocable.invoke(exchange, bindingContext, arguments);
228236
}
229237
catch (Throwable invocationEx) {
230-
if (logger.isWarnEnabled()) {
238+
// Any other than the original exception (or a cause) is unintended here,
239+
// probably an accident (e.g. failed assertion or the like).
240+
if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
231241
logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx);
232242
}
233243
}

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ControllerAdviceTests.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -82,9 +82,10 @@ public void resolveExceptionWithAssertionError() throws Exception {
8282

8383
@Test
8484
public void resolveExceptionWithAssertionErrorAsRootCause() throws Exception {
85-
AssertionError cause = new AssertionError("argh");
86-
FatalBeanException exception = new FatalBeanException("wrapped", cause);
87-
testException(exception, cause.toString());
85+
AssertionError rootCause = new AssertionError("argh");
86+
FatalBeanException cause = new FatalBeanException("wrapped", rootCause);
87+
Exception exception = new Exception(cause);
88+
testException(exception, rootCause.toString());
8889
}
8990

9091
private void testException(Throwable exception, String expected) throws Exception {

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -138,7 +138,7 @@ public Publisher<String> handleAndThrowExceptionWithCause() {
138138

139139
@GetMapping("/thrown-exception-with-cause-to-handle")
140140
public Publisher<String> handleAndThrowExceptionWithCauseToHandle() {
141-
throw new RuntimeException("State", new IOException("IO"));
141+
throw new RuntimeException("State1", new RuntimeException("State2", new IOException("IO")));
142142
}
143143

144144
@GetMapping(path = "/mono-error")

0 commit comments

Comments
 (0)