Skip to content

Commit 6e4bbac

Browse files
committed
MvcResult returns asyncResult after asyncDispatch
Issue: SPR-16648
1 parent 2e4963f commit 6e4bbac

File tree

5 files changed

+75
-29
lines changed

5 files changed

+75
-29
lines changed

spring-test/src/main/java/org/springframework/mock/web/MockAsyncContext.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,7 +62,14 @@ public MockAsyncContext(ServletRequest request, ServletResponse response) {
6262

6363
public void addDispatchHandler(Runnable handler) {
6464
Assert.notNull(handler, "Dispatch handler must not be null");
65-
this.dispatchHandlers.add(handler);
65+
synchronized (this) {
66+
if (this.dispatchedPath == null) {
67+
this.dispatchHandlers.add(handler);
68+
}
69+
else {
70+
handler.run();
71+
}
72+
}
6673
}
6774

6875
@Override
@@ -92,9 +99,11 @@ public void dispatch(String path) {
9299

93100
@Override
94101
public void dispatch(ServletContext context, String path) {
95-
this.dispatchedPath = path;
96-
for (Runnable r : this.dispatchHandlers) {
97-
r.run();
102+
synchronized (this) {
103+
this.dispatchedPath = path;
104+
for (Runnable r : this.dispatchHandlers) {
105+
r.run();
106+
}
98107
}
99108
}
100109

spring-test/src/main/java/org/springframework/test/web/servlet/DefaultMvcResult.java

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
package org.springframework.test.web.servlet;
1818

19+
import java.util.concurrent.CountDownLatch;
20+
import java.util.concurrent.TimeUnit;
1921
import java.util.concurrent.atomic.AtomicReference;
2022

2123
import org.springframework.mock.web.MockHttpServletRequest;
2224
import org.springframework.mock.web.MockHttpServletResponse;
25+
import org.springframework.util.Assert;
2326
import org.springframework.web.servlet.FlashMap;
2427
import org.springframework.web.servlet.HandlerInterceptor;
2528
import org.springframework.web.servlet.ModelAndView;
@@ -51,6 +54,8 @@ class DefaultMvcResult implements MvcResult {
5154

5255
private final AtomicReference<Object> asyncResult = new AtomicReference<Object>(RESULT_NONE);
5356

57+
private CountDownLatch asyncDispatchLatch;
58+
5459

5560
/**
5661
* Create a new instance with the given request and response.
@@ -126,27 +131,31 @@ public Object getAsyncResult(long timeToWait) {
126131
if (this.mockRequest.getAsyncContext() != null) {
127132
timeToWait = (timeToWait == -1 ? this.mockRequest.getAsyncContext().getTimeout() : timeToWait);
128133
}
129-
130-
if (timeToWait > 0) {
131-
long endTime = System.currentTimeMillis() + timeToWait;
132-
while (System.currentTimeMillis() < endTime && this.asyncResult.get() == RESULT_NONE) {
133-
try {
134-
Thread.sleep(100);
135-
}
136-
catch (InterruptedException ex) {
137-
Thread.currentThread().interrupt();
138-
throw new IllegalStateException("Interrupted while waiting for " +
139-
"async result to be set for handler [" + this.handler + "]", ex);
140-
}
141-
}
134+
if (!awaitAsyncDispatch(timeToWait)) {
135+
throw new IllegalStateException("Async result for handler [" + this.handler + "]" +
136+
" was not set during the specified timeToWait=" + timeToWait);
142137
}
143-
144138
Object result = this.asyncResult.get();
145-
if (result == RESULT_NONE) {
146-
throw new IllegalStateException("Async result for handler [" + this.handler + "] " +
147-
"was not set during the specified timeToWait=" + timeToWait);
139+
Assert.state(result != RESULT_NONE, "Async result for handler [" + this.handler + "] was not set");
140+
return this.asyncResult.get();
141+
}
142+
143+
/**
144+
* True if is there a latch was not set, or the latch count reached 0.
145+
*/
146+
private boolean awaitAsyncDispatch(long timeout) {
147+
Assert.state(this.asyncDispatchLatch != null,
148+
"The asynDispatch CountDownLatch was not set by the TestDispatcherServlet.\n");
149+
try {
150+
return this.asyncDispatchLatch.await(timeout, TimeUnit.MILLISECONDS);
151+
}
152+
catch (InterruptedException e) {
153+
return false;
148154
}
149-
return result;
155+
}
156+
157+
void setAsyncDispatchLatch(CountDownLatch asyncDispatchLatch) {
158+
this.asyncDispatchLatch = asyncDispatchLatch;
150159
}
151160

152161
}

spring-test/src/main/java/org/springframework/test/web/servlet/TestDispatcherServlet.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
import java.io.IOException;
2020
import java.util.concurrent.Callable;
21+
import java.util.concurrent.CountDownLatch;
2122
import javax.servlet.ServletException;
2223
import javax.servlet.ServletRequest;
2324
import javax.servlet.http.HttpServletRequest;
2425
import javax.servlet.http.HttpServletResponse;
2526

27+
import org.springframework.mock.web.MockAsyncContext;
2628
import org.springframework.web.context.WebApplicationContext;
2729
import org.springframework.web.context.request.NativeWebRequest;
2830
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
@@ -63,6 +65,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
6365

6466
registerAsyncResultInterceptors(request);
6567
super.service(request, response);
68+
initAsyncDispatchLatch(request);
6669
}
6770

6871
private void registerAsyncResultInterceptors(final HttpServletRequest request) {
@@ -81,6 +84,19 @@ public <T> void postProcess(NativeWebRequest r, DeferredResult<T> result, Object
8184
});
8285
}
8386

87+
private void initAsyncDispatchLatch(HttpServletRequest request) {
88+
if (request.getAsyncContext() != null) {
89+
final CountDownLatch dispatchLatch = new CountDownLatch(1);
90+
((MockAsyncContext) request.getAsyncContext()).addDispatchHandler(new Runnable() {
91+
@Override
92+
public void run() {
93+
dispatchLatch.countDown();
94+
}
95+
});
96+
getMvcResult(request).setAsyncDispatchLatch(dispatchLatch);
97+
}
98+
}
99+
84100
protected DefaultMvcResult getMvcResult(ServletRequest request) {
85101
return (DefaultMvcResult) request.getAttribute(MockMvc.MVC_RESULT_ATTRIBUTE);
86102
}

spring-test/src/test/java/org/springframework/test/web/servlet/DefaultMvcResultTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.test.web.servlet;
1717

18+
import java.util.concurrent.CountDownLatch;
19+
1820
import org.junit.Before;
1921
import org.junit.Test;
2022

@@ -40,6 +42,7 @@ public void setup() {
4042
@Test
4143
public void getAsyncResultSuccess() throws Exception {
4244
this.mvcResult.setAsyncResult("Foo");
45+
this.mvcResult.setAsyncDispatchLatch(new CountDownLatch(0));
4346
assertEquals("Foo", this.mvcResult.getAsyncResult());
4447
}
4548

spring-web/src/test/java/org/springframework/mock/web/test/MockAsyncContext.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,7 +62,14 @@ public MockAsyncContext(ServletRequest request, ServletResponse response) {
6262

6363
public void addDispatchHandler(Runnable handler) {
6464
Assert.notNull(handler, "Dispatch handler must not be null");
65-
this.dispatchHandlers.add(handler);
65+
synchronized (this) {
66+
if (this.dispatchedPath == null) {
67+
this.dispatchHandlers.add(handler);
68+
}
69+
else {
70+
handler.run();
71+
}
72+
}
6673
}
6774

6875
@Override
@@ -92,9 +99,11 @@ public void dispatch(String path) {
9299

93100
@Override
94101
public void dispatch(ServletContext context, String path) {
95-
this.dispatchedPath = path;
96-
for (Runnable r : this.dispatchHandlers) {
97-
r.run();
102+
synchronized (this) {
103+
this.dispatchedPath = path;
104+
for (Runnable r : this.dispatchHandlers) {
105+
r.run();
106+
}
98107
}
99108
}
100109

0 commit comments

Comments
 (0)