View Javadoc

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