提交 119e7939 编写于 作者: R Rossen Stoyanchev

Fix concurrency issue in TestDispatcherServlet

This change fixes a timing issue in tests using Spring MVC Tests where
assertions on an async result may not wait long enough.

The fix involves the use of a new callback in  MockAsyncContext that
allows tests to detect when an async dispatch has been invoked.

Issue: SPR-10838
上级 ce3e5574
......@@ -28,7 +28,9 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.web.util.WebUtils;
/**
......@@ -49,6 +51,8 @@ public class MockAsyncContext implements AsyncContext {
private long timeout = 10 * 1000L; // 10 seconds is Tomcat's default
private final List<Runnable> dispatchHandlers = new ArrayList<Runnable>();
public MockAsyncContext(ServletRequest request, ServletResponse response) {
this.request = (HttpServletRequest) request;
......@@ -56,6 +60,11 @@ public class MockAsyncContext implements AsyncContext {
}
public void addDispatchHandler(Runnable handler) {
Assert.notNull(handler);
this.dispatchHandlers.add(handler);
}
@Override
public ServletRequest getRequest() {
return this.request;
......@@ -84,6 +93,9 @@ public class MockAsyncContext implements AsyncContext {
@Override
public void dispatch(ServletContext context, String path) {
this.dispatchedPath = path;
for (Runnable r : this.dispatchHandlers) {
r.run();
}
}
public String getDispatchedPath() {
......
......@@ -25,13 +25,10 @@ import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.mock.web.MockAsyncContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.request.async.*;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView;
......@@ -50,6 +47,7 @@ final class TestDispatcherServlet extends DispatcherServlet {
private static final String KEY = TestDispatcherServlet.class.getName() + ".interceptor";
/**
* Create a new instance with the given web application context.
*/
......@@ -57,37 +55,44 @@ final class TestDispatcherServlet extends DispatcherServlet {
super(webApplicationContext);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
CountDownLatch latch = registerAsyncInterceptors(request);
getMvcResult(request).setAsyncResultLatch(latch);
registerAsyncResultInterceptors(request);
super.service(request, response);
}
private CountDownLatch registerAsyncInterceptors(final HttpServletRequest servletRequest) {
final CountDownLatch asyncResultLatch = new CountDownLatch(1);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
if (request.isAsyncStarted()) {
addAsyncResultLatch(request);
}
}
private void registerAsyncResultInterceptors(final HttpServletRequest request) {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(KEY, new CallableProcessingInterceptorAdapter() {
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object value) throws Exception {
getMvcResult(servletRequest).setAsyncResult(value);
asyncResultLatch.countDown();
public <T> void postProcess(NativeWebRequest r, Callable<T> task, Object value) throws Exception {
getMvcResult(request).setAsyncResult(value);
}
});
asyncManager.registerDeferredResultInterceptor(KEY, new DeferredResultProcessingInterceptorAdapter() {
@Override
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> result, Object value) throws Exception {
getMvcResult(servletRequest).setAsyncResult(value);
asyncResultLatch.countDown();
public <T> void postProcess(NativeWebRequest r, DeferredResult<T> result, Object value) throws Exception {
getMvcResult(request).setAsyncResult(value);
}
});
}
return asyncResultLatch;
private void addAsyncResultLatch(HttpServletRequest request) {
final CountDownLatch latch = new CountDownLatch(1);
((MockAsyncContext) request.getAsyncContext()).addDispatchHandler(new Runnable() {
@Override
public void run() {
latch.countDown();
}
});
getMvcResult(request).setAsyncResultLatch(latch);
}
protected DefaultMvcResult getMvcResult(ServletRequest request) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册