Add onError callback to DeferredResult

Issue: SPR-15614
This commit is contained in:
Violeta Georgieva
2017-06-23 16:48:40 +03:00
committed by Rossen Stoyanchev
parent 140542e8b1
commit e0678ba583
27 changed files with 771 additions and 41 deletions

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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;
}
}
}

View File

@@ -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();