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