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