Handle errors via AsyncListener

This is a limited backport of commit #e0678b mainly providing the fix without
exposing the onError callbacks.

Issue: SPR-16058
This commit is contained in:
Rossen Stoyanchev
2017-11-08 23:10:16 -05:00
parent 5867ea0c3c
commit 97bc2762e1
5 changed files with 52 additions and 6 deletions

View File

@@ -50,6 +50,8 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
private final List<Runnable> timeoutHandlers = new ArrayList<Runnable>();
private ErrorHandler errorHandler;
private final List<Runnable> completionHandlers = new ArrayList<Runnable>();
@@ -78,6 +80,10 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
this.timeoutHandlers.add(timeoutHandler);
}
void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
@Override
public void addCompletionHandler(Runnable runnable) {
this.completionHandlers.add(runnable);
@@ -134,7 +140,9 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
@Override
public void onError(AsyncEvent event) throws IOException {
onComplete(event);
if (this.errorHandler != null) {
this.errorHandler.handle(event.getThrowable());
}
}
@Override
@@ -153,4 +161,11 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
this.asyncCompleted.set(true);
}
interface ErrorHandler {
void handle(Throwable ex);
}
}

View File

@@ -298,6 +298,16 @@ public final class WebAsyncManager {
}
});
if (this.asyncWebRequest instanceof StandardServletAsyncWebRequest) {
((StandardServletAsyncWebRequest) this.asyncWebRequest).setErrorHandler(
new StandardServletAsyncWebRequest.ErrorHandler() {
@Override
public void handle(Throwable ex) {
setConcurrentResultAndDispatch(ex);
}
});
}
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
@@ -399,6 +409,16 @@ public final class WebAsyncManager {
}
});
if (this.asyncWebRequest instanceof StandardServletAsyncWebRequest) {
((StandardServletAsyncWebRequest) this.asyncWebRequest).setErrorHandler(
new StandardServletAsyncWebRequest.ErrorHandler() {
@Override
public void handle(Throwable ex) {
deferredResult.setErrorResult(ex);
}
});
}
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {

View File

@@ -25,6 +25,7 @@ import org.junit.Test;
import org.springframework.mock.web.test.MockAsyncContext;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest.ErrorHandler;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
@@ -148,13 +149,26 @@ public class StandardServletAsyncWebRequestTests {
// SPR-13292
@SuppressWarnings("unchecked")
@Test
public void onCompletionHandlerAfterOnErrorEvent() throws Exception {
public void onErrorHandlerAfterOnErrorEvent() throws Exception {
ErrorHandler handler = mock(ErrorHandler.class);
this.asyncRequest.setErrorHandler(handler);
this.asyncRequest.startAsync();
Exception e = new Exception();
this.asyncRequest.onError(new AsyncEvent(this.request.getAsyncContext(), e));
verify(handler).handle(e);
}
@Test
public void onCompletionHandlerAfterOnCompleteEvent() throws Exception {
Runnable handler = mock(Runnable.class);
this.asyncRequest.addCompletionHandler(handler);
this.asyncRequest.startAsync();
this.asyncRequest.onError(new AsyncEvent(null));
this.asyncRequest.onComplete(new AsyncEvent(this.request.getAsyncContext()));
verify(handler).run();
assertTrue(this.asyncRequest.isAsyncComplete());

View File

@@ -166,11 +166,9 @@ public class ResponseBodyEmitter {
this.handler.send(object, mediaType);
}
catch (IOException ex) {
completeWithError(ex);
throw ex;
}
catch (Throwable ex) {
completeWithError(ex);
throw new IllegalStateException("Failed to send " + object, ex);
}
}

View File

@@ -150,7 +150,6 @@ public class ResponseBodyEmitterTests {
// expected
}
verify(this.handler).send("foo", MediaType.TEXT_PLAIN);
verify(this.handler).completeWithError(failure);
verifyNoMoreInteractions(this.handler);
}