Add onError callback to DeferredResult
Issue: SPR-15614
This commit is contained in:
committed by
Rossen Stoyanchev
parent
140542e8b1
commit
e0678ba583
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.web.context.request.async.DeferredResult.DeferredResultHandler;
|
||||
@@ -125,4 +127,27 @@ public class DeferredResultTests {
|
||||
verify(handler).handleResult("timeout result");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onError() throws Exception {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
DeferredResultHandler handler = mock(DeferredResultHandler.class);
|
||||
|
||||
DeferredResult<String> result = new DeferredResult<>(null, "error result");
|
||||
result.setResultHandler(handler);
|
||||
Exception e = new Exception();
|
||||
result.onError(new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(Throwable t) {
|
||||
sb.append("error event");
|
||||
}
|
||||
});
|
||||
|
||||
result.getInterceptor().handleError(null, null, e);
|
||||
|
||||
assertEquals("error event", sb.toString());
|
||||
assertFalse("Should not be able to set result a second time", result.setResult("hello"));
|
||||
verify(handler).handleResult(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.servlet.AsyncEvent;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -128,6 +130,16 @@ public class StandardServletAsyncWebRequestTests {
|
||||
verify(timeoutHandler).run();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void onErrorHandler() throws Exception {
|
||||
Consumer<Throwable> errorHandler = mock(Consumer.class);
|
||||
this.asyncRequest.addErrorHandler(errorHandler);
|
||||
Exception e = new Exception();
|
||||
this.asyncRequest.onError(new AsyncEvent(new MockAsyncContext(this.request, this.response), e));
|
||||
verify(errorHandler).accept(e);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void setTimeoutDuringConcurrentHandling() {
|
||||
this.asyncRequest.startAsync();
|
||||
@@ -148,13 +160,26 @@ public class StandardServletAsyncWebRequestTests {
|
||||
|
||||
// SPR-13292
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void onCompletionHandlerAfterOnErrorEvent() throws Exception {
|
||||
public void onErrorHandlerAfterOnErrorEvent() throws Exception {
|
||||
Consumer<Throwable> handler = mock(Consumer.class);
|
||||
this.asyncRequest.addErrorHandler(handler);
|
||||
|
||||
this.asyncRequest.startAsync();
|
||||
Exception e = new Exception();
|
||||
this.asyncRequest.onError(new AsyncEvent(this.request.getAsyncContext(), e));
|
||||
|
||||
verify(handler).accept(e);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCompletionHandlerAfterOnCompleteEvent() throws Exception {
|
||||
Runnable handler = mock(Runnable.class);
|
||||
this.asyncRequest.addCompletionHandler(handler);
|
||||
|
||||
this.asyncRequest.startAsync();
|
||||
this.asyncRequest.onError(new AsyncEvent(this.request.getAsyncContext()));
|
||||
this.asyncRequest.onComplete(new AsyncEvent(this.request.getAsyncContext()));
|
||||
|
||||
verify(handler).run();
|
||||
assertTrue(this.asyncRequest.isAsyncComplete());
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.servlet.AsyncEvent;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
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.NativeWebRequest;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.springframework.web.context.request.async.CallableProcessingInterceptor.*;
|
||||
|
||||
/**
|
||||
* {@link WebAsyncManager} tests where container-triggered error/completion
|
||||
* events are simulated.
|
||||
*
|
||||
* @author Violeta Georgieva
|
||||
* @since 5.0
|
||||
*/
|
||||
public class WebAsyncManagerErrorTests {
|
||||
|
||||
private WebAsyncManager asyncManager;
|
||||
|
||||
private StandardServletAsyncWebRequest asyncWebRequest;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.servletRequest = new MockHttpServletRequest("GET", "/test");
|
||||
this.servletRequest.setAsyncSupported(true);
|
||||
this.servletResponse = new MockHttpServletResponse();
|
||||
this.asyncWebRequest = new StandardServletAsyncWebRequest(servletRequest, servletResponse);
|
||||
|
||||
AsyncTaskExecutor executor = mock(AsyncTaskExecutor.class);
|
||||
|
||||
this.asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
|
||||
this.asyncManager.setTaskExecutor(executor);
|
||||
this.asyncManager.setAsyncWebRequest(this.asyncWebRequest);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingErrorAndComplete() throws Exception {
|
||||
StubCallable callable = new StubCallable();
|
||||
|
||||
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
|
||||
Exception e = new Exception();
|
||||
given(interceptor.handleError(this.asyncWebRequest, callable, e)).willReturn(RESULT_NONE);
|
||||
|
||||
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
|
||||
this.asyncManager.startCallableProcessing(callable);
|
||||
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
this.asyncWebRequest.onComplete(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
|
||||
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, callable);
|
||||
verify(interceptor).afterCompletion(this.asyncWebRequest, callable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingErrorAndResumeThroughCallback() throws Exception {
|
||||
|
||||
StubCallable callable = new StubCallable();
|
||||
WebAsyncTask<Object> webAsyncTask = new WebAsyncTask<>(callable);
|
||||
webAsyncTask.onError(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return 7;
|
||||
}
|
||||
});
|
||||
|
||||
this.asyncManager.startCallableProcessing(webAsyncTask);
|
||||
|
||||
Exception e = new Exception();
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(7, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingErrorAndResumeThroughInterceptor() throws Exception {
|
||||
|
||||
StubCallable callable = new StubCallable();
|
||||
|
||||
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
|
||||
Exception e = new Exception();
|
||||
given(interceptor.handleError(this.asyncWebRequest, callable, e)).willReturn(22);
|
||||
|
||||
this.asyncManager.registerCallableInterceptor("errorInterceptor", interceptor);
|
||||
this.asyncManager.startCallableProcessing(callable);
|
||||
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(22, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
|
||||
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, callable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingAfterException() throws Exception {
|
||||
|
||||
StubCallable callable = new StubCallable();
|
||||
Exception exception = new Exception();
|
||||
|
||||
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
|
||||
Exception e = new Exception();
|
||||
given(interceptor.handleError(this.asyncWebRequest, callable, e)).willThrow(exception);
|
||||
|
||||
this.asyncManager.registerCallableInterceptor("errorInterceptor", interceptor);
|
||||
this.asyncManager.startCallableProcessing(callable);
|
||||
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(exception, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
|
||||
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, callable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingErrorAndComplete() throws Exception {
|
||||
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<>();
|
||||
|
||||
DeferredResultProcessingInterceptor interceptor = mock(DeferredResultProcessingInterceptor.class);
|
||||
Exception e = new Exception();
|
||||
given(interceptor.handleError(this.asyncWebRequest, deferredResult, e)).willReturn(true);
|
||||
|
||||
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
this.asyncWebRequest.onComplete(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
|
||||
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult);
|
||||
verify(interceptor).preProcess(this.asyncWebRequest, deferredResult);
|
||||
verify(interceptor).afterCompletion(this.asyncWebRequest, deferredResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingErrorAndResumeWithDefaultResult() throws Exception {
|
||||
|
||||
Exception e = new Exception();
|
||||
DeferredResult<Throwable> deferredResult = new DeferredResult<>(null, e);
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingErrorAndResumeThroughCallback() throws Exception {
|
||||
|
||||
final DeferredResult<Throwable> deferredResult = new DeferredResult<>();
|
||||
deferredResult.onError(new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(Throwable t) {
|
||||
deferredResult.setResult(t);
|
||||
}
|
||||
});
|
||||
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
Exception e = new Exception();
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingErrorAndResumeThroughInterceptor() throws Exception {
|
||||
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<>();
|
||||
|
||||
DeferredResultProcessingInterceptor interceptor = new DeferredResultProcessingInterceptorAdapter() {
|
||||
@Override
|
||||
public <T> boolean handleError(NativeWebRequest request, DeferredResult<T> result, Throwable t)
|
||||
throws Exception {
|
||||
result.setErrorResult(t);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
Exception e = new Exception();
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingAfterException() throws Exception {
|
||||
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<>();
|
||||
final Exception exception = new Exception();
|
||||
|
||||
DeferredResultProcessingInterceptor interceptor = new DeferredResultProcessingInterceptorAdapter() {
|
||||
@Override
|
||||
public <T> boolean handleError(NativeWebRequest request, DeferredResult<T> result, Throwable t)
|
||||
throws Exception {
|
||||
throw exception;
|
||||
}
|
||||
};
|
||||
|
||||
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
Exception e = new Exception();
|
||||
AsyncEvent event = new AsyncEvent(new MockAsyncContext(this.servletRequest, this.servletResponse), e);
|
||||
this.asyncWebRequest.onError(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(e, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
|
||||
private final class StubCallable implements Callable<Object> {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return 21;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -138,6 +140,7 @@ public class WebAsyncManagerTests {
|
||||
verify(interceptor).postProcess(this.asyncWebRequest, task, concurrentResult);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void startCallableProcessingBeforeConcurrentHandlingException() throws Exception {
|
||||
Callable<Object> task = new StubCallable(21);
|
||||
@@ -159,6 +162,7 @@ public class WebAsyncManagerTests {
|
||||
assertFalse(this.asyncManager.hasConcurrentResult());
|
||||
|
||||
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
|
||||
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
|
||||
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
|
||||
}
|
||||
|
||||
@@ -228,18 +232,19 @@ public class WebAsyncManagerTests {
|
||||
verify(interceptor2).preProcess(this.asyncWebRequest, task);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void startCallableProcessingWithAsyncTask() throws Exception {
|
||||
AsyncTaskExecutor executor = mock(AsyncTaskExecutor.class);
|
||||
given(this.asyncWebRequest.getNativeRequest(HttpServletRequest.class)).willReturn(this.servletRequest);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WebAsyncTask<Object> asyncTask = new WebAsyncTask<>(1000L, executor, mock(Callable.class));
|
||||
this.asyncManager.startCallableProcessing(asyncTask);
|
||||
|
||||
verify(executor).submit((Runnable) notNull());
|
||||
verify(this.asyncWebRequest).setTimeout(1000L);
|
||||
verify(this.asyncWebRequest).addTimeoutHandler(any(Runnable.class));
|
||||
verify(this.asyncWebRequest).addErrorHandler(any(Consumer.class));
|
||||
verify(this.asyncWebRequest).addCompletionHandler(any(Runnable.class));
|
||||
verify(this.asyncWebRequest).startAsync();
|
||||
}
|
||||
@@ -277,6 +282,7 @@ public class WebAsyncManagerTests {
|
||||
verify(this.asyncWebRequest).setTimeout(1000L);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void startDeferredResultProcessingBeforeConcurrentHandlingException() throws Exception {
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<>();
|
||||
@@ -298,6 +304,7 @@ public class WebAsyncManagerTests {
|
||||
assertFalse(this.asyncManager.hasConcurrentResult());
|
||||
|
||||
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
|
||||
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
|
||||
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
|
||||
}
|
||||
|
||||
@@ -359,8 +366,10 @@ public class WebAsyncManagerTests {
|
||||
given(this.asyncWebRequest.isAsyncComplete()).willReturn(false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void verifyDefaultAsyncScenario() {
|
||||
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
|
||||
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
|
||||
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
|
||||
verify(this.asyncWebRequest).startAsync();
|
||||
verify(this.asyncWebRequest).dispatch();
|
||||
|
||||
Reference in New Issue
Block a user