Skip to content

Commit eab6e1d

Browse files
committed
Parameterize DeferredResult
Issue: SPR-9579
1 parent 55bd99f commit eab6e1d

File tree

6 files changed

+84
-86
lines changed

6 files changed

+84
-86
lines changed

spring-web/src/main/java/org/springframework/web/context/request/async/AsyncExecutionChain.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ private Callable<Object> buildChain() {
181181
* the threading model, i.e. whether a TaskExecutor is used.
182182
* @see DeferredResult
183183
*/
184-
public void startDeferredResultProcessing(final DeferredResult deferredResult) {
184+
public void startDeferredResultProcessing(final DeferredResult<?> deferredResult) {
185185
Assert.notNull(deferredResult, "DeferredResult is required");
186186
startAsync();
187187
deferredResult.init(new DeferredResultHandler() {
@@ -193,13 +193,7 @@ public void handle(Object result) {
193193
new AsyncExecutionChainRunnable(asyncWebRequest, buildChain()).run();
194194
}
195195
});
196-
if (deferredResult.canHandleTimeout()) {
197-
this.asyncWebRequest.setTimeoutHandler(new Runnable() {
198-
public void run() {
199-
deferredResult.handleTimeout();
200-
}
201-
});
202-
}
196+
this.asyncWebRequest.setTimeoutHandler(deferredResult.getTimeoutHandler());
203197
}
204198

205199

spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -47,68 +47,68 @@
4747
* @author Rossen Stoyanchev
4848
* @since 3.2
4949
*/
50-
public final class DeferredResult {
50+
public final class DeferredResult<V> {
5151

52-
private final static Object TIMEOUT_RESULT_NONE = new Object();
52+
private V result;
5353

54-
private Object result;
54+
private DeferredResultHandler resultHandler;
5555

56-
private final Object timeoutResult;
56+
private final V timeoutValue;
5757

58-
private DeferredResultHandler resultHandler;
58+
private final boolean timeoutValueSet;
5959

60-
private final CountDownLatch readySignal = new CountDownLatch(1);
60+
private boolean timeoutValueUsed;
6161

62-
private final ReentrantLock timeoutLock = new ReentrantLock();
62+
private final CountDownLatch initializationLatch = new CountDownLatch(1);
63+
64+
private final ReentrantLock setLock = new ReentrantLock();
6365

6466
/**
6567
* Create a new instance.
6668
*/
6769
public DeferredResult() {
68-
this(TIMEOUT_RESULT_NONE);
70+
this.timeoutValue = null;
71+
this.timeoutValueSet = false;
6972
}
7073

7174
/**
72-
* Create a new instance and also provide a default result to use if a
73-
* timeout occurs before {@link #set(Object)} is called.
75+
* Create a new instance also providing a default value to set if a timeout
76+
* occurs before {@link #set(Object)} is called.
7477
*/
75-
public DeferredResult(Object timeoutResult) {
76-
this.timeoutResult = timeoutResult;
77-
}
78-
79-
boolean canHandleTimeout() {
80-
return this.timeoutResult != TIMEOUT_RESULT_NONE;
78+
public DeferredResult(V timeoutValue) {
79+
this.timeoutValue = timeoutValue;
80+
this.timeoutValueSet = true;
8181
}
8282

8383
/**
84-
* Complete async processing with the given result. If the DeferredResult is
85-
* not yet fully initialized, this method will block and wait for that to
84+
* Complete async processing with the given value. If the DeferredResult is
85+
* not fully initialized yet, this method will block and wait for that to
8686
* occur before proceeding. See the class level javadoc for more details.
8787
*
8888
* @throws StaleAsyncWebRequestException if the underlying async request
8989
* has already timed out or ended due to a network error.
9090
*/
91-
public void set(Object result) throws StaleAsyncWebRequestException {
92-
if (this.timeoutLock.tryLock() && (this.result != this.timeoutResult)) {
91+
public void set(V value) throws StaleAsyncWebRequestException {
92+
if (this.setLock.tryLock() && (!this.timeoutValueUsed)) {
9393
try {
94-
handle(result);
94+
handle(value);
9595
}
9696
finally {
97-
this.timeoutLock.unlock();
97+
this.setLock.unlock();
9898
}
9999
}
100100
else {
101-
// A timeout is in progress
102-
throw new StaleAsyncWebRequestException("Async request already timed out");
101+
// A timeout is in progress or has already occurred
102+
throw new StaleAsyncWebRequestException("Async request timed out");
103103
}
104104
}
105105

106106
/**
107-
* A variant of {@link #set(Object)} that absorbs a potential, resulting
107+
* An alternative to {@link #set(Object)} that absorbs a potential
108108
* {@link StaleAsyncWebRequestException}.
109109
* @return {@code false} if the outcome was a {@code StaleAsyncWebRequestException}
110110
*/
111-
public boolean trySet(Object result) throws StaleAsyncWebRequestException {
111+
public boolean trySet(V result) throws StaleAsyncWebRequestException {
112112
try {
113113
set(result);
114114
return true;
@@ -119,29 +119,12 @@ public boolean trySet(Object result) throws StaleAsyncWebRequestException {
119119
return false;
120120
}
121121

122-
/**
123-
* Invoked to complete async processing when a timeout occurs before
124-
* {@link #set(Object)} is called. Or if {@link #set(Object)} is already in
125-
* progress, this method blocks, waits for it to complete, and then returns.
126-
*/
127-
void handleTimeout() {
128-
Assert.state(canHandleTimeout(), "Can't handle timeout");
129-
this.timeoutLock.lock();
130-
try {
131-
if (this.result == null) {
132-
handle(this.timeoutResult);
133-
}
134-
}
135-
finally {
136-
this.timeoutLock.unlock();
137-
}
138-
}
139-
140-
private void handle(Object result) throws StaleAsyncWebRequestException {
122+
private void handle(V result) throws StaleAsyncWebRequestException {
141123
Assert.isNull(this.result, "A deferred result can be set once only");
142124
this.result = result;
125+
this.timeoutValueUsed = (this.timeoutValueSet && (this.result == this.timeoutValue));
143126
try {
144-
this.readySignal.await(10, TimeUnit.SECONDS);
127+
this.initializationLatch.await(10, TimeUnit.SECONDS);
145128
}
146129
catch (InterruptedException e) {
147130
throw new IllegalStateException(
@@ -153,9 +136,35 @@ private void handle(Object result) throws StaleAsyncWebRequestException {
153136
this.resultHandler.handle(result);
154137
}
155138

139+
/**
140+
* Return a handler to use to complete processing using the default timeout value
141+
* provided via {@link #DeferredResult(Object)} or {@code null} if no timeout
142+
* value was provided.
143+
*/
144+
Runnable getTimeoutHandler() {
145+
if (!this.timeoutValueSet) {
146+
return null;
147+
}
148+
return new Runnable() {
149+
public void run() { useTimeoutValue(); }
150+
};
151+
}
152+
153+
private void useTimeoutValue() {
154+
this.setLock.lock();
155+
try {
156+
if (this.result == null) {
157+
handle(this.timeoutValue);
158+
this.timeoutValueUsed = true;
159+
}
160+
} finally {
161+
this.setLock.unlock();
162+
}
163+
}
164+
156165
void init(DeferredResultHandler handler) {
157166
this.resultHandler = handler;
158-
this.readySignal.countDown();
167+
this.initializationLatch.countDown();
159168
}
160169

161170

spring-web/src/test/java/org/springframework/web/context/request/async/AsyncExecutionChainTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public void startDeferredResultProcessing() throws Exception {
144144
this.chain.addDelegatingCallable(new IntegerIncrementingCallable());
145145
this.chain.addDelegatingCallable(new IntegerIncrementingCallable());
146146

147-
DeferredResult deferredResult = new DeferredResult();
147+
DeferredResult<Integer> deferredResult = new DeferredResult<Integer>();
148148
this.chain.startDeferredResultProcessing(deferredResult);
149149

150150
assertTrue(this.asyncWebRequest.isAsyncStarted());
@@ -159,7 +159,7 @@ public void startDeferredResultProcessing_staleRequest() throws Exception {
159159
this.asyncWebRequest.startAsync();
160160
this.asyncWebRequest.complete();
161161

162-
DeferredResult deferredResult = new DeferredResult();
162+
DeferredResult<Integer> deferredResult = new DeferredResult<Integer>();
163163
this.chain.startDeferredResultProcessing(deferredResult);
164164
deferredResult.set(1);
165165
}

spring-web/src/test/java/org/springframework/web/context/request/async/DeferredResultTests.java

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import static org.easymock.EasyMock.replay;
2121
import static org.easymock.EasyMock.reset;
2222
import static org.easymock.EasyMock.verify;
23-
import static org.junit.Assert.assertFalse;
24-
import static org.junit.Assert.assertTrue;
23+
import static org.junit.Assert.assertNotNull;
24+
import static org.junit.Assert.assertNull;
2525
import static org.junit.Assert.fail;
2626

2727
import org.junit.Test;
@@ -34,16 +34,10 @@
3434
*/
3535
public class DeferredResultTests {
3636

37-
@Test
38-
public void canHandleTimeout() {
39-
assertFalse(new DeferredResult().canHandleTimeout());
40-
assertTrue(new DeferredResult("foo").canHandleTimeout());
41-
}
42-
4337
@Test
4438
public void set() {
4539
DeferredResultHandler resultHandler = createMock(DeferredResultHandler.class);
46-
DeferredResult deferredResult = new DeferredResult();
40+
DeferredResult<String> deferredResult = new DeferredResult<String>();
4741
deferredResult.init(resultHandler);
4842

4943
resultHandler.handle("foo");
@@ -54,35 +48,36 @@ public void set() {
5448
verify(resultHandler);
5549
}
5650

51+
@Test
52+
public void getTimeoutHandler() {
53+
assertNull(new DeferredResult<String>().getTimeoutHandler());
54+
assertNotNull(new DeferredResult<String>("foo").getTimeoutHandler());
55+
}
56+
5757
@Test
5858
public void handleTimeout() {
5959
DeferredResultHandler resultHandler = createMock(DeferredResultHandler.class);
60-
DeferredResult deferredResult = new DeferredResult("foo");
61-
deferredResult.init(resultHandler);
62-
6360
resultHandler.handle("foo");
6461
replay(resultHandler);
6562

66-
deferredResult.handleTimeout();
63+
DeferredResult<String> deferredResult = new DeferredResult<String>("foo");
64+
deferredResult.init(resultHandler);
6765

68-
verify(resultHandler);
69-
}
66+
deferredResult.getTimeoutHandler().run();
7067

71-
@Test(expected=IllegalStateException.class)
72-
public void handleTimeout_timeoutResultNone() {
73-
new DeferredResult().handleTimeout();
68+
verify(resultHandler);
7469
}
7570

7671
@Test
77-
public void setAfterHandleTimeout() {
72+
public void setAfterTimeoutValueUsed() {
7873
DeferredResultHandler resultHandler = createMock(DeferredResultHandler.class);
79-
DeferredResult deferredResult = new DeferredResult("foo");
80-
deferredResult.init(resultHandler);
81-
8274
resultHandler.handle("foo");
8375
replay(resultHandler);
8476

85-
deferredResult.handleTimeout();
77+
DeferredResult<String> deferredResult = new DeferredResult<String>("foo");
78+
deferredResult.init(resultHandler);
79+
80+
deferredResult.getTimeoutHandler().run();
8681

8782
verify(resultHandler);
8883

@@ -96,22 +91,21 @@ public void setAfterHandleTimeout() {
9691
}
9792

9893
@Test
99-
public void setBeforeHandleTimeout() {
94+
public void setBeforeTimeoutValueUsed() {
10095
DeferredResultHandler resultHandler = createMock(DeferredResultHandler.class);
101-
DeferredResult deferredResult = new DeferredResult("foo");
102-
deferredResult.init(resultHandler);
103-
10496
resultHandler.handle("foo");
10597
replay(resultHandler);
10698

99+
DeferredResult<String> deferredResult = new DeferredResult<String>("foo");
100+
deferredResult.init(resultHandler);
107101
deferredResult.set("foo");
108102

109103
verify(resultHandler);
110104

111105
reset(resultHandler);
112106
replay(resultHandler);
113107

114-
deferredResult.handleTimeout();
108+
deferredResult.getTimeoutHandler().run();
115109

116110
verify(resultHandler);
117111
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AsyncMethodReturnValueHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void handleReturnValue(Object returnValue,
6363
chain.startCallableChainProcessing();
6464
}
6565
else if (DeferredResult.class.isAssignableFrom(paramType)) {
66-
chain.startDeferredResultProcessing((DeferredResult) returnValue);
66+
chain.startDeferredResultProcessing((DeferredResult<?>) returnValue);
6767
}
6868
else {
6969
// should never happen..

src/dist/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Changes in version 3.2 M2 (2012-08-xx)
2222
* add @ExceptionResolver annotation to detect classes with @ExceptionHandler methods
2323
* move RSS/Atom message converter registration ahead of jackson/jaxb2
2424
* handle BindException in DefaultHandlerExceptionResolver
25+
* Parameterize DefaultResult type
2526

2627

2728
Changes in version 3.2 M1 (2012-05-28)

0 commit comments

Comments
 (0)