View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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;
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      @Override
89      public boolean isResponseWrapped()
90      {
91          return _responseWrapped;
92      }
93  
94      /* ------------------------------------------------------------ */
95      @Override
96      public boolean isInitial()
97      {
98          synchronized(this)
99          {
100             return _initial;
101         }
102     }
103 
104     /* ------------------------------------------------------------ */
105     @Override
106     public boolean isResumed()
107     {
108         synchronized(this)
109         {
110             return _resumed;
111         }
112     }
113 
114     /* ------------------------------------------------------------ */
115     @Override
116     public boolean isSuspended()
117     {
118         synchronized(this)
119         {
120             switch(_state)
121             {
122                 case __HANDLING:
123                     return false;
124                 case __SUSPENDING:
125                 case __RESUMING:
126                 case __COMPLETING:
127                 case __SUSPENDED:
128                     return true;
129                 case __UNSUSPENDING:
130                 default:
131                     return false;
132             }
133         }
134     }
135 
136     /* ------------------------------------------------------------ */
137     @Override
138     public boolean isExpired()
139     {
140         synchronized(this)
141         {
142             return _timeout;
143         }
144     }
145 
146     /* ------------------------------------------------------------ */
147     @Override
148     public void setTimeout(long timeoutMs)
149     {
150         _timeoutMs = timeoutMs;
151     }
152 
153     /* ------------------------------------------------------------ */
154     @Override
155     public void suspend(ServletResponse response)
156     {
157         _response=response;
158         _responseWrapped=response instanceof ServletResponseWrapper;
159         suspend();
160     }
161 
162     /* ------------------------------------------------------------ */
163     @Override
164     public void suspend()
165     {
166         synchronized (this)
167         {
168             switch(_state)
169             {
170                 case __HANDLING:
171                     _timeout=false;
172                     _resumed=false;
173                     _state=__SUSPENDING;
174                     return;
175 
176                 case __SUSPENDING:
177                 case __RESUMING:
178                     return;
179 
180                 case __COMPLETING:
181                 case __SUSPENDED:
182                 case __UNSUSPENDING:
183                     throw new IllegalStateException(this.getStatusString());
184 
185                 default:
186                     throw new IllegalStateException(""+_state);
187             }
188 
189         }
190     }
191 
192 
193     /* ------------------------------------------------------------ */
194     /* (non-Javadoc)
195      * @see org.mortbay.jetty.Suspendor#resume()
196      */
197     @Override
198     public void resume()
199     {
200         synchronized (this)
201         {
202             switch(_state)
203             {
204                 case __HANDLING:
205                     _resumed=true;
206                     return;
207 
208                 case __SUSPENDING:
209                     _resumed=true;
210                     _state=__RESUMING;
211                     return;
212 
213                 case __RESUMING:
214                 case __COMPLETING:
215                     return;
216 
217                 case __SUSPENDED:
218                     fauxResume();
219                     _resumed=true;
220                     _state=__UNSUSPENDING;
221                     break;
222 
223                 case __UNSUSPENDING:
224                     _resumed=true;
225                     return;
226 
227                 default:
228                     throw new IllegalStateException(this.getStatusString());
229             }
230         }
231 
232     }
233 
234 
235     /* ------------------------------------------------------------ */
236     @Override
237     public void complete()
238     {
239         // just like resume, except don't set _resumed=true;
240         synchronized (this)
241         {
242             switch(_state)
243             {
244                 case __HANDLING:
245                     throw new IllegalStateException(this.getStatusString());
246 
247                 case __SUSPENDING:
248                     _state=__COMPLETING;
249                     break;
250 
251                 case __RESUMING:
252                     break;
253 
254                 case __COMPLETING:
255                     return;
256 
257                 case __SUSPENDED:
258                     _state=__COMPLETING;
259                     fauxResume();
260                     break;
261 
262                 case __UNSUSPENDING:
263                     return;
264 
265                 default:
266                     throw new IllegalStateException(this.getStatusString());
267             }
268         }
269     }
270 
271     /* ------------------------------------------------------------ */
272     /**
273      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
274      */
275     @Override
276     public boolean enter(ServletResponse response)
277     {
278         _response=response;
279         return true;
280     }
281 
282     /* ------------------------------------------------------------ */
283     /**
284      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
285      */
286     @Override
287     public ServletResponse getServletResponse()
288     {
289         return _response;
290     }
291 
292 
293     /* ------------------------------------------------------------ */
294     void handling()
295     {
296         synchronized (this)
297         {
298             _responseWrapped=false;
299             switch(_state)
300             {
301                 case __HANDLING:
302                     throw new IllegalStateException(this.getStatusString());
303 
304                 case __SUSPENDING:
305                 case __RESUMING:
306                     throw new IllegalStateException(this.getStatusString());
307 
308                 case __COMPLETING:
309                     return;
310 
311                 case __SUSPENDED:
312                     fauxResume();
313                     _state=__HANDLING;
314                     return;
315                     
316                 case __UNSUSPENDING:
317                     _state=__HANDLING;
318                     return;
319 
320                 default:
321                     throw new IllegalStateException(""+_state);
322             }
323 
324         }
325     }
326 
327     /* ------------------------------------------------------------ */
328     /**
329      * @return true if handling is complete
330      */
331     @Override
332     public boolean exit()
333     {
334         synchronized (this)
335         {
336             switch(_state)
337             {
338                 case __HANDLING:
339                     _state=__COMPLETE;
340                     onComplete();
341                     return true;
342 
343                 case __SUSPENDING:
344                     _initial=false;
345                     _state=__SUSPENDED;
346                     fauxSuspend(); // could block and change state.
347                     if (_state==__SUSPENDED || _state==__COMPLETING)
348                     {
349                         onComplete();
350                         return true;
351                     }
352 
353                     _initial=false;
354                     _state=__HANDLING;
355                     return false;
356 
357                 case __RESUMING:
358                     _initial=false;
359                     _state=__HANDLING;
360                     return false;
361 
362                 case __COMPLETING:
363                     _initial=false;
364                     _state=__COMPLETE;
365                     onComplete();
366                     return true;
367 
368                 case __SUSPENDED:
369                 case __UNSUSPENDING:
370                 default:
371                     throw new IllegalStateException(this.getStatusString());
372             }
373         }
374     }
375 
376     /* ------------------------------------------------------------ */
377     protected void expire()
378     {
379         // just like resume, except don't set _resumed=true;
380 
381         synchronized (this)
382         {
383             _timeout=true;
384         }
385 
386         onTimeout();
387 
388         synchronized (this)
389         {
390             switch(_state)
391             {
392                 case __HANDLING:
393                     return;
394 
395                 case __SUSPENDING:
396                     _timeout=true;
397                     _state=__RESUMING;
398                     fauxResume();
399                     return;
400 
401                 case __RESUMING:
402                     return;
403 
404                 case __COMPLETING:
405                     return;
406 
407                 case __SUSPENDED:
408                     _timeout=true;
409                     _state=__UNSUSPENDING;
410                     break;
411 
412                 case __UNSUSPENDING:
413                     _timeout=true;
414                     return;
415 
416                 default:
417                     throw new IllegalStateException(this.getStatusString());
418             }
419         }
420     }
421 
422     private void fauxSuspend()
423     {
424         long expire_at = System.currentTimeMillis()+_timeoutMs;
425         long wait=_timeoutMs;
426         while (_timeoutMs>0 && wait>0)
427         {
428             try
429             {
430                 this.wait(wait);
431             }
432             catch (InterruptedException e)
433             {
434                 break;
435             }
436             wait=expire_at-System.currentTimeMillis();
437         }
438 
439         if (_timeoutMs>0 && wait<=0)
440             expire();
441     }
442 
443     private void fauxResume()
444     {
445         _timeoutMs=0;
446         this.notifyAll();
447     }
448 
449     @Override
450     public String toString()
451     {
452         return getStatusString();
453     }
454 
455     String getStatusString()
456     {
457         synchronized (this)
458         {
459             return
460             ((_state==__HANDLING)?"HANDLING":
461                     (_state==__SUSPENDING)?"SUSPENDING":
462                         (_state==__SUSPENDED)?"SUSPENDED":
463                             (_state==__RESUMING)?"RESUMING":
464                                 (_state==__UNSUSPENDING)?"UNSUSPENDING":
465                                     (_state==__COMPLETING)?"COMPLETING":
466                                     ("???"+_state))+
467             (_initial?",initial":"")+
468             (_resumed?",resumed":"")+
469             (_timeout?",timeout":"");
470         }
471     }
472 
473 
474     @Override
475     public void addContinuationListener(ContinuationListener listener)
476     {
477         if (_listeners==null)
478             _listeners=new ArrayList<ContinuationListener>();
479         _listeners.add(listener);
480 
481     }
482 
483     /* ------------------------------------------------------------ */
484     /**
485      * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
486      */
487     @Override
488     public Object getAttribute(String name)
489     {
490         return _request.getAttribute(name);
491     }
492 
493     /* ------------------------------------------------------------ */
494     /**
495      * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
496      */
497     @Override
498     public void removeAttribute(String name)
499     {
500         _request.removeAttribute(name);
501     }
502 
503     /* ------------------------------------------------------------ */
504     /**
505      * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
506      */
507     @Override
508     public void setAttribute(String name, Object attribute)
509     {
510         _request.setAttribute(name,attribute);
511     }
512 
513     /* ------------------------------------------------------------ */
514     /**
515      * @see org.eclipse.jetty.continuation.Continuation#undispatch()
516      */
517     @Override
518     public void undispatch()
519     {
520         if (isSuspended())
521         {
522             if (ContinuationFilter.__debug)
523                 throw new ContinuationThrowable();
524             throw __exception;
525         }
526         throw new IllegalStateException("!suspended");
527 
528     }
529 }