1 // ========================================================================
2 // Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14
15 package org.eclipse.jetty.continuation;
16
17 import java.util.ArrayList;
18
19 import javax.servlet.ServletRequest;
20 import javax.servlet.ServletResponse;
21 import javax.servlet.ServletResponseWrapper;
22
23
24 /* ------------------------------------------------------------ */
25 /**
26 * A blocking implementation of Continuation.
27 * This implementation of Continuation is used by the {@link ContinuationFilter}
28 * when there are is no native or asynchronous continuation type available.
29 */
30 class FauxContinuation implements Continuation
31 {
32 // common exception used for all continuations.
33 // Turn on debug in ContinuationFilter to see real stack trace.
34 private final static ContinuationThrowable __exception = new ContinuationThrowable();
35
36 private static final int __HANDLING=1; // Request dispatched to filter/servlet
37 private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container
38 private static final int __RESUMING=3; // resumed while suspending
39 private static final int __COMPLETING=4; // resumed while suspending or suspended
40 private static final int __SUSPENDED=5; // Suspended and parked
41 private static final int __UNSUSPENDING=6;
42 private static final int __COMPLETE=7;
43
44 private final ServletRequest _request;
45 private ServletResponse _response;
46
47 private int _state=__HANDLING;
48 private boolean _initial=true;
49 private boolean _resumed=false;
50 private boolean _timeout=false;
51 private boolean _responseWrapped=false;
52 private long _timeoutMs=30000; // TODO configure
53
54 private ArrayList<ContinuationListener> _listeners;
55
56 FauxContinuation(final ServletRequest request)
57 {
58 _request=request;
59 }
60
61 /* ------------------------------------------------------------ */
62 public void onComplete()
63 {
64 if (_listeners!=null)
65 for (ContinuationListener l:_listeners)
66 l.onComplete(this);
67 }
68
69 /* ------------------------------------------------------------ */
70 public void onTimeout()
71 {
72 if (_listeners!=null)
73 for (ContinuationListener l:_listeners)
74 l.onTimeout(this);
75 }
76
77 /* ------------------------------------------------------------ */
78 /**
79 * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped()
80 */
81 public boolean isResponseWrapped()
82 {
83 return _responseWrapped;
84 }
85
86 /* ------------------------------------------------------------ */
87 public boolean isInitial()
88 {
89 synchronized(this)
90 {
91 return _initial;
92 }
93 }
94
95 /* ------------------------------------------------------------ */
96 public boolean isResumed()
97 {
98 synchronized(this)
99 {
100 return _resumed;
101 }
102 }
103
104 /* ------------------------------------------------------------ */
105 public boolean isSuspended()
106 {
107 synchronized(this)
108 {
109 switch(_state)
110 {
111 case __HANDLING:
112 return false;
113 case __SUSPENDING:
114 case __RESUMING:
115 case __COMPLETING:
116 case __SUSPENDED:
117 return true;
118 case __UNSUSPENDING:
119 default:
120 return false;
121 }
122 }
123 }
124
125 /* ------------------------------------------------------------ */
126 public boolean isExpired()
127 {
128 synchronized(this)
129 {
130 return _timeout;
131 }
132 }
133
134 /* ------------------------------------------------------------ */
135 public void setTimeout(long timeoutMs)
136 {
137 _timeoutMs = timeoutMs;
138 }
139
140 /* ------------------------------------------------------------ */
141 public void suspend(ServletResponse response)
142 {
143 _response=response;
144 _responseWrapped=response instanceof ServletResponseWrapper;
145 suspend();
146 }
147
148 /* ------------------------------------------------------------ */
149 public void suspend()
150 {
151 synchronized (this)
152 {
153 switch(_state)
154 {
155 case __HANDLING:
156 _timeout=false;
157 _resumed=false;
158 _state=__SUSPENDING;
159 return;
160
161 case __SUSPENDING:
162 case __RESUMING:
163 return;
164
165 case __COMPLETING:
166 case __SUSPENDED:
167 case __UNSUSPENDING:
168 throw new IllegalStateException(this.getStatusString());
169
170 default:
171 throw new IllegalStateException(""+_state);
172 }
173
174 }
175 }
176
177
178 /* ------------------------------------------------------------ */
179 /* (non-Javadoc)
180 * @see org.mortbay.jetty.Suspendor#resume()
181 */
182 public void resume()
183 {
184 synchronized (this)
185 {
186 switch(_state)
187 {
188 case __HANDLING:
189 _resumed=true;
190 return;
191
192 case __SUSPENDING:
193 _resumed=true;
194 _state=__RESUMING;
195 return;
196
197 case __RESUMING:
198 case __COMPLETING:
199 return;
200
201 case __SUSPENDED:
202 fauxResume();
203 _resumed=true;
204 _state=__UNSUSPENDING;
205 break;
206
207 case __UNSUSPENDING:
208 _resumed=true;
209 return;
210
211 default:
212 throw new IllegalStateException(this.getStatusString());
213 }
214 }
215
216 }
217
218
219 /* ------------------------------------------------------------ */
220 public void complete()
221 {
222 // just like resume, except don't set _resumed=true;
223 synchronized (this)
224 {
225 switch(_state)
226 {
227 case __HANDLING:
228 throw new IllegalStateException(this.getStatusString());
229
230 case __SUSPENDING:
231 _state=__COMPLETING;
232 break;
233
234 case __RESUMING:
235 break;
236
237 case __COMPLETING:
238 return;
239
240 case __SUSPENDED:
241 _state=__COMPLETING;
242 fauxResume();
243 break;
244
245 case __UNSUSPENDING:
246 return;
247
248 default:
249 throw new IllegalStateException(this.getStatusString());
250 }
251 }
252 }
253
254 /* ------------------------------------------------------------ */
255 /**
256 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
257 */
258 void setServletResponse(ServletResponse response)
259 {
260 _response=response;
261 }
262
263 /* ------------------------------------------------------------ */
264 /**
265 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
266 */
267 public ServletResponse getServletResponse()
268 {
269 return _response;
270 }
271
272
273 /* ------------------------------------------------------------ */
274 void handling()
275 {
276 synchronized (this)
277 {
278 _responseWrapped=false;
279 switch(_state)
280 {
281 case __HANDLING:
282 throw new IllegalStateException(this.getStatusString());
283
284 case __SUSPENDING:
285 case __RESUMING:
286 throw new IllegalStateException(this.getStatusString());
287
288 case __COMPLETING:
289 return;
290
291 case __SUSPENDED:
292 fauxResume();
293 case __UNSUSPENDING:
294 _state=__HANDLING;
295 return;
296
297 default:
298 throw new IllegalStateException(""+_state);
299 }
300
301 }
302 }
303
304 /* ------------------------------------------------------------ */
305 /**
306 * @return true if handling is complete
307 */
308 public boolean handleSuspension()
309 {
310 synchronized (this)
311 {
312 switch(_state)
313 {
314 case __HANDLING:
315 _state=__COMPLETE;
316 return true;
317
318 case __SUSPENDING:
319 _initial=false;
320 _state=__SUSPENDED;
321 fauxSuspend(); // could block and change state.
322 if (_state==__SUSPENDED || _state==__COMPLETING)
323 return true;
324
325 _initial=false;
326 _state=__HANDLING;
327 return false;
328
329 case __RESUMING:
330 _initial=false;
331 _state=__HANDLING;
332 return false;
333
334 case __COMPLETING:
335 _initial=false;
336 _state=__COMPLETE;
337 return true;
338
339 case __SUSPENDED:
340 case __UNSUSPENDING:
341 default:
342 throw new IllegalStateException(this.getStatusString());
343 }
344 }
345 }
346
347 /* ------------------------------------------------------------ */
348 protected void expire()
349 {
350 // just like resume, except don't set _resumed=true;
351
352 synchronized (this)
353 {
354 _timeout=true;
355 }
356
357 onTimeout();
358
359 synchronized (this)
360 {
361 switch(_state)
362 {
363 case __HANDLING:
364 return;
365
366 case __SUSPENDING:
367 _timeout=true;
368 _state=__RESUMING;
369 fauxResume();
370 return;
371
372 case __RESUMING:
373 return;
374
375 case __COMPLETING:
376 return;
377
378 case __SUSPENDED:
379 _timeout=true;
380 _state=__UNSUSPENDING;
381 break;
382
383 case __UNSUSPENDING:
384 _timeout=true;
385 return;
386
387 default:
388 throw new IllegalStateException(this.getStatusString());
389 }
390 }
391 }
392
393 private void fauxSuspend()
394 {
395 long expire_at = System.currentTimeMillis()+_timeoutMs;
396 long wait=_timeoutMs;
397 while (_timeoutMs>0 && wait>0)
398 {
399 try
400 {
401 this.wait(wait);
402 }
403 catch (InterruptedException e)
404 {
405 break;
406 }
407 wait=expire_at-System.currentTimeMillis();
408 }
409
410 if (_timeoutMs>0 && wait<=0)
411 expire();
412 }
413
414 private void fauxResume()
415 {
416 _timeoutMs=0;
417 this.notifyAll();
418 }
419
420 public String toString()
421 {
422 return getStatusString();
423 }
424
425 String getStatusString()
426 {
427 synchronized (this)
428 {
429 return
430 ((_state==__HANDLING)?"HANDLING":
431 (_state==__SUSPENDING)?"SUSPENDING":
432 (_state==__SUSPENDED)?"SUSPENDED":
433 (_state==__RESUMING)?"RESUMING":
434 (_state==__UNSUSPENDING)?"UNSUSPENDING":
435 (_state==__COMPLETING)?"COMPLETING":
436 ("???"+_state))+
437 (_initial?",initial":"")+
438 (_resumed?",resumed":"")+
439 (_timeout?",timeout":"");
440 }
441 }
442
443
444 public void addContinuationListener(ContinuationListener listener)
445 {
446 if (_listeners==null)
447 _listeners=new ArrayList<ContinuationListener>();
448 _listeners.add(listener);
449
450 }
451
452 /* ------------------------------------------------------------ */
453 /**
454 * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
455 */
456 public Object getAttribute(String name)
457 {
458 return _request.getAttribute(name);
459 }
460
461 /* ------------------------------------------------------------ */
462 /**
463 * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
464 */
465 public void removeAttribute(String name)
466 {
467 _request.removeAttribute(name);
468 }
469
470 /* ------------------------------------------------------------ */
471 /**
472 * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
473 */
474 public void setAttribute(String name, Object attribute)
475 {
476 _request.setAttribute(name,attribute);
477 }
478
479 /* ------------------------------------------------------------ */
480 /**
481 * @see org.eclipse.jetty.continuation.Continuation#undispatch()
482 */
483 public void undispatch()
484 {
485 if (isSuspended())
486 {
487 if (ContinuationFilter.__debug)
488 throw new ContinuationThrowable();
489 throw __exception;
490 }
491 throw new IllegalStateException("!suspended");
492
493 }
494 }