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     /**
257      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
258      */
259     public ServletResponse getServletResponse()
260     {
261         return _response;
262     }
263     
264 
265     /* ------------------------------------------------------------ */
266     void handling()
267     {
268         synchronized (this)
269         {
270             _responseWrapped=false;
271             switch(_state)
272             {
273                 case __HANDLING:
274                     throw new IllegalStateException(this.getStatusString());
275 
276                 case __SUSPENDING:
277                 case __RESUMING:
278                     throw new IllegalStateException(this.getStatusString());
279 
280                 case __COMPLETING:
281                     return;
282 
283                 case __SUSPENDED:
284                     fauxResume();
285                 case __UNSUSPENDING:
286                     _state=__HANDLING;
287                     return;
288 
289                 default:
290                     throw new IllegalStateException(""+_state);
291             }
292 
293         }
294     }
295 
296     /* ------------------------------------------------------------ */
297     /**
298      * @return true if handling is complete
299      */
300     public boolean handleSuspension()
301     {
302         synchronized (this)
303         {
304             switch(_state)
305             {
306                 case __HANDLING:
307                     _state=__COMPLETE;
308                     return true;
309 
310                 case __SUSPENDING:
311                     _initial=false;
312                     _state=__SUSPENDED;
313                     fauxSuspend(); // could block and change state.
314                     if (_state==__SUSPENDED || _state==__COMPLETING)
315                         return true;
316                     
317                     _initial=false;
318                     _state=__HANDLING;
319                     return false; 
320 
321                 case __RESUMING:
322                     _initial=false;
323                     _state=__HANDLING;
324                     return false; 
325 
326                 case __COMPLETING:
327                     _initial=false;
328                     _state=__COMPLETE;
329                     return true;
330 
331                 case __SUSPENDED:
332                 case __UNSUSPENDING:
333                 default:
334                     throw new IllegalStateException(this.getStatusString());
335             }
336         }
337     }
338 
339     /* ------------------------------------------------------------ */
340     protected void expire()
341     {
342         // just like resume, except don't set _resumed=true;
343 
344         synchronized (this)
345         {
346             _timeout=true;
347         }
348         
349         onTimeout();
350         
351         synchronized (this)
352         {
353             switch(_state)
354             {
355                 case __HANDLING:
356                     return;
357                     
358                 case __SUSPENDING:
359                     _timeout=true;
360                     _state=__RESUMING;
361                     fauxResume();
362                     return;
363                     
364                 case __RESUMING:
365                     return;
366                     
367                 case __COMPLETING:
368                     return;
369                     
370                 case __SUSPENDED:
371                     _timeout=true;
372                     _state=__UNSUSPENDING;
373                     break;
374                     
375                 case __UNSUSPENDING:
376                     _timeout=true;
377                     return;
378                     
379                 default:
380                     throw new IllegalStateException(this.getStatusString());
381             }
382         }
383     }
384 
385     private void fauxSuspend()
386     {
387         long expire_at = System.currentTimeMillis()+_timeoutMs;
388         long wait=_timeoutMs;
389         while (_timeoutMs>0 && wait>0)
390         {
391             try
392             {
393                 this.wait(wait);
394             }
395             catch (InterruptedException e)
396             {
397                 break;
398             }
399             wait=expire_at-System.currentTimeMillis();
400         }
401 
402         if (_timeoutMs>0 && wait<=0)
403             expire();
404     }
405     
406     private void fauxResume()
407     {
408         _timeoutMs=0;
409         this.notifyAll();
410     }
411     
412     public String toString()
413     {
414         return getStatusString();
415     }
416     
417     String getStatusString()
418     {
419         synchronized (this)
420         {
421             return
422             ((_state==__HANDLING)?"HANDLING":
423                     (_state==__SUSPENDING)?"SUSPENDING":
424                         (_state==__SUSPENDED)?"SUSPENDED":
425                             (_state==__RESUMING)?"RESUMING":
426                                 (_state==__UNSUSPENDING)?"UNSUSPENDING":
427                                     (_state==__COMPLETING)?"COMPLETING":
428                                     ("???"+_state))+
429             (_initial?",initial":"")+
430             (_resumed?",resumed":"")+
431             (_timeout?",timeout":"");
432         }
433     }
434 
435     
436     public void addContinuationListener(ContinuationListener listener)
437     {
438         if (_listeners==null)
439             _listeners=new ArrayList<ContinuationListener>();
440         _listeners.add(listener);
441         
442     }
443 
444     /* ------------------------------------------------------------ */
445     /**
446      * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
447      */
448     public Object getAttribute(String name)
449     {
450         return _request.getAttribute(name);
451     }
452 
453     /* ------------------------------------------------------------ */
454     /**
455      * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
456      */
457     public void removeAttribute(String name)
458     {
459         _request.removeAttribute(name);
460     }
461 
462     /* ------------------------------------------------------------ */
463     /**
464      * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
465      */
466     public void setAttribute(String name, Object attribute)
467     {
468         _request.setAttribute(name,attribute);
469     }
470 
471     /* ------------------------------------------------------------ */
472     /**
473      * @see org.eclipse.jetty.continuation.Continuation#undispatch()
474      */
475     public void undispatch()
476     {
477         if (isSuspended())
478         {
479             if (ContinuationFilter.__debug)
480                 throw new ContinuationThrowable();
481             throw __exception;
482         }
483         throw new IllegalStateException("!suspended");
484         
485     }
486 }