View Javadoc

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 }