/*
* 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 javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.mock.web.test.MockHttpServletRequest;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Test fixture with an {@link WebAsyncManager} with a mock AsyncWebRequest.
*
* @author Rossen Stoyanchev
*/
public class WebAsyncManagerTests {
private WebAsyncManager asyncManager;
private AsyncWebRequest asyncWebRequest;
private MockHttpServletRequest servletRequest;
@Before
public void setup() {
this.servletRequest = new MockHttpServletRequest();
this.asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
this.asyncManager.setTaskExecutor(new SyncTaskExecutor());
this.asyncWebRequest = mock(AsyncWebRequest.class);
this.asyncManager.setAsyncWebRequest(this.asyncWebRequest);
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
reset(this.asyncWebRequest);
}
@Test
public void startAsyncProcessingWithoutAsyncWebRequest() throws Exception {
WebAsyncManager manager = WebAsyncUtils.getAsyncManager(new MockHttpServletRequest());
try {
manager.startCallableProcessing(new StubCallable(1));
fail("Expected exception");
}
catch (IllegalStateException ex) {
assertEquals(ex.getMessage(), "AsyncWebRequest must not be null");
}
try {
manager.startDeferredResultProcessing(new DeferredResult<String>());
fail("Expected exception");
}
catch (IllegalStateException ex) {
assertEquals(ex.getMessage(), "AsyncWebRequest must not be null");
}
}
@Test
public void isConcurrentHandlingStarted() {
given(this.asyncWebRequest.isAsyncStarted()).willReturn(false);
assertFalse(this.asyncManager.isConcurrentHandlingStarted());
reset(this.asyncWebRequest);
given(this.asyncWebRequest.isAsyncStarted()).willReturn(true);
assertTrue(this.asyncManager.isConcurrentHandlingStarted());
}
@Test(expected = IllegalArgumentException.class)
public void setAsyncWebRequestAfterAsyncStarted() {
this.asyncWebRequest.startAsync();
this.asyncManager.setAsyncWebRequest(null);
}
@Test
public void startCallableProcessing() throws Exception {
int concurrentResult = 21;
Callable<Object> task = new StubCallable(concurrentResult);
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
setupDefaultAsyncScenario();
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
this.asyncManager.startCallableProcessing(task);
assertTrue(this.asyncManager.hasConcurrentResult());
assertEquals(concurrentResult, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor).preProcess(this.asyncWebRequest, task);
verify(interceptor).postProcess(this.asyncWebRequest, task, new Integer(concurrentResult));
}
@Test
public void startCallableProcessingCallableException() throws Exception {
Exception concurrentResult = new Exception();
Callable<Object> task = new StubCallable(concurrentResult);
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
setupDefaultAsyncScenario();
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
this.asyncManager.startCallableProcessing(task);
assertTrue(this.asyncManager.hasConcurrentResult());
assertEquals(concurrentResult, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor).preProcess(this.asyncWebRequest, task);
verify(interceptor).postProcess(this.asyncWebRequest, task, concurrentResult);
}
@Test
public void startCallableProcessingBeforeConcurrentHandlingException() throws Exception {
Callable<Object> task = new StubCallable(21);
Exception exception = new Exception();
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
willThrow(exception).given(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
try {
this.asyncManager.startCallableProcessing(task);
fail("Expected Exception");
}
catch (Exception ex) {
assertEquals(exception, ex);
}
assertFalse(this.asyncManager.hasConcurrentResult());
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
}
@Test
public void startCallableProcessingPreProcessException() throws Exception {
Callable<Object> task = new StubCallable(21);
Exception exception = new Exception();
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
willThrow(exception).given(interceptor).preProcess(this.asyncWebRequest, task);
setupDefaultAsyncScenario();
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
this.asyncManager.startCallableProcessing(task);
assertTrue(this.asyncManager.hasConcurrentResult());
assertEquals(exception, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
}
@Test
public void startCallableProcessingPostProcessException() throws Exception {
Callable<Object> task = new StubCallable(21);
Exception exception = new Exception();
CallableProcessingInterceptor interceptor = mock(CallableProcessingInterceptor.class);
willThrow(exception).given(interceptor).postProcess(this.asyncWebRequest, task, 21);
setupDefaultAsyncScenario();
this.asyncManager.registerCallableInterceptor("interceptor", interceptor);
this.asyncManager.startCallableProcessing(task);
assertTrue(this.asyncManager.hasConcurrentResult());
assertEquals(exception, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor).preProcess(this.asyncWebRequest, task);
}
@Test
public void startCallableProcessingPostProcessContinueAfterException() throws Exception {
Callable<Object> task = new StubCallable(21);
Exception exception = new Exception();
CallableProcessingInterceptor interceptor1 = mock(CallableProcessingInterceptor.class);
CallableProcessingInterceptor interceptor2 = mock(CallableProcessingInterceptor.class);
willThrow(exception).given(interceptor2).postProcess(this.asyncWebRequest, task, 21);
setupDefaultAsyncScenario();
this.asyncManager.registerCallableInterceptors(interceptor1, interceptor2);
this.asyncManager.startCallableProcessing(task);
assertTrue(this.asyncManager.hasConcurrentResult());
assertEquals(exception, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor1).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor1).preProcess(this.asyncWebRequest, task);
verify(interceptor1).postProcess(this.asyncWebRequest, task, 21);
verify(interceptor2).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor2).preProcess(this.asyncWebRequest, task);
}
@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).addCompletionHandler(any(Runnable.class));
verify(this.asyncWebRequest).startAsync();
}
@Test
public void startCallableProcessingNullInput() throws Exception {
try {
this.asyncManager.startCallableProcessing((Callable<?>) null);
fail("Expected exception");
}
catch (IllegalArgumentException ex) {
assertEquals(ex.getMessage(), "Callable must not be null");
}
}
@Test
public void startDeferredResultProcessing() throws Exception {
DeferredResult<String> deferredResult = new DeferredResult<>(1000L);
String concurrentResult = "abc";
DeferredResultProcessingInterceptor interceptor = mock(DeferredResultProcessingInterceptor.class);
setupDefaultAsyncScenario();
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
this.asyncManager.startDeferredResultProcessing(deferredResult);
deferredResult.setResult(concurrentResult);
assertEquals(concurrentResult, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult);
verify(interceptor).preProcess(this.asyncWebRequest, deferredResult);
verify(interceptor).postProcess(asyncWebRequest, deferredResult, concurrentResult);
verify(this.asyncWebRequest).setTimeout(1000L);
}
@Test
public void startDeferredResultProcessingBeforeConcurrentHandlingException() throws Exception {
DeferredResult<Integer> deferredResult = new DeferredResult<>();
Exception exception = new Exception();
DeferredResultProcessingInterceptor interceptor = mock(DeferredResultProcessingInterceptor.class);
willThrow(exception).given(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult);
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
try {
this.asyncManager.startDeferredResultProcessing(deferredResult);
fail("Expected Exception");
}
catch (Exception success) {
assertEquals(exception, success);
}
assertFalse(this.asyncManager.hasConcurrentResult());
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
}
@Test
public void startDeferredResultProcessingPreProcessException() throws Exception {
DeferredResult<Integer> deferredResult = new DeferredResult<>();
Exception exception = new Exception();
DeferredResultProcessingInterceptor interceptor = mock(DeferredResultProcessingInterceptor.class);
willThrow(exception).given(interceptor).preProcess(this.asyncWebRequest, deferredResult);
setupDefaultAsyncScenario();
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
this.asyncManager.startDeferredResultProcessing(deferredResult);
deferredResult.setResult(25);
assertEquals(exception, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult);
}
@Test
public void startDeferredResultProcessingPostProcessException() throws Exception {
DeferredResult<Integer> deferredResult = new DeferredResult<>();
Exception exception = new Exception();
DeferredResultProcessingInterceptor interceptor = mock(DeferredResultProcessingInterceptor.class);
willThrow(exception).given(interceptor).postProcess(this.asyncWebRequest, deferredResult, 25);;
setupDefaultAsyncScenario();
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
this.asyncManager.startDeferredResultProcessing(deferredResult);
deferredResult.setResult(25);
assertEquals(exception, this.asyncManager.getConcurrentResult());
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult);
verify(interceptor).preProcess(this.asyncWebRequest, deferredResult);
}
@Test
public void startDeferredResultProcessingNullInput() throws Exception {
try {
this.asyncManager.startDeferredResultProcessing((DeferredResult<?>) null);
fail("Expected exception");
}
catch (IllegalArgumentException ex) {
assertEquals(ex.getMessage(), "DeferredResult must not be null");
}
}
private void setupDefaultAsyncScenario() {
given(this.asyncWebRequest.getNativeRequest(HttpServletRequest.class)).willReturn(this.servletRequest);
given(this.asyncWebRequest.isAsyncComplete()).willReturn(false);
}
private void verifyDefaultAsyncScenario() {
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
verify(this.asyncWebRequest).startAsync();
verify(this.asyncWebRequest).dispatch();
}
private final class StubCallable implements Callable<Object> {
private Object value;
public StubCallable(Object value) {
this.value = value;
}
@Override
public Object call() throws Exception {
if (this.value instanceof Exception) {
throw ((Exception) this.value);
}
return this.value;
}
}
@SuppressWarnings("serial")
private static class SyncTaskExecutor extends SimpleAsyncTaskExecutor {
@Override
public void execute(Runnable task, long startTimeout) {
task.run();
}
}
}