Home | History | Annotate | Download | only in server
      1 //
      2 //  ========================================================================
      3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
      4 //  ------------------------------------------------------------------------
      5 //  All rights reserved. This program and the accompanying materials
      6 //  are made available under the terms of the Eclipse Public License v1.0
      7 //  and Apache License v2.0 which accompanies this distribution.
      8 //
      9 //      The Eclipse Public License is available at
     10 //      http://www.eclipse.org/legal/epl-v10.html
     11 //
     12 //      The Apache License v2.0 is available at
     13 //      http://www.opensource.org/licenses/apache2.0.php
     14 //
     15 //  You may elect to redistribute this code under either of these licenses.
     16 //  ========================================================================
     17 //
     18 
     19 package org.eclipse.jetty.server;
     20 
     21 import javax.servlet.AsyncContext;
     22 import javax.servlet.AsyncEvent;
     23 import javax.servlet.AsyncListener;
     24 import javax.servlet.RequestDispatcher;
     25 import javax.servlet.ServletException;
     26 
     27 import java.util.ArrayList;
     28 import java.util.List;
     29 
     30 import javax.servlet.ServletContext;
     31 import javax.servlet.ServletRequest;
     32 import javax.servlet.ServletResponse;
     33 import javax.servlet.http.HttpServletRequest;
     34 
     35 import org.eclipse.jetty.continuation.Continuation;
     36 import org.eclipse.jetty.continuation.ContinuationThrowable;
     37 import org.eclipse.jetty.continuation.ContinuationListener;
     38 import org.eclipse.jetty.io.AsyncEndPoint;
     39 import org.eclipse.jetty.io.EndPoint;
     40 import org.eclipse.jetty.server.handler.ContextHandler;
     41 import org.eclipse.jetty.server.handler.ContextHandler.Context;
     42 import org.eclipse.jetty.util.URIUtil;
     43 import org.eclipse.jetty.util.log.Log;
     44 import org.eclipse.jetty.util.log.Logger;
     45 import org.eclipse.jetty.util.thread.Timeout;
     46 
     47 /* ------------------------------------------------------------ */
     48 /** Implementation of Continuation and AsyncContext interfaces
     49  *
     50  */
     51 public class AsyncContinuation implements AsyncContext, Continuation
     52 {
     53     private static final Logger LOG = Log.getLogger(AsyncContinuation.class);
     54 
     55     private final static long DEFAULT_TIMEOUT=30000L;
     56 
     57     private final static ContinuationThrowable __exception = new ContinuationThrowable();
     58 
     59     // STATES:
     60     //               handling()    suspend()     unhandle()    resume()       complete()  doComplete()
     61     //                             startAsync()                dispatch()
     62     // IDLE          DISPATCHED
     63     // DISPATCHED                  ASYNCSTARTED  UNCOMPLETED
     64     // ASYNCSTARTED                              ASYNCWAIT     REDISPATCHING  COMPLETING
     65     // REDISPATCHING                             REDISPATCHED
     66     // ASYNCWAIT                                               REDISPATCH     COMPLETING
     67     // REDISPATCH    REDISPATCHED
     68     // REDISPATCHED                ASYNCSTARTED  UNCOMPLETED
     69     // COMPLETING    UNCOMPLETED                 UNCOMPLETED
     70     // UNCOMPLETED                                                                        COMPLETED
     71     // COMPLETED
     72     private static final int __IDLE=0;         // Idle request
     73     private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
     74     private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container
     75     private static final int __REDISPATCHING=3;// resumed while dispatched
     76     private static final int __ASYNCWAIT=4;    // Suspended and parked
     77     private static final int __REDISPATCH=5;   // Has been scheduled
     78     private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
     79     private static final int __COMPLETING=7;   // complete while dispatched
     80     private static final int __UNCOMPLETED=8;  // Request is completable
     81     private static final int __COMPLETED=9;    // Request is complete
     82 
     83     /* ------------------------------------------------------------ */
     84     protected AbstractHttpConnection _connection;
     85     private List<AsyncListener> _lastAsyncListeners;
     86     private List<AsyncListener> _asyncListeners;
     87     private List<ContinuationListener> _continuationListeners;
     88 
     89     /* ------------------------------------------------------------ */
     90     private int _state;
     91     private boolean _initial;
     92     private boolean _resumed;
     93     private boolean _expired;
     94     private volatile boolean _responseWrapped;
     95     private long _timeoutMs=DEFAULT_TIMEOUT;
     96     private AsyncEventState _event;
     97     private volatile long _expireAt;
     98     private volatile boolean _continuation;
     99 
    100     /* ------------------------------------------------------------ */
    101     protected AsyncContinuation()
    102     {
    103         _state=__IDLE;
    104         _initial=true;
    105     }
    106 
    107     /* ------------------------------------------------------------ */
    108     protected void setConnection(final AbstractHttpConnection connection)
    109     {
    110         synchronized(this)
    111         {
    112             _connection=connection;
    113         }
    114     }
    115 
    116     /* ------------------------------------------------------------ */
    117     public void addListener(AsyncListener listener)
    118     {
    119         synchronized(this)
    120         {
    121             if (_asyncListeners==null)
    122                 _asyncListeners=new ArrayList<AsyncListener>();
    123             _asyncListeners.add(listener);
    124         }
    125     }
    126 
    127     /* ------------------------------------------------------------ */
    128     public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
    129     {
    130         synchronized(this)
    131         {
    132             // TODO handle the request/response ???
    133             if (_asyncListeners==null)
    134                 _asyncListeners=new ArrayList<AsyncListener>();
    135             _asyncListeners.add(listener);
    136         }
    137     }
    138 
    139     /* ------------------------------------------------------------ */
    140     public void addContinuationListener(ContinuationListener listener)
    141     {
    142         synchronized(this)
    143         {
    144             if (_continuationListeners==null)
    145                 _continuationListeners=new ArrayList<ContinuationListener>();
    146             _continuationListeners.add(listener);
    147         }
    148     }
    149 
    150     /* ------------------------------------------------------------ */
    151     public void setTimeout(long ms)
    152     {
    153         synchronized(this)
    154         {
    155             _timeoutMs=ms;
    156         }
    157     }
    158 
    159     /* ------------------------------------------------------------ */
    160     public long getTimeout()
    161     {
    162         synchronized(this)
    163         {
    164             return _timeoutMs;
    165         }
    166     }
    167 
    168     /* ------------------------------------------------------------ */
    169     public AsyncEventState getAsyncEventState()
    170     {
    171         synchronized(this)
    172         {
    173             return _event;
    174         }
    175     }
    176 
    177     /* ------------------------------------------------------------ */
    178     /**
    179      * @see org.eclipse.jetty.continuation.Continuation#keepWrappers()
    180      */
    181 
    182     /* ------------------------------------------------------------ */
    183     /**
    184      * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped()
    185      */
    186     public boolean isResponseWrapped()
    187     {
    188         return _responseWrapped;
    189     }
    190 
    191     /* ------------------------------------------------------------ */
    192     /* (non-Javadoc)
    193      * @see javax.servlet.ServletRequest#isInitial()
    194      */
    195     public boolean isInitial()
    196     {
    197         synchronized(this)
    198         {
    199             return _initial;
    200         }
    201     }
    202 
    203     public boolean isContinuation()
    204     {
    205         return _continuation;
    206     }
    207 
    208     /* ------------------------------------------------------------ */
    209     /* (non-Javadoc)
    210      * @see javax.servlet.ServletRequest#isSuspended()
    211      */
    212     public boolean isSuspended()
    213     {
    214         synchronized(this)
    215         {
    216             switch(_state)
    217             {
    218                 case __ASYNCSTARTED:
    219                 case __REDISPATCHING:
    220                 case __COMPLETING:
    221                 case __ASYNCWAIT:
    222                     return true;
    223 
    224                 default:
    225                     return false;
    226             }
    227         }
    228     }
    229 
    230     /* ------------------------------------------------------------ */
    231     public boolean isSuspending()
    232     {
    233         synchronized(this)
    234         {
    235             switch(_state)
    236             {
    237                 case __ASYNCSTARTED:
    238                 case __ASYNCWAIT:
    239                     return true;
    240 
    241                 default:
    242                     return false;
    243             }
    244         }
    245     }
    246 
    247     /* ------------------------------------------------------------ */
    248     public boolean isDispatchable()
    249     {
    250         synchronized(this)
    251         {
    252             switch(_state)
    253             {
    254                 case __REDISPATCH:
    255                 case __REDISPATCHED:
    256                 case __REDISPATCHING:
    257                 case __COMPLETING:
    258                     return true;
    259 
    260                 default:
    261                     return false;
    262             }
    263         }
    264     }
    265 
    266     /* ------------------------------------------------------------ */
    267     @Override
    268     public String toString()
    269     {
    270         synchronized (this)
    271         {
    272             return super.toString()+"@"+getStatusString();
    273         }
    274     }
    275 
    276     /* ------------------------------------------------------------ */
    277     public String getStatusString()
    278     {
    279         synchronized (this)
    280         {
    281             return
    282             ((_state==__IDLE)?"IDLE":
    283                 (_state==__DISPATCHED)?"DISPATCHED":
    284                     (_state==__ASYNCSTARTED)?"ASYNCSTARTED":
    285                         (_state==__ASYNCWAIT)?"ASYNCWAIT":
    286                             (_state==__REDISPATCHING)?"REDISPATCHING":
    287                                 (_state==__REDISPATCH)?"REDISPATCH":
    288                                     (_state==__REDISPATCHED)?"REDISPATCHED":
    289                                         (_state==__COMPLETING)?"COMPLETING":
    290                                             (_state==__UNCOMPLETED)?"UNCOMPLETED":
    291                                                 (_state==__COMPLETED)?"COMPLETE":
    292                                                     ("UNKNOWN?"+_state))+
    293             (_initial?",initial":"")+
    294             (_resumed?",resumed":"")+
    295             (_expired?",expired":"");
    296         }
    297     }
    298 
    299     /* ------------------------------------------------------------ */
    300     /**
    301      * @return false if the handling of the request should not proceed
    302      */
    303     protected boolean handling()
    304     {
    305         synchronized (this)
    306         {
    307             _continuation=false;
    308 
    309             switch(_state)
    310             {
    311                 case __IDLE:
    312                     _initial=true;
    313                     _state=__DISPATCHED;
    314                     if (_lastAsyncListeners!=null)
    315                         _lastAsyncListeners.clear();
    316                     if (_asyncListeners!=null)
    317                         _asyncListeners.clear();
    318                     else
    319                     {
    320                         _asyncListeners=_lastAsyncListeners;
    321                         _lastAsyncListeners=null;
    322                     }
    323                     return true;
    324 
    325                 case __COMPLETING:
    326                     _state=__UNCOMPLETED;
    327                     return false;
    328 
    329                 case __ASYNCWAIT:
    330                     return false;
    331 
    332                 case __REDISPATCH:
    333                     _state=__REDISPATCHED;
    334                     return true;
    335 
    336                 default:
    337                     throw new IllegalStateException(this.getStatusString());
    338             }
    339         }
    340     }
    341 
    342     /* ------------------------------------------------------------ */
    343     /* (non-Javadoc)
    344      * @see javax.servlet.ServletRequest#suspend(long)
    345      */
    346     private void doSuspend(final ServletContext context,
    347             final ServletRequest request,
    348             final ServletResponse response)
    349     {
    350         synchronized (this)
    351         {
    352             switch(_state)
    353             {
    354                 case __DISPATCHED:
    355                 case __REDISPATCHED:
    356                     _resumed=false;
    357                     _expired=false;
    358 
    359                     if (_event==null || request!=_event.getSuppliedRequest() || response != _event.getSuppliedResponse() || context != _event.getServletContext())
    360                         _event=new AsyncEventState(context,request,response);
    361                     else
    362                     {
    363                         _event._dispatchContext=null;
    364                         _event._pathInContext=null;
    365                     }
    366                     _state=__ASYNCSTARTED;
    367                     List<AsyncListener> recycle=_lastAsyncListeners;
    368                     _lastAsyncListeners=_asyncListeners;
    369                     _asyncListeners=recycle;
    370                     if (_asyncListeners!=null)
    371                         _asyncListeners.clear();
    372                     break;
    373 
    374                 default:
    375                     throw new IllegalStateException(this.getStatusString());
    376             }
    377         }
    378 
    379         if (_lastAsyncListeners!=null)
    380         {
    381             for (AsyncListener listener : _lastAsyncListeners)
    382             {
    383                 try
    384                 {
    385                     listener.onStartAsync(_event);
    386                 }
    387                 catch(Exception e)
    388                 {
    389                     LOG.warn(e);
    390                 }
    391             }
    392         }
    393     }
    394 
    395     /* ------------------------------------------------------------ */
    396     /**
    397      * Signal that the HttpConnection has finished handling the request.
    398      * For blocking connectors, this call may block if the request has
    399      * been suspended (startAsync called).
    400      * @return true if handling is complete, false if the request should
    401      * be handled again (eg because of a resume that happened before unhandle was called)
    402      */
    403     protected boolean unhandle()
    404     {
    405         synchronized (this)
    406         {
    407             switch(_state)
    408             {
    409                 case __REDISPATCHED:
    410                 case __DISPATCHED:
    411                     _state=__UNCOMPLETED;
    412                     return true;
    413 
    414                 case __IDLE:
    415                     throw new IllegalStateException(this.getStatusString());
    416 
    417                 case __ASYNCSTARTED:
    418                     _initial=false;
    419                     _state=__ASYNCWAIT;
    420                     scheduleTimeout(); // could block and change state.
    421                     if (_state==__ASYNCWAIT)
    422                         return true;
    423                     else if (_state==__COMPLETING)
    424                     {
    425                         _state=__UNCOMPLETED;
    426                         return true;
    427                     }
    428                     _initial=false;
    429                     _state=__REDISPATCHED;
    430                     return false;
    431 
    432                 case __REDISPATCHING:
    433                     _initial=false;
    434                     _state=__REDISPATCHED;
    435                     return false;
    436 
    437                 case __COMPLETING:
    438                     _initial=false;
    439                     _state=__UNCOMPLETED;
    440                     return true;
    441 
    442                 default:
    443                     throw new IllegalStateException(this.getStatusString());
    444             }
    445         }
    446     }
    447 
    448     /* ------------------------------------------------------------ */
    449     public void dispatch()
    450     {
    451         boolean dispatch=false;
    452         synchronized (this)
    453         {
    454             switch(_state)
    455             {
    456                 case __ASYNCSTARTED:
    457                     _state=__REDISPATCHING;
    458                     _resumed=true;
    459                     return;
    460 
    461                 case __ASYNCWAIT:
    462                     dispatch=!_expired;
    463                     _state=__REDISPATCH;
    464                     _resumed=true;
    465                     break;
    466 
    467                 case __REDISPATCH:
    468                     return;
    469 
    470                 default:
    471                     throw new IllegalStateException(this.getStatusString());
    472             }
    473         }
    474 
    475         if (dispatch)
    476         {
    477             cancelTimeout();
    478             scheduleDispatch();
    479         }
    480     }
    481 
    482     /* ------------------------------------------------------------ */
    483     protected void expired()
    484     {
    485         final List<ContinuationListener> cListeners;
    486         final List<AsyncListener> aListeners;
    487         synchronized (this)
    488         {
    489             switch(_state)
    490             {
    491                 case __ASYNCSTARTED:
    492                 case __ASYNCWAIT:
    493                     cListeners=_continuationListeners;
    494                     aListeners=_asyncListeners;
    495                     break;
    496                 default:
    497                     cListeners=null;
    498                     aListeners=null;
    499                     return;
    500             }
    501             _expired=true;
    502         }
    503 
    504         if (aListeners!=null)
    505         {
    506             for (AsyncListener listener : aListeners)
    507             {
    508                 try
    509                 {
    510                     listener.onTimeout(_event);
    511                 }
    512                 catch(Exception e)
    513                 {
    514                     LOG.debug(e);
    515                     _connection.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
    516                     break;
    517                 }
    518             }
    519         }
    520         if (cListeners!=null)
    521         {
    522             for (ContinuationListener listener : cListeners)
    523             {
    524                 try
    525                 {
    526                     listener.onTimeout(this);
    527                 }
    528                 catch(Exception e)
    529                 {
    530                     LOG.warn(e);
    531                 }
    532             }
    533         }
    534 
    535         synchronized (this)
    536         {
    537             switch(_state)
    538             {
    539                 case __ASYNCSTARTED:
    540                 case __ASYNCWAIT:
    541                     dispatch();
    542                     break;
    543 
    544                 default:
    545                     if (!_continuation)
    546                         _expired=false;
    547             }
    548         }
    549 
    550         scheduleDispatch();
    551     }
    552 
    553     /* ------------------------------------------------------------ */
    554     /* (non-Javadoc)
    555      * @see javax.servlet.ServletRequest#complete()
    556      */
    557     public void complete()
    558     {
    559         // just like resume, except don't set _resumed=true;
    560         boolean dispatch=false;
    561         synchronized (this)
    562         {
    563             switch(_state)
    564             {
    565                 case __DISPATCHED:
    566                 case __REDISPATCHED:
    567                     throw new IllegalStateException(this.getStatusString());
    568 
    569                 case __ASYNCSTARTED:
    570                     _state=__COMPLETING;
    571                     return;
    572 
    573                 case __ASYNCWAIT:
    574                     _state=__COMPLETING;
    575                     dispatch=!_expired;
    576                     break;
    577 
    578                 default:
    579                     throw new IllegalStateException(this.getStatusString());
    580             }
    581         }
    582 
    583         if (dispatch)
    584         {
    585             cancelTimeout();
    586             scheduleDispatch();
    587         }
    588     }
    589 
    590     /* ------------------------------------------------------------ */
    591     /* (non-Javadoc)
    592      * @see javax.servlet.ServletRequest#complete()
    593      */
    594     public void errorComplete()
    595     {
    596         // just like complete except can overrule a prior dispatch call;
    597         synchronized (this)
    598         {
    599             switch(_state)
    600             {
    601                 case __REDISPATCHING:
    602                 case __ASYNCSTARTED:
    603                     _state=__COMPLETING;
    604                     _resumed=false;
    605                     return;
    606 
    607                 case __COMPLETING:
    608                     return;
    609 
    610                 default:
    611                     throw new IllegalStateException(this.getStatusString());
    612             }
    613         }
    614     }
    615 
    616     /* ------------------------------------------------------------ */
    617     @Override
    618     public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
    619     {
    620         try
    621         {
    622             // TODO inject
    623             return clazz.newInstance();
    624         }
    625         catch(Exception e)
    626         {
    627             throw new ServletException(e);
    628         }
    629     }
    630 
    631 
    632     /* ------------------------------------------------------------ */
    633     /* (non-Javadoc)
    634      * @see javax.servlet.ServletRequest#complete()
    635      */
    636     protected void doComplete(Throwable ex)
    637     {
    638         final List<ContinuationListener> cListeners;
    639         final List<AsyncListener> aListeners;
    640         synchronized (this)
    641         {
    642             switch(_state)
    643             {
    644                 case __UNCOMPLETED:
    645                     _state=__COMPLETED;
    646                     cListeners=_continuationListeners;
    647                     aListeners=_asyncListeners;
    648                     break;
    649 
    650                 default:
    651                     cListeners=null;
    652                     aListeners=null;
    653                     throw new IllegalStateException(this.getStatusString());
    654             }
    655         }
    656 
    657         if (aListeners!=null)
    658         {
    659             for (AsyncListener listener : aListeners)
    660             {
    661                 try
    662                 {
    663                     if (ex!=null)
    664                     {
    665                         _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
    666                         _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage());
    667                         listener.onError(_event);
    668                     }
    669                     else
    670                         listener.onComplete(_event);
    671                 }
    672                 catch(Exception e)
    673                 {
    674                     LOG.warn(e);
    675                 }
    676             }
    677         }
    678         if (cListeners!=null)
    679         {
    680             for (ContinuationListener listener : cListeners)
    681             {
    682                 try
    683                 {
    684                     listener.onComplete(this);
    685                 }
    686                 catch(Exception e)
    687                 {
    688                     LOG.warn(e);
    689                 }
    690             }
    691         }
    692     }
    693 
    694     /* ------------------------------------------------------------ */
    695     protected void recycle()
    696     {
    697         synchronized (this)
    698         {
    699             switch(_state)
    700             {
    701                 case __DISPATCHED:
    702                 case __REDISPATCHED:
    703                     throw new IllegalStateException(getStatusString());
    704                 default:
    705                     _state=__IDLE;
    706             }
    707             _initial = true;
    708             _resumed=false;
    709             _expired=false;
    710             _responseWrapped=false;
    711             cancelTimeout();
    712             _timeoutMs=DEFAULT_TIMEOUT;
    713             _continuationListeners=null;
    714         }
    715     }
    716 
    717     /* ------------------------------------------------------------ */
    718     public void cancel()
    719     {
    720         synchronized (this)
    721         {
    722             cancelTimeout();
    723             _continuationListeners=null;
    724         }
    725     }
    726 
    727     /* ------------------------------------------------------------ */
    728     protected void scheduleDispatch()
    729     {
    730         EndPoint endp=_connection.getEndPoint();
    731         if (!endp.isBlocking())
    732         {
    733             ((AsyncEndPoint)endp).asyncDispatch();
    734         }
    735     }
    736 
    737     /* ------------------------------------------------------------ */
    738     protected void scheduleTimeout()
    739     {
    740         EndPoint endp=_connection.getEndPoint();
    741         if (_timeoutMs>0)
    742         {
    743             if (endp.isBlocking())
    744             {
    745                 synchronized(this)
    746                 {
    747                     _expireAt = System.currentTimeMillis()+_timeoutMs;
    748                     long wait=_timeoutMs;
    749                     while (_expireAt>0 && wait>0 && _connection.getServer().isRunning())
    750                     {
    751                         try
    752                         {
    753                             this.wait(wait);
    754                         }
    755                         catch (InterruptedException e)
    756                         {
    757                             LOG.ignore(e);
    758                         }
    759                         wait=_expireAt-System.currentTimeMillis();
    760                     }
    761 
    762                     if (_expireAt>0 && wait<=0 && _connection.getServer().isRunning())
    763                     {
    764                         expired();
    765                     }
    766                 }
    767             }
    768             else
    769             {
    770                 ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs);
    771             }
    772         }
    773     }
    774 
    775     /* ------------------------------------------------------------ */
    776     protected void cancelTimeout()
    777     {
    778         EndPoint endp=_connection.getEndPoint();
    779         if (endp.isBlocking())
    780         {
    781             synchronized(this)
    782             {
    783                 _expireAt=0;
    784                 this.notifyAll();
    785             }
    786         }
    787         else
    788         {
    789             final AsyncEventState event=_event;
    790             if (event!=null)
    791             {
    792                 ((AsyncEndPoint)endp).cancelTimeout(event._timeout);
    793             }
    794         }
    795     }
    796 
    797     /* ------------------------------------------------------------ */
    798     public boolean isCompleting()
    799     {
    800         synchronized (this)
    801         {
    802             return _state==__COMPLETING;
    803         }
    804     }
    805 
    806     /* ------------------------------------------------------------ */
    807     boolean isUncompleted()
    808     {
    809         synchronized (this)
    810         {
    811             return _state==__UNCOMPLETED;
    812         }
    813     }
    814 
    815     /* ------------------------------------------------------------ */
    816     public boolean isComplete()
    817     {
    818         synchronized (this)
    819         {
    820             return _state==__COMPLETED;
    821         }
    822     }
    823 
    824 
    825     /* ------------------------------------------------------------ */
    826     public boolean isAsyncStarted()
    827     {
    828         synchronized (this)
    829         {
    830             switch(_state)
    831             {
    832                 case __ASYNCSTARTED:
    833                 case __REDISPATCHING:
    834                 case __REDISPATCH:
    835                 case __ASYNCWAIT:
    836                     return true;
    837 
    838                 default:
    839                     return false;
    840             }
    841         }
    842     }
    843 
    844 
    845     /* ------------------------------------------------------------ */
    846     public boolean isAsync()
    847     {
    848         synchronized (this)
    849         {
    850             switch(_state)
    851             {
    852                 case __IDLE:
    853                 case __DISPATCHED:
    854                 case __UNCOMPLETED:
    855                 case __COMPLETED:
    856                     return false;
    857 
    858                 default:
    859                     return true;
    860             }
    861         }
    862     }
    863 
    864     /* ------------------------------------------------------------ */
    865     public void dispatch(ServletContext context, String path)
    866     {
    867         _event._dispatchContext=context;
    868         _event.setPath(path);
    869         dispatch();
    870     }
    871 
    872     /* ------------------------------------------------------------ */
    873     public void dispatch(String path)
    874     {
    875         _event.setPath(path);
    876         dispatch();
    877     }
    878 
    879     /* ------------------------------------------------------------ */
    880     public Request getBaseRequest()
    881     {
    882         return _connection.getRequest();
    883     }
    884 
    885     /* ------------------------------------------------------------ */
    886     public ServletRequest getRequest()
    887     {
    888         if (_event!=null)
    889             return _event.getSuppliedRequest();
    890         return _connection.getRequest();
    891     }
    892 
    893     /* ------------------------------------------------------------ */
    894     public ServletResponse getResponse()
    895     {
    896         if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
    897             return _event.getSuppliedResponse();
    898         return _connection.getResponse();
    899     }
    900 
    901     /* ------------------------------------------------------------ */
    902     public void start(final Runnable run)
    903     {
    904         final AsyncEventState event=_event;
    905         if (event!=null)
    906         {
    907             _connection.getServer().getThreadPool().dispatch(new Runnable()
    908             {
    909                 public void run()
    910                 {
    911                     ((Context)event.getServletContext()).getContextHandler().handle(run);
    912                 }
    913             });
    914         }
    915     }
    916 
    917     /* ------------------------------------------------------------ */
    918     public boolean hasOriginalRequestAndResponse()
    919     {
    920         synchronized (this)
    921         {
    922             return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response);
    923         }
    924     }
    925 
    926     /* ------------------------------------------------------------ */
    927     public ContextHandler getContextHandler()
    928     {
    929         final AsyncEventState event=_event;
    930         if (event!=null)
    931             return ((Context)event.getServletContext()).getContextHandler();
    932         return null;
    933     }
    934 
    935 
    936     /* ------------------------------------------------------------ */
    937     /**
    938      * @see Continuation#isResumed()
    939      */
    940     public boolean isResumed()
    941     {
    942         synchronized (this)
    943         {
    944             return _resumed;
    945         }
    946     }
    947     /* ------------------------------------------------------------ */
    948     /**
    949      * @see Continuation#isExpired()
    950      */
    951     public boolean isExpired()
    952     {
    953         synchronized (this)
    954         {
    955             return _expired;
    956         }
    957     }
    958 
    959     /* ------------------------------------------------------------ */
    960     /**
    961      * @see Continuation#resume()
    962      */
    963     public void resume()
    964     {
    965         dispatch();
    966     }
    967 
    968 
    969 
    970     /* ------------------------------------------------------------ */
    971     protected void startAsync(final ServletContext context,
    972             final ServletRequest request,
    973             final ServletResponse response)
    974     {
    975         synchronized (this)
    976         {
    977             _responseWrapped=!(response instanceof Response);
    978             doSuspend(context,request,response);
    979             if (request instanceof HttpServletRequest)
    980             {
    981                 _event._pathInContext = URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo());
    982             }
    983         }
    984     }
    985 
    986     /* ------------------------------------------------------------ */
    987     protected void startAsync()
    988     {
    989         _responseWrapped=false;
    990         _continuation=false;
    991         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
    992     }
    993 
    994 
    995     /* ------------------------------------------------------------ */
    996     /**
    997      * @see Continuation#suspend()
    998      */
    999     public void suspend(ServletResponse response)
   1000     {
   1001         _continuation=true;
   1002         _responseWrapped=!(response instanceof Response);
   1003         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response);
   1004     }
   1005 
   1006     /* ------------------------------------------------------------ */
   1007     /**
   1008      * @see Continuation#suspend()
   1009      */
   1010     public void suspend()
   1011     {
   1012         _responseWrapped=false;
   1013         _continuation=true;
   1014         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
   1015     }
   1016 
   1017     /* ------------------------------------------------------------ */
   1018     /**
   1019      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
   1020      */
   1021     public ServletResponse getServletResponse()
   1022     {
   1023         if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
   1024             return _event.getSuppliedResponse();
   1025         return _connection.getResponse();
   1026     }
   1027 
   1028     /* ------------------------------------------------------------ */
   1029     /**
   1030      * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
   1031      */
   1032     public Object getAttribute(String name)
   1033     {
   1034         return _connection.getRequest().getAttribute(name);
   1035     }
   1036 
   1037     /* ------------------------------------------------------------ */
   1038     /**
   1039      * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
   1040      */
   1041     public void removeAttribute(String name)
   1042     {
   1043         _connection.getRequest().removeAttribute(name);
   1044     }
   1045 
   1046     /* ------------------------------------------------------------ */
   1047     /**
   1048      * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
   1049      */
   1050     public void setAttribute(String name, Object attribute)
   1051     {
   1052         _connection.getRequest().setAttribute(name,attribute);
   1053     }
   1054 
   1055     /* ------------------------------------------------------------ */
   1056     /**
   1057      * @see org.eclipse.jetty.continuation.Continuation#undispatch()
   1058      */
   1059     public void undispatch()
   1060     {
   1061         if (isSuspended())
   1062         {
   1063             if (LOG.isDebugEnabled())
   1064                 throw new ContinuationThrowable();
   1065             else
   1066                 throw __exception;
   1067         }
   1068         throw new IllegalStateException("!suspended");
   1069     }
   1070 
   1071     /* ------------------------------------------------------------ */
   1072     /* ------------------------------------------------------------ */
   1073     public class AsyncTimeout extends Timeout.Task implements Runnable
   1074     {
   1075             @Override
   1076             public void expired()
   1077             {
   1078                 AsyncContinuation.this.expired();
   1079             }
   1080 
   1081             @Override
   1082             public void run()
   1083             {
   1084                 AsyncContinuation.this.expired();
   1085             }
   1086     }
   1087 
   1088     /* ------------------------------------------------------------ */
   1089     /* ------------------------------------------------------------ */
   1090     public class AsyncEventState extends AsyncEvent
   1091     {
   1092         private final ServletContext _suspendedContext;
   1093         private ServletContext _dispatchContext;
   1094         private String _pathInContext;
   1095         private Timeout.Task _timeout=  new AsyncTimeout();
   1096 
   1097         public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
   1098         {
   1099             super(AsyncContinuation.this, request,response);
   1100             _suspendedContext=context;
   1101             // Get the base request So we can remember the initial paths
   1102             Request r=_connection.getRequest();
   1103 
   1104             // If we haven't been async dispatched before
   1105             if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
   1106             {
   1107                 // We are setting these attributes during startAsync, when the spec implies that
   1108                 // they are only available after a call to AsyncContext.dispatch(...);
   1109 
   1110                 // have we been forwarded before?
   1111                 String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
   1112                 if (uri!=null)
   1113                 {
   1114                     r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
   1115                     r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
   1116                     r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
   1117                     r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
   1118                     r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
   1119                 }
   1120                 else
   1121                 {
   1122                     r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
   1123                     r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
   1124                     r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
   1125                     r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
   1126                     r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
   1127                 }
   1128             }
   1129         }
   1130 
   1131         public ServletContext getSuspendedContext()
   1132         {
   1133             return _suspendedContext;
   1134         }
   1135 
   1136         public ServletContext getDispatchContext()
   1137         {
   1138             return _dispatchContext;
   1139         }
   1140 
   1141         public ServletContext getServletContext()
   1142         {
   1143             return _dispatchContext==null?_suspendedContext:_dispatchContext;
   1144         }
   1145 
   1146         public void setPath(String path)
   1147         {
   1148             _pathInContext=path;
   1149         }
   1150 
   1151         /* ------------------------------------------------------------ */
   1152         /**
   1153          * @return The path in the context
   1154          */
   1155         public String getPath()
   1156         {
   1157             return _pathInContext;
   1158         }
   1159     }
   1160 }
   1161