-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Closed
Labels
Milestone
Description
Kent Yeh opened SPR-10838 and commented
First, TestDispatcherServlet can't using single key to intercept multiple threads.
final class TestDispatcherServlet extends DispatcherServlet {
private static final String KEY = TestDispatcherServlet.class.getName() + ".interceptor";
private CountDownLatch registerAsyncInterceptors(final HttpServletRequest servletRequest) {
//A unique key isn't suitable for multiple threads.
asyncManager.registerCallableInterceptor(KEY, new CallableProcessingInterceptorAdapter() {
...
}
...
Second, TestDispatcherServlet need override doDispatch() to sign dispatch process completed.
So, my solution is
- Add CountDownLatch member to DefaultMvcResult
class DefaultMvcResult implements MvcResult {
private CountDownLatch dispatchLatch;
public CountDownLatch getDispatchLatch() {...}
public void setDispatchLatch(CountDownLatch dispatchLatch) {...}
...
}
- Change TestDispatcherServlet KEY to automic and override doDispatch() to notify process completed.
final class TestDispatcherServlet extends DispatcherServlet {
private static final AtomicInteger idx = new AtomicInteger(0);
private CountDownLatch registerAsyncInterceptors(final HttpServletRequest servletRequest) {
final CountDownLatch asyncResultLatch = new CountDownLatch(1);
//A CountDownLatch to offer doDispatch() a chance to sign that dispatch process completed.
getMvcResult(servletRequest).setDispatchLatch(new CountDownLatch(1));
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
//Make key atomic.
String key = String.format("%s.%d", KEY, idx.getAndIncrement());
asyncManager.registerCallableInterceptor(key, new CallableProcessingInterceptorAdapter() {
...
});
asyncManager.registerDeferredResultInterceptor(key, new DeferredResultProcessingInterceptorAdapter() {
});
return asyncResultLatch;
}
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(request, response);
DefaultMvcResult mvcResult = getMvcResult(request);
//Sign for process completed.
if (mvcResult != null && mvcResult.getDispatchLatch() != null) {
mvcResult.getDispatchLatch().countDown();
}
}
- Finally override MockMvc perform()'s ResultActions to wait dispatch process completed.
public final class MockMvc {
public ResultActions perform(RequestBuilder requestBuilder) throws Exception {
....
return new ResultActions() {
...
@Override
public MvcResult andReturn() {
if (mvcResult.getDispatchLatch() != null) {
try {
HttpServletRequest request = mvcResult.getRequest();
long timeout = request.getAsyncContext() == null ? defaultTimeout : request.getAsyncContext().getTimeout();
mvcResult.getDispatchLatch().await(timeout - delayResult, TimeUnit.MILLISECONDS);
Thread.sleep(delayResult);
} catch (InterruptedException ex) {
}
}
return mvcResult;
}
}
}
...
Attatchment contain two projects that one raise error and another rewrited(source) run succees.
Affects: 3.2 GA
Attachments:
- Spring32MvcJIRA.zip (91.48 kB)
Issue Links:
- Spring MVC Test can fail to obtain async result due to timing issue [SPR-11516] #16141 Spring MVC Test can fail to obtain async result due to timing issue
Referenced from: commits 119e793, 072e5e8, ed9b296
3 votes, 10 watchers