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