Skip to content

Commit 97bc276

Browse files
committed
Handle errors via AsyncListener
This is a limited backport of commit #e0678b mainly providing the fix without exposing the onError callbacks. Issue: SPR-16058
1 parent 5867ea0 commit 97bc276

File tree

5 files changed

+52
-6
lines changed

5 files changed

+52
-6
lines changed

spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
5050

5151
private final List<Runnable> timeoutHandlers = new ArrayList<Runnable>();
5252

53+
private ErrorHandler errorHandler;
54+
5355
private final List<Runnable> completionHandlers = new ArrayList<Runnable>();
5456

5557

@@ -78,6 +80,10 @@ public void addTimeoutHandler(Runnable timeoutHandler) {
7880
this.timeoutHandlers.add(timeoutHandler);
7981
}
8082

83+
void setErrorHandler(ErrorHandler errorHandler) {
84+
this.errorHandler = errorHandler;
85+
}
86+
8187
@Override
8288
public void addCompletionHandler(Runnable runnable) {
8389
this.completionHandlers.add(runnable);
@@ -134,7 +140,9 @@ public void onStartAsync(AsyncEvent event) throws IOException {
134140

135141
@Override
136142
public void onError(AsyncEvent event) throws IOException {
137-
onComplete(event);
143+
if (this.errorHandler != null) {
144+
this.errorHandler.handle(event.getThrowable());
145+
}
138146
}
139147

140148
@Override
@@ -153,4 +161,11 @@ public void onComplete(AsyncEvent event) throws IOException {
153161
this.asyncCompleted.set(true);
154162
}
155163

164+
165+
interface ErrorHandler {
166+
167+
void handle(Throwable ex);
168+
169+
}
170+
156171
}

spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ public void run() {
298298
}
299299
});
300300

301+
if (this.asyncWebRequest instanceof StandardServletAsyncWebRequest) {
302+
((StandardServletAsyncWebRequest) this.asyncWebRequest).setErrorHandler(
303+
new StandardServletAsyncWebRequest.ErrorHandler() {
304+
@Override
305+
public void handle(Throwable ex) {
306+
setConcurrentResultAndDispatch(ex);
307+
}
308+
});
309+
}
310+
301311
this.asyncWebRequest.addCompletionHandler(new Runnable() {
302312
@Override
303313
public void run() {
@@ -399,6 +409,16 @@ public void run() {
399409
}
400410
});
401411

412+
if (this.asyncWebRequest instanceof StandardServletAsyncWebRequest) {
413+
((StandardServletAsyncWebRequest) this.asyncWebRequest).setErrorHandler(
414+
new StandardServletAsyncWebRequest.ErrorHandler() {
415+
@Override
416+
public void handle(Throwable ex) {
417+
deferredResult.setErrorResult(ex);
418+
}
419+
});
420+
}
421+
402422
this.asyncWebRequest.addCompletionHandler(new Runnable() {
403423
@Override
404424
public void run() {

spring-web/src/test/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequestTests.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.mock.web.test.MockAsyncContext;
2626
import org.springframework.mock.web.test.MockHttpServletRequest;
2727
import org.springframework.mock.web.test.MockHttpServletResponse;
28+
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest.ErrorHandler;
2829

2930
import static org.hamcrest.Matchers.containsString;
3031
import static org.junit.Assert.assertEquals;
@@ -148,13 +149,26 @@ public void onCompletionHandler() throws Exception {
148149

149150
// SPR-13292
150151

152+
@SuppressWarnings("unchecked")
151153
@Test
152-
public void onCompletionHandlerAfterOnErrorEvent() throws Exception {
154+
public void onErrorHandlerAfterOnErrorEvent() throws Exception {
155+
ErrorHandler handler = mock(ErrorHandler.class);
156+
this.asyncRequest.setErrorHandler(handler);
157+
158+
this.asyncRequest.startAsync();
159+
Exception e = new Exception();
160+
this.asyncRequest.onError(new AsyncEvent(this.request.getAsyncContext(), e));
161+
162+
verify(handler).handle(e);
163+
}
164+
165+
@Test
166+
public void onCompletionHandlerAfterOnCompleteEvent() throws Exception {
153167
Runnable handler = mock(Runnable.class);
154168
this.asyncRequest.addCompletionHandler(handler);
155169

156170
this.asyncRequest.startAsync();
157-
this.asyncRequest.onError(new AsyncEvent(null));
171+
this.asyncRequest.onComplete(new AsyncEvent(this.request.getAsyncContext()));
158172

159173
verify(handler).run();
160174
assertTrue(this.asyncRequest.isAsyncComplete());

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,9 @@ private void sendInternal(Object object, MediaType mediaType) throws IOException
166166
this.handler.send(object, mediaType);
167167
}
168168
catch (IOException ex) {
169-
completeWithError(ex);
170169
throw ex;
171170
}
172171
catch (Throwable ex) {
173-
completeWithError(ex);
174172
throw new IllegalStateException("Failed to send " + object, ex);
175173
}
176174
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ public void sendWithError() throws Exception {
150150
// expected
151151
}
152152
verify(this.handler).send("foo", MediaType.TEXT_PLAIN);
153-
verify(this.handler).completeWithError(failure);
154153
verifyNoMoreInteractions(this.handler);
155154
}
156155

0 commit comments

Comments
 (0)