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