20
20
import org .apache .commons .logging .Log ;
21
21
import org .apache .commons .logging .LogFactory ;
22
22
import org .springframework .util .Assert ;
23
+ import org .springframework .web .context .request .NativeWebRequest ;
23
24
24
25
/**
25
26
* {@code DeferredResult} provides an alternative to using a {@link Callable}
@@ -41,6 +42,10 @@ public final class DeferredResult<T> {
41
42
42
43
private final Object timeoutResult ;
43
44
45
+ private Runnable timeoutCallback ;
46
+
47
+ private Runnable completionCallback ;
48
+
44
49
private DeferredResultHandler resultHandler ;
45
50
46
51
private Object result = RESULT_NONE ;
@@ -56,15 +61,16 @@ public DeferredResult() {
56
61
}
57
62
58
63
/**
59
- * Create a DeferredResult with a timeout.
64
+ * Create a DeferredResult with a timeout value .
60
65
* @param timeout timeout value in milliseconds
61
66
*/
62
67
public DeferredResult (long timeout ) {
63
68
this (timeout , RESULT_NONE );
64
69
}
65
70
66
71
/**
67
- * Create a DeferredResult with a timeout and a default result to use on timeout.
72
+ * Create a DeferredResult with a timeout value and a default result to use
73
+ * in case of timeout.
68
74
* @param timeout timeout value in milliseconds; ignored if {@code null}
69
75
* @param timeoutResult the result to use
70
76
*/
@@ -73,13 +79,48 @@ public DeferredResult(Long timeout, Object timeoutResult) {
73
79
this .timeout = timeout ;
74
80
}
75
81
82
+ /**
83
+ * Return {@code true} if this DeferredResult is no longer usable either
84
+ * because it was previously set or because the underlying request expired.
85
+ * <p>
86
+ * The result may have been set with a call to {@link #setResult(Object)},
87
+ * or {@link #setErrorResult(Object)}, or as a result of a timeout, if a
88
+ * timeout result was provided to the constructor. The request may also
89
+ * expire due to a timeout or network error.
90
+ */
91
+ public boolean isSetOrExpired () {
92
+ return ((this .result != RESULT_NONE ) || this .expired );
93
+ }
94
+
76
95
/**
77
96
* Return the configured timeout value in milliseconds.
78
97
*/
79
- public Long getTimeoutMilliseconds () {
98
+ Long getTimeoutValue () {
80
99
return this .timeout ;
81
100
}
82
101
102
+ /**
103
+ * Register code to invoke when the async request times out. This method is
104
+ * called from a container thread when an async request times out before the
105
+ * {@code DeferredResult} has been set. It may invoke
106
+ * {@link DeferredResult#setResult(Object) setResult} or
107
+ * {@link DeferredResult#setErrorResult(Object) setErrorResult} to resume
108
+ * processing.
109
+ */
110
+ public void onTimeout (Runnable callback ) {
111
+ this .timeoutCallback = callback ;
112
+ }
113
+
114
+ /**
115
+ * Register code to invoke when the async request completes. This method is
116
+ * called from a container thread when an async request completed for any
117
+ * reason including timeout and network error. This method is useful for
118
+ * detecting that a {@code DeferredResult} instance is no longer usable.
119
+ */
120
+ public void onCompletion (Runnable callback ) {
121
+ this .completionCallback = callback ;
122
+ }
123
+
83
124
/**
84
125
* Provide a handler to use to handle the result value.
85
126
* @param resultHandler the handler
@@ -138,33 +179,29 @@ public boolean setErrorResult(Object result) {
138
179
return setResultInternal (result );
139
180
}
140
181
141
- /**
142
- * Return {@code true} if this DeferredResult is no longer usable either
143
- * because it was previously set or because the underlying request expired.
144
- * <p>
145
- * The result may have been set with a call to {@link #setResult(Object)},
146
- * or {@link #setErrorResult(Object)}, or as a result of a timeout, if a
147
- * timeout result was provided to the constructor. The request may also
148
- * expire due to a timeout or network error.
149
- */
150
- public boolean isSetOrExpired () {
151
- return ((this .result != RESULT_NONE ) || this .expired );
152
- }
182
+ DeferredResultProcessingInterceptor getInterceptor () {
183
+ return new DeferredResultProcessingInterceptorAdapter () {
153
184
154
- /**
155
- * Mark this instance expired so it may no longer be used.
156
- * @return the previous value of the expiration flag
157
- */
158
- boolean expire () {
159
- synchronized (this ) {
160
- boolean previous = this .expired ;
161
- this .expired = true ;
162
- return previous ;
163
- }
164
- }
185
+ @ Override
186
+ public <S > void afterTimeout (NativeWebRequest request , DeferredResult <S > deferredResult ) {
187
+ if (timeoutCallback != null ) {
188
+ timeoutCallback .run ();
189
+ }
190
+ if (DeferredResult .this .timeoutResult != RESULT_NONE ) {
191
+ setResultInternal (timeoutResult );
192
+ }
193
+ }
165
194
166
- boolean applyTimeoutResult () {
167
- return (this .timeoutResult != RESULT_NONE ) ? setResultInternal (this .timeoutResult ) : false ;
195
+ @ Override
196
+ public <S > void afterCompletion (NativeWebRequest request , DeferredResult <S > deferredResult ) {
197
+ synchronized (this ) {
198
+ expired = true ;
199
+ }
200
+ if (completionCallback != null ) {
201
+ completionCallback .run ();
202
+ }
203
+ }
204
+ };
168
205
}
169
206
170
207
0 commit comments