View Javadoc

1   package org.eclipse.jetty.continuation;
2   
3   import java.io.IOException;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import javax.servlet.AsyncContext;
8   import javax.servlet.AsyncEvent;
9   import javax.servlet.AsyncListener;
10  import javax.servlet.DispatcherType;
11  import javax.servlet.ServletRequest;
12  import javax.servlet.ServletResponse;
13  import javax.servlet.ServletResponseWrapper;
14  
15  
16  /* ------------------------------------------------------------ */
17  /**
18   * This implementation of Continuation is used by {@link ContinuationSupport}
19   * when it detects that the application has been deployed in a non-jetty Servlet 3 
20   * server.
21   */
22  public class Servlet3Continuation implements Continuation
23  {
24      // Exception reused for all continuations
25      // Turn on debug in ContinuationFilter to see real stack trace.
26      private final static ContinuationThrowable __exception = new ContinuationThrowable();
27      
28      private final ServletRequest _request;
29      private ServletResponse _response;
30      private AsyncContext _context;
31      private List<AsyncListener> _listeners=new ArrayList<AsyncListener>(); 
32      private volatile boolean _initial=true;
33      private volatile boolean _resumed=false;
34      private volatile boolean _expired=false;
35      private volatile boolean _responseWrapped=false;
36  
37      private long _timeoutMs=-1;
38  
39      /* ------------------------------------------------------------ */
40      public Servlet3Continuation(ServletRequest request)
41      {
42          _request=request;
43  
44          _listeners.add(new AsyncListener()
45          {
46              public void onComplete(AsyncEvent event) throws IOException
47              {
48              }
49  
50              public void onError(AsyncEvent event) throws IOException
51              {
52              }
53  
54              public void onStartAsync(AsyncEvent event) throws IOException
55              {
56                  event.getAsyncContext().addListener(this);
57              }
58  
59              public void onTimeout(AsyncEvent event) throws IOException
60              {
61                  _initial=false;
62                  event.getAsyncContext().dispatch();
63              }
64          });
65      }
66  
67      /* ------------------------------------------------------------ */
68      public void addContinuationListener(final ContinuationListener listener)
69      {
70          AsyncListener wrapped = new AsyncListener()
71          {
72              public void onComplete(final AsyncEvent event) throws IOException
73              {
74                  listener.onComplete(Servlet3Continuation.this);
75              }
76  
77              public void onError(AsyncEvent event) throws IOException
78              {
79                  listener.onComplete(Servlet3Continuation.this);
80              }
81  
82              public void onStartAsync(AsyncEvent event) throws IOException
83              {
84                  event.getAsyncContext().addListener(this);
85              }
86  
87              public void onTimeout(AsyncEvent event) throws IOException
88              {
89                  _expired=true;
90                  listener.onTimeout(Servlet3Continuation.this);
91              }
92          };
93          
94          if (_context==null)
95              _context.addListener(wrapped);
96          else
97              _listeners.add(wrapped);
98      }
99  
100     /* ------------------------------------------------------------ */
101     public void complete()
102     {
103         AsyncContext context=_context;
104         if (context==null)
105             throw new IllegalStateException();
106         _context.complete();
107     }
108 
109     /* ------------------------------------------------------------ */
110     public ServletResponse getServletResponse()
111     {
112         return _response;
113     }
114 
115     /* ------------------------------------------------------------ */
116     public boolean isExpired()
117     {
118         return _expired;
119     }
120 
121     /* ------------------------------------------------------------ */
122     public boolean isInitial()
123     {
124         // TODO - this is not perfect if non continuation API is used directly
125         return _initial&&_request.getDispatcherType()!=DispatcherType.ASYNC;
126     }
127 
128     /* ------------------------------------------------------------ */
129     public boolean isResumed()
130     {
131         return _resumed;
132     }
133 
134     /* ------------------------------------------------------------ */
135     public boolean isSuspended()
136     {
137         return _request.isAsyncStarted();
138     }
139 
140     /* ------------------------------------------------------------ */
141     public void keepWrappers()
142     {
143         _responseWrapped=true;
144     }
145 
146     /* ------------------------------------------------------------ */
147     public void resume()
148     {
149         AsyncContext context=_context;
150         if (context==null)
151             throw new IllegalStateException();
152         _resumed=true;
153         _context.dispatch();
154     }
155 
156     /* ------------------------------------------------------------ */
157     public void setTimeout(long timeoutMs)
158     {
159         _timeoutMs=timeoutMs;
160         if (_context!=null)
161             _context.setTimeout(timeoutMs);
162     }
163 
164     /* ------------------------------------------------------------ */
165     public void suspend(ServletResponse response)
166     {
167         _response=response;
168         _responseWrapped=response instanceof ServletResponseWrapper;
169         _resumed=false;
170         _expired=false;
171         _context=_request.startAsync();
172         _context.setTimeout(_timeoutMs);
173         
174         for (AsyncListener listener:_listeners)
175             _context.addListener(listener);
176         _listeners.clear();
177     }
178 
179     /* ------------------------------------------------------------ */
180     public void suspend()
181     {
182         _resumed=false;
183         _expired=false;
184         _context=_request.startAsync();
185         _context.setTimeout(_timeoutMs);
186                 
187         for (AsyncListener listener:_listeners)
188             _context.addListener(listener);
189         _listeners.clear();
190     }
191 
192     /* ------------------------------------------------------------ */
193     public boolean isResponseWrapped()
194     {
195         return _responseWrapped;
196     }
197 
198     /* ------------------------------------------------------------ */
199     /**
200      * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
201      */
202     public Object getAttribute(String name)
203     {
204         return _request.getAttribute(name);
205     }
206 
207     /* ------------------------------------------------------------ */
208     /**
209      * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
210      */
211     public void removeAttribute(String name)
212     {
213         _request.removeAttribute(name);
214     }
215 
216     /* ------------------------------------------------------------ */
217     /**
218      * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
219      */
220     public void setAttribute(String name, Object attribute)
221     {
222         _request.setAttribute(name,attribute);
223     }
224 
225     /* ------------------------------------------------------------ */
226     /**
227      * @see org.eclipse.jetty.continuation.Continuation#undispatch()
228      */
229     public void undispatch()
230     {
231         if (isSuspended())
232         {
233             if (ContinuationFilter.__debug)
234                 throw new ContinuationThrowable();
235             throw __exception;
236         }
237         throw new IllegalStateException("!suspended");
238     }
239 }