View Javadoc

1   // ========================================================================
2   // Copyright (c) 2007-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  package org.eclipse.jetty.server;
15  
16  import javax.servlet.ServletContext;
17  import javax.servlet.ServletRequest;
18  import javax.servlet.ServletResponse;
19  
20  import org.eclipse.jetty.continuation.ContinuationEvent;
21  import org.eclipse.jetty.continuation.ContinuationListener;
22  import org.eclipse.jetty.continuation.Continuation;
23  import org.eclipse.jetty.io.AsyncEndPoint;
24  import org.eclipse.jetty.io.EndPoint;
25  import org.eclipse.jetty.server.handler.ContextHandler;
26  import org.eclipse.jetty.server.handler.ContextHandler.Context;
27  import org.eclipse.jetty.util.LazyList;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.thread.Timeout;
30  
31  /* ------------------------------------------------------------ */
32  /** Asyncrhonous Request.
33   * 
34   */
35  public class AsyncRequest implements AsyncContext, Continuation
36  {
37      // STATES:
38      private static final int __IDLE=0;         // Idle request
39      private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
40      private static final int __SUSPENDING=2;   // Suspend called, but not yet returned to container
41      private static final int __REDISPATCHING=3;// resumed while dispatched
42      private static final int __SUSPENDED=4;    // Suspended and parked
43      private static final int __UNSUSPENDING=5; // Has been scheduled
44      private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
45      private static final int __COMPLETING=7;   // complete while dispatched
46      private static final int __UNCOMPLETED=8;  // Request is completable
47      private static final int __COMPLETE=9;     // Request is complete
48      
49      // State table
50      //                       __HANDLE      __UNHANDLE       __SUSPEND    __REDISPATCH   
51      // IDLE */          {  __DISPATCHED,    __Illegal,      __Illegal,      __Illegal  },    
52      // DISPATCHED */    {   __Illegal,  __UNCOMPLETED,   __SUSPENDING,       __Ignore  }, 
53      // SUSPENDING */    {   __Illegal,    __SUSPENDED,      __Illegal,__REDISPATCHING  },
54      // REDISPATCHING */ {   __Illegal,  _REDISPATCHED,      __Ignored,       __Ignore  },
55      // COMPLETING */    {   __Illegal,  __UNCOMPLETED,      __Illegal,       __Illegal },
56      // SUSPENDED */     {  __REDISPATCHED,  __Illegal,      __Illegal, __UNSUSPENDING  },
57      // UNSUSPENDING */  {  __REDISPATCHED,  __Illegal,      __Illegal,       __Ignore  },
58      // REDISPATCHED */  {   __Illegal,  __UNCOMPLETED,   __SUSPENDING,       __Ignore  },
59      
60  
61      /* ------------------------------------------------------------ */
62      protected HttpConnection _connection;
63      private Object _listeners;
64  
65      /* ------------------------------------------------------------ */
66      private int _state;
67      private boolean _initial;
68      private boolean _resumed;
69      private boolean _expired;
70      private boolean _keepWrappers;
71      private long _timeoutMs;
72      private AsyncEventState _event;
73  
74      /* ------------------------------------------------------------ */
75      protected AsyncRequest()
76      {
77          _state=__IDLE;
78          _initial=true;
79      }
80  
81      /* ------------------------------------------------------------ */
82      protected void setConnection(final HttpConnection connection)
83      {
84          _connection=connection;
85      }
86  
87      /* ------------------------------------------------------------ */
88      public void addContinuationListener(ContinuationListener listener)
89      {
90          _listeners=LazyList.add(_listeners,listener);
91      }
92  
93      /* ------------------------------------------------------------ */
94      public void setAsyncTimeout(long ms)
95      {
96          _timeoutMs=ms;
97      } 
98  
99      /* ------------------------------------------------------------ */
100     public long getAsyncTimeout()
101     {
102         return _timeoutMs;
103     } 
104 
105     /* ------------------------------------------------------------ */
106     public AsyncEventState getAsyncEventState()
107     {
108         return _event;
109     } 
110    
111     /* ------------------------------------------------------------ */
112     /**
113      * @see org.eclipse.jetty.continuation.Continuation#keepWrappers()
114      */
115     public void keepWrappers()
116     {
117         _keepWrappers=true;
118     }
119 
120     /* ------------------------------------------------------------ */
121     /**
122      * @see org.eclipse.jetty.continuation.Continuation#wrappersKept()
123      */
124     public boolean wrappersKept()
125     {
126         return _keepWrappers;
127     }
128 
129     /* ------------------------------------------------------------ */
130     /* (non-Javadoc)
131      * @see javax.servlet.ServletRequest#isInitial()
132      */
133     public boolean isInitial()
134     {
135         synchronized(this)
136         {
137             return _initial;
138         }
139     }
140     
141     /* ------------------------------------------------------------ */
142     /* (non-Javadoc)
143      * @see javax.servlet.ServletRequest#isSuspended()
144      */
145     public boolean isSuspended()
146     {
147         synchronized(this)
148         {
149             switch(_state)
150             {
151                 case __SUSPENDING:
152                 case __REDISPATCHING:
153                 case __COMPLETING:
154                 case __SUSPENDED:
155                     return true;
156                     
157                 default:
158                     return false;   
159             }
160         }
161     }
162 
163     /* ------------------------------------------------------------ */
164     public String toString()
165     {
166         return getStatusString();
167     }
168 
169     /* ------------------------------------------------------------ */
170     public String getStatusString()
171     {
172         synchronized (this)
173         {
174             return
175             ((_state==__IDLE)?"IDLE":
176                 (_state==__DISPATCHED)?"DISPATCHED":
177                     (_state==__SUSPENDING)?"SUSPENDING":
178                         (_state==__SUSPENDED)?"SUSPENDED":
179                             (_state==__REDISPATCHING)?"REDISPATCHING":
180                                 (_state==__UNSUSPENDING)?"UNSUSPENDING":
181                                     (_state==__REDISPATCHED)?"REDISPATCHED":
182                                         (_state==__COMPLETING)?"COMPLETING":
183                                             (_state==__UNCOMPLETED)?"UNCOMPLETED":
184                                                 (_state==__COMPLETE)?"COMPLETE":
185                                                     ("UNKNOWN?"+_state))+
186             (_initial?",initial":"");
187         }
188     }
189 
190     /* ------------------------------------------------------------ */
191     /* (non-Javadoc)
192      * @see javax.servlet.ServletRequest#resume()
193      */
194     protected boolean handling()
195     {
196         synchronized (this)
197         {
198             _keepWrappers=false;
199             
200             switch(_state)
201             {
202                 case __DISPATCHED:
203                 case __REDISPATCHED:
204                 case __COMPLETE:
205                     throw new IllegalStateException(this.getStatusString());
206 
207                 case __IDLE:
208                     _initial=true;
209                     _state=__DISPATCHED;
210                     return true;
211 
212                 case __SUSPENDING:
213                 case __REDISPATCHING:
214                     throw new IllegalStateException(this.getStatusString());
215 
216                 case __COMPLETING:
217                     _state=__UNCOMPLETED;
218                     return false;
219 
220                 case __SUSPENDED:
221                     cancelTimeout();
222                 case __UNSUSPENDING:
223                     _state=__REDISPATCHED;
224                     return true;
225 
226                 default:
227                     throw new IllegalStateException(""+_state);
228             }
229         }
230     }
231 
232     /* ------------------------------------------------------------ */
233     /* (non-Javadoc)
234      * @see javax.servlet.ServletRequest#suspend(long)
235      */
236     protected void suspend(final ServletContext context,
237             final ServletRequest request,
238             final ServletResponse response)
239     {
240         synchronized (this)
241         {
242             _resumed=false;
243             _expired=false;
244             
245             if (_event==null || request!=_event.getRequest() || response != _event.getResponse() || context != _event.getServletContext())  
246                 _event=new AsyncEventState(context,request,response);
247             else
248             {
249                 _event._dispatchContext=null;
250                 _event._path=null;
251             }
252             
253             switch(_state)
254             {
255                 case __DISPATCHED:
256                 case __REDISPATCHED:
257                     _state=__SUSPENDING;
258                     return;
259 
260                 case __IDLE:
261                     throw new IllegalStateException(this.getStatusString());
262 
263                 case __SUSPENDING:
264                 case __REDISPATCHING:
265                     return;
266 
267                 case __COMPLETING:
268                 case __SUSPENDED:
269                 case __UNSUSPENDING:
270                 case __COMPLETE:
271                     throw new IllegalStateException(this.getStatusString());
272 
273                 default:
274                     throw new IllegalStateException(""+_state);
275             }
276         }
277     }
278 
279     /* ------------------------------------------------------------ */
280     /**
281      * Signal that the HttpConnection has finished handling the request.
282      * For blocking connectors, this call may block if the request has
283      * been suspended (startAsync called).
284      * @return true if handling is complete, false if the request should 
285      * be handled again (eg because of a resume that happened before unhandle was called)
286      */
287     protected boolean unhandle()
288     {
289         synchronized (this)
290         {
291             switch(_state)
292             {
293                 case __REDISPATCHED:
294                 case __DISPATCHED:
295                     _state=__UNCOMPLETED;
296                     return true;
297 
298                 case __IDLE:
299                     throw new IllegalStateException(this.getStatusString());
300 
301                 case __SUSPENDING:
302                     _initial=false;
303                     _state=__SUSPENDED;
304                     scheduleTimeout(); // could block and change state.
305                     if (_state==__SUSPENDED)
306                         return true;
307                     else if (_state==__COMPLETING)
308                     {
309                         _state=__UNCOMPLETED;
310                         return true;
311                     }
312                     _initial=false;
313                     _state=__REDISPATCHED;
314                     return false; 
315 
316                 case __REDISPATCHING:
317                     _initial=false;
318                     _state=__REDISPATCHED;
319                     return false; 
320 
321                 case __COMPLETING:
322                     _initial=false;
323                     _state=__UNCOMPLETED;
324                     return true;
325 
326                 case __SUSPENDED:
327                 case __UNSUSPENDING:
328                 default:
329                     throw new IllegalStateException(this.getStatusString());
330             }
331         }
332     }
333 
334     /* ------------------------------------------------------------ */
335     public void dispatch()
336     {
337         boolean dispatch=false;
338         synchronized (this)
339         {
340             switch(_state)
341             {
342                 case __REDISPATCHED:
343                 case __DISPATCHED:
344                 case __IDLE:
345                 case __REDISPATCHING:
346                 case __COMPLETING:
347                 case __COMPLETE:
348                 case __UNCOMPLETED:
349                     return;
350                     
351                 case __SUSPENDING:
352                     _state=__REDISPATCHING;
353                     _resumed=true;
354                     return;
355 
356                 case __SUSPENDED:
357                     dispatch=true;
358                     _state=__UNSUSPENDING;
359                     _resumed=true;
360                     break;
361                     
362                 case __UNSUSPENDING:
363                     return;
364                     
365                 default:
366                     throw new IllegalStateException(this.getStatusString());
367             }
368         }
369         
370         if (dispatch)
371         {
372             cancelTimeout();
373             scheduleDispatch();
374         }
375     }
376 
377     /* ------------------------------------------------------------ */
378     protected void expired()
379     {
380         synchronized (this)
381         {
382             switch(_state)
383             {
384                 case __SUSPENDING:
385                 case __SUSPENDED:
386                     break;
387                 default:
388                     return;
389             }
390             _expired=true;
391         }
392         
393         if (_listeners!=null)
394         {
395             for(int i=0;i<LazyList.size(_listeners);i++)
396             {
397                 try
398                 {
399                     ContinuationListener listener=((ContinuationListener)LazyList.get(_listeners,i));
400                     listener.onTimeout(_event);
401                 }
402                 catch(Exception e)
403                 {
404                     Log.warn(e);
405                 }
406             }
407         }
408         
409         synchronized (this)
410         {
411             switch(_state)
412             {
413                 case __SUSPENDING:
414                 case __SUSPENDED:
415                     dispatch();
416                     return;
417                     
418                 default:
419                     return;
420             }
421         }
422     }
423     
424     /* ------------------------------------------------------------ */
425     /* (non-Javadoc)
426      * @see javax.servlet.ServletRequest#complete()
427      */
428     public void complete()
429     {
430         // just like resume, except don't set _resumed=true;
431         boolean dispatch=false;
432         synchronized (this)
433         {
434             switch(_state)
435             {
436                 case __IDLE:
437                 case __COMPLETE:
438                 case __REDISPATCHING:
439                 case __COMPLETING:
440                 case __UNSUSPENDING:
441                     return;
442                     
443                 case __DISPATCHED:
444                 case __REDISPATCHED:
445                     throw new IllegalStateException(this.getStatusString());
446 
447                 case __SUSPENDING:
448                     _state=__COMPLETING;
449                     return;
450                     
451                 case __SUSPENDED:
452                     _state=__COMPLETING;
453                     dispatch=true;
454                     break;
455                     
456                 default:
457                     throw new IllegalStateException(this.getStatusString());
458             }
459         }
460         
461         if (dispatch)
462         {
463             cancelTimeout();
464             scheduleDispatch();
465         }
466     }
467 
468     
469     /* ------------------------------------------------------------ */
470     /* (non-Javadoc)
471      * @see javax.servlet.ServletRequest#complete()
472      */
473     protected void doComplete()
474     {
475         synchronized (this)
476         {
477             switch(_state)
478             {
479                 case __UNCOMPLETED:
480                     _state=__COMPLETE;
481                     break;
482                     
483                 default:
484                     throw new IllegalStateException(this.getStatusString());
485             }
486         }
487 
488         if (_listeners!=null)
489         {
490             for(int i=0;i<LazyList.size(_listeners);i++)
491             {
492                 try
493                 {
494                     ((ContinuationListener)LazyList.get(_listeners,i)).onComplete(_event);
495                 }
496                 catch(Exception e)
497                 {
498                     Log.warn(e);
499                 }
500             }
501         }
502     }
503 
504     /* ------------------------------------------------------------ */
505     protected void recycle()
506     {
507         synchronized (this)
508         {
509             switch(_state)
510             {
511                 case __DISPATCHED:
512                 case __REDISPATCHED:
513                     throw new IllegalStateException(getStatusString());
514                 default:
515                     _state=__IDLE;
516             }
517             _initial = true;
518             _resumed=false;
519             _expired=false;
520             _keepWrappers=false;
521             cancelTimeout();
522             _timeoutMs=60000L; // TODO configure
523             _listeners=null;
524         }
525     }    
526     
527     /* ------------------------------------------------------------ */
528     public void cancel()
529     {
530         synchronized (this)
531         {
532             _state=__COMPLETE;
533             _initial = false;
534             cancelTimeout();
535             _listeners=null;
536         }
537     }
538 
539     /* ------------------------------------------------------------ */
540     protected void scheduleDispatch()
541     {
542         EndPoint endp=_connection.getEndPoint();
543         if (!endp.isBlocking())
544         {
545             ((AsyncEndPoint)endp).dispatch();
546         }
547     }
548 
549     /* ------------------------------------------------------------ */
550     protected void scheduleTimeout()
551     {
552         EndPoint endp=_connection.getEndPoint();
553         if (endp.isBlocking())
554         {
555             synchronized(this)
556             {
557                 long expire_at = System.currentTimeMillis()+_timeoutMs;
558                 long wait=_timeoutMs;
559                 while (_timeoutMs>0 && wait>0)
560                 {
561                     try
562                     {
563                         this.wait(wait);
564                     }
565                     catch (InterruptedException e)
566                     {
567                         Log.ignore(e);
568                     }
569                     wait=expire_at-System.currentTimeMillis();
570                 }
571 
572                 if (_timeoutMs>0 && wait<=0)
573                     expired();
574             }            
575         }
576         else
577             _connection.scheduleTimeout(_event._timeout,_timeoutMs);
578     }
579 
580     /* ------------------------------------------------------------ */
581     protected void cancelTimeout()
582     {
583         EndPoint endp=_connection.getEndPoint();
584         if (endp.isBlocking())
585         {
586             synchronized(this)
587             {
588                 _timeoutMs=0;
589                 this.notifyAll();
590             }
591         }
592         else 
593         {
594             final AsyncEventState event=_event;
595             if (event!=null)
596                 _connection.cancelTimeout(event._timeout);
597         }
598     }
599 
600     /* ------------------------------------------------------------ */
601     public boolean isCompleting()
602     {
603         return _state==__COMPLETING;
604     }
605     
606     /* ------------------------------------------------------------ */
607     boolean isUncompleted()
608     {
609         return _state==__UNCOMPLETED;
610     } 
611     
612     /* ------------------------------------------------------------ */
613     public boolean isComplete()
614     {
615         return _state==__COMPLETE;
616     }
617 
618 
619     /* ------------------------------------------------------------ */
620     public boolean isAsyncStarted()
621     {
622         switch(_state)
623         {
624             case __SUSPENDING:
625             case __REDISPATCHING:
626             case __UNSUSPENDING:
627             case __SUSPENDED:
628                 return true;
629                 
630             default:
631             return false;
632         }
633     }
634 
635 
636     /* ------------------------------------------------------------ */
637     public boolean isAsync()
638     {
639         switch(_state)
640         {
641             case __IDLE:
642             case __DISPATCHED:
643                 return false;
644                 
645             default:
646             return true;
647         }
648     }
649 
650     /* ------------------------------------------------------------ */
651     public void dispatch(ServletContext context, String path)
652     {
653         _event._dispatchContext=context;
654         _event._path=path;
655         dispatch();
656     }
657 
658     /* ------------------------------------------------------------ */
659     public void dispatch(String path)
660     {
661         _event._path=path;
662         dispatch();
663     }
664 
665     /* ------------------------------------------------------------ */
666     public ServletRequest getRequest()
667     {
668         if (_event!=null)
669             return _event.getRequest();
670         return _connection.getRequest();
671     }
672 
673     /* ------------------------------------------------------------ */
674     public ServletResponse getResponse()
675     {
676         if (_event!=null)
677             return _event.getResponse();
678         return _connection.getResponse();
679     }
680 
681     /* ------------------------------------------------------------ */
682     public void start(Runnable run)
683     {
684         ((Context)_event.getServletContext()).getContextHandler().handle(run);
685     }
686 
687     /* ------------------------------------------------------------ */
688     public boolean hasOriginalRequestAndResponse()
689     {
690         return (_event!=null && _event.getRequest()==_connection._request && _event.getResponse()==_connection._response);
691     }
692 
693     /* ------------------------------------------------------------ */
694     public ContextHandler getContextHandler()
695     {
696         if (_event!=null)
697             return ((Context)_event.getServletContext()).getContextHandler();
698         return null;
699     }
700 
701 
702     /* ------------------------------------------------------------ */
703     /**
704      * @see Continuation#isResumed()
705      */
706     public boolean isResumed()
707     {
708         return _resumed;
709     }
710     /* ------------------------------------------------------------ */
711     /**
712      * @see Continuation#isExpired()
713      */
714     public boolean isExpired()
715     {
716         return _expired;
717     }
718 
719     /* ------------------------------------------------------------ */
720     /**
721      * @see Continuation#resume()
722      */
723     public void resume()
724     {
725         dispatch();
726     }
727 
728     /* ------------------------------------------------------------ */
729     /**
730      * @see Continuation#suspend(long)
731      */
732     public void setTimeout(long timeoutMs)
733     {
734         setAsyncTimeout(timeoutMs);
735     }
736 
737     /* ------------------------------------------------------------ */
738     /**
739      * @see Continuation#suspend()
740      */
741     public void suspend()
742     {
743         // TODO simplify?
744         AsyncRequest.this.suspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());       
745     }
746 
747     /* ------------------------------------------------------------ */
748     /**
749      * @see org.eclipse.jetty.continuation.Continuation#getServletRequest()
750      */
751     public ServletRequest getServletRequest()
752     {
753         return _connection.getRequest();
754     }
755 
756     /* ------------------------------------------------------------ */
757     /**
758      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
759      */
760     public ServletResponse getServletResponse()
761     {
762         return _connection.getResponse();
763     }
764 
765     /* ------------------------------------------------------------ */
766     /* ------------------------------------------------------------ */
767     public class AsyncEventState implements ContinuationEvent
768     {
769         private final ServletContext _suspendedContext;
770         private final ServletRequest _request;
771         private final ServletResponse _response;
772         
773         ServletContext _dispatchContext;
774         
775         String _path;
776         final Timeout.Task _timeout = new Timeout.Task()
777         {
778             public void expired()
779             {
780                 AsyncRequest.this.expired();
781             }
782         };
783         
784         public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
785         {
786             _suspendedContext=context;
787             _request=request;
788             _response=response;
789         }
790         
791         public ServletContext getSuspendedContext()
792         {
793             return _suspendedContext;
794         }
795         
796         public ServletContext getDispatchContext()
797         {
798             return _dispatchContext;
799         }
800         
801         public ServletContext getServletContext()
802         {
803             return _dispatchContext==null?_suspendedContext:_dispatchContext;
804         }
805 
806         public ServletRequest getRequest()
807         {
808             return _request;
809         }
810 
811         public ServletResponse getResponse()
812         {
813             return _response;
814         }
815         
816         public String getPath()
817         {
818             return _path;
819         }   
820     }
821 }