1 // ========================================================================
2 // Copyright (c) 2004-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.continuation;
15
16 import javax.servlet.Filter;
17 import javax.servlet.FilterChain;
18 import javax.servlet.Servlet;
19 import javax.servlet.ServletRequest;
20 import javax.servlet.ServletResponse;
21 import javax.servlet.ServletResponseWrapper;
22
23 /* ------------------------------------------------------------ */
24 /**
25 * Continuation.
26 *
27 * A continuation is a mechanism by which a HTTP Request can be suspended and
28 * restarted after a timeout or an asynchronous event has occurred.
29 * <p>
30 * The continuation mechanism is a portable mechansim that will work
31 * asychronously without additional configuration of all jetty-7,
32 * jetty-8 and Servlet 3.0 containers. With the addition of
33 * the {@link ContinuationFilter}, the mechansism will also work
34 * asynchronously on jetty-6 and non-asynchronously on any
35 * servlet 2.5 container.
36 * <p>
37 * The Continuation API is a simplification of the richer async API
38 * provided by the servlet-3.0 and an enhancement of the continuation
39 * API that was introduced with jetty-6.
40 * </p>
41 * <h1>Continuation Usage</h1>
42 * <p>
43 * A continuation object is obtained for a request by calling the
44 * factory method {@link ContinuationSupport#getContinuation(ServletRequest)}.
45 * The continuation type returned will depend on the servlet container
46 * being used.
47 * </p>
48 * <p>
49 * There are two distinct style of operation of the continuation API.
50 * </p>
51 * <h3>Suspend/Resume Usage</h3>
52 * <p>The suspend/resume style is used when a servlet and/or
53 * filter is used to generate the response after a asynchronous wait that is
54 * terminated by an asynchronous handler.
55 * </p>
56 * <pre>
57 * <b>Filter/Servlet:</b>
58 * // if we need to get asynchronous results
59 * Object results = request.getAttribute("results);
60 * if (results==null)
61 * {
62 * Continuation continuation = ContinuationSupport.getContinuation(request);
63 * continuation.suspend();
64 * myAsyncHandler.register(continuation);
65 * return; // or continuation.undispatch();
66 * }
67 *
68 * async wait ...
69 *
70 * <b>Async Handler:</b>
71 * // when the waited for event happens
72 * continuation.setAttribute("results",event);
73 * continuation.resume();
74 *
75 * <b>Filter/Servlet:</b>
76 * // when the request is redispatched
77 * if (results==null)
78 * {
79 * ... // see above
80 * }
81 * else
82 * {
83 * response.getOutputStream().write(process(results));
84 * }
85 * </pre>
86 * <h3>Suspend/Complete Usage</h3>
87 * <p>
88 * The suspend/complete style is used when an asynchronous handler is used to
89 * generate the response:
90 * </p>
91 * <pre>
92 * <b>Filter/Servlet:</b>
93 * // when we want to enter asynchronous mode
94 * Continuation continuation = ContinuationSupport.getContinuation(request);
95 * continuation.suspend(response); // response may be wrapped
96 * myAsyncHandler.register(continuation);
97 * return; // or continuation.undispatch();
98 *
99 * <b>Wrapping Filter:</b>
100 * // any filter that had wrapped the response should be implemented like:
101 * try
102 * {
103 * chain.doFilter(request,wrappedResponse);
104 * }
105 * finally
106 * {
107 * if (!continuation.isResponseWrapped())
108 * wrappedResponse.finish()
109 * else
110 * continuation.addContinuationListener(myCompleteListener)
111 * }
112 *
113 * async wait ...
114 *
115 * <b>Async Handler:</b>
116 * // when the async event happens
117 * continuation.getServletResponse().getOutputStream().write(process(event));
118 * continuation.complete()
119 * </pre>
120 *
121 * <h1>Continuation Timeout</h1>
122 * <p>
123 * If a continuation is suspended, but neither {@link #complete()} or {@link #resume()} is
124 * called during the period set by {@link #setTimeout(long)}, then the continuation will
125 * expire and {@link #isExpired()} will return true.
126 * </p>
127 * <p>
128 * When a continuation expires, the {@link ContinuationListener#onTimeout(Continuation)}
129 * method is called on any {@link ContinuationListener} that has been registered via the
130 * {@link #addContinuationListener(ContinuationListener)} method. The onTimeout handlers
131 * may write a response and call {@link #complete()}. If {@link #complete()} is not called,
132 * then the container will redispatch the request as if {@link #resume()} had been called,
133 * except that {@link #isExpired()} will be true and {@link #isResumed()} will be false.
134 * </p>
135 *
136 * @see ContinuationSupport
137 * @see ContinuationListener
138 *
139 */
140 public interface Continuation
141 {
142 public final static String ATTRIBUTE = "org.eclipse.jetty.continuation";
143
144 /* ------------------------------------------------------------ */
145 /**
146 * Set the continuation timeout.
147 *
148 * @param timeoutMs
149 * The time in milliseconds to wait before expiring this
150 * continuation after a call to {@link #suspend()} or {@link #suspend(ServletResponse)}
151 */
152 void setTimeout(long timeoutMs);
153
154 /* ------------------------------------------------------------ */
155 /**
156 * Suspend the processing of the request and associated
157 * {@link ServletResponse}.
158 *
159 * <p>
160 * After this method has been called, the lifecycle of the request will be
161 * extended beyond the return to the container from the
162 * {@link Servlet#service(ServletRequest, ServletResponse)} method and
163 * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
164 * calls. When a suspended request is returned to the container after
165 * a dispatch, then the container will not commit the associated response
166 * (unless an exception other than {@link ContinuationThrowable} is thrown).
167 * </p>
168 *
169 * <p>
170 * When the thread calling the filter chain and/or servlet has returned to
171 * the container with a suspended request, the thread is freed for other
172 * tasks and the request is held until either:
173 * <ul>
174 * <li>a call to {@link ServletRequest#resume()}.</li>
175 * <li>a call to {@link ServletRequest#complete()}.</li>
176 * <li>the timeout expires.</li>
177 * </ul>
178 * <p>
179 * Typically suspend with no arguments is uses when a call to {@link #resume()}
180 * is expected. If a call to {@link #complete()} is expected, then the
181 * {@link #suspend(ServletResponse)} method should be used instead of this method.
182 * </p>
183 *
184 * @exception IllegalStateException
185 * If the request cannot be suspended
186 */
187 void suspend();
188
189
190 /* ------------------------------------------------------------ */
191 /**
192 * Suspend the processing of the request and associated
193 * {@link ServletResponse}.
194 *
195 * <p>
196 * After this method has been called, the lifecycle of the request will be
197 * extended beyond the return to the container from the
198 * {@link Servlet#service(ServletRequest, ServletResponse)} method and
199 * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
200 * calls. When a suspended request is returned to the container after
201 * a dispatch, then the container will not commit the associated response
202 * (unless an exception other than {@link ContinuationThrowable} is thrown).
203 * </p>
204 * <p>
205 * When the thread calling the filter chain and/or servlet has returned to
206 * the container with a suspended request, the thread is freed for other
207 * tasks and the request is held until either:
208 * <ul>
209 * <li>a call to {@link ServletRequest#resume()}.</li>
210 * <li>a call to {@link ServletRequest#complete()}.</li>
211 * <li>the timeout expires.</li>
212 * </ul>
213 * <p>
214 * Typically suspend with a response argument is uses when a call to {@link #complete()}
215 * is expected. If a call to {@link #resume()} is expected, then the
216 * {@link #suspend()} method should be used instead of this method.
217 * </p>
218 * <p>
219 * Filters that may wrap the response object should check {@link #isResponseWrapped()}
220 * to decide if they should destroy/finish the wrapper. If {@link #isResponseWrapped()}
221 * returns true, then the wrapped request has been passed to the asynchronous
222 * handler and the wrapper should not be destroyed/finished until after a call to
223 * {@link #complete()} (potentially using a {@link ContinuationListener#onComplete(Continuation)}
224 * listener).
225 *
226 * @param response The response to return via a call to {@link #getServletResponse()}
227 * @exception IllegalStateException
228 * If the request cannot be suspended
229 */
230 void suspend(ServletResponse response);
231
232 /* ------------------------------------------------------------ */
233 /**
234 * Resume a suspended request.
235 *
236 * <p>
237 * This method can be called by any thread that has been passed a reference
238 * to a continuation. When called the request is redispatched to the
239 * normal filter chain and servlet processing with {@link #isInitial()} false.
240 * </p>
241 * <p>
242 * If resume is called before a suspended request is returned to the
243 * container (ie the thread that called {@link #suspend(long)} is still
244 * within the filter chain and/or servlet service method), then the resume
245 * does not take effect until the call to the filter chain and/or servlet
246 * returns to the container. In this case both {@link #isSuspended()} and
247 * {@link isResumed()} return true. Multiple calls to resume are ignored.
248 * </p>
249 * <p>
250 * Typically resume() is used after a call to {@link #suspend()} with
251 * no arguments. The dispatch after a resume call will use the original
252 * request and response objects, even if {@link #suspend(ServletResponse)}
253 * had been passed a wrapped response.
254 * </p>
255 *
256 * @see {@link #suspend()}
257 * @exception IllegalStateException if the request is not suspended.
258 *
259 */
260 void resume();
261
262 /* ------------------------------------------------------------ */
263 /**
264 * Complete a suspended request.
265 *
266 * <p>
267 * This method can be called by any thread that has been passed a reference
268 * to a suspended request. When a request is completed, the associated
269 * response object commited and flushed. The request is not redispatched.
270 * </p>
271 *
272 * <p>
273 * If complete is called before a suspended request is returned to the
274 * container (ie the thread that called {@link #suspend(long)} is still
275 * within the filter chain and/or servlet service method), then the complete
276 * does not take effect until the call to the filter chain and/or servlet
277 * returns to the container. In this case both {@link #isSuspended()} and
278 * {@link isResumed()} return true.
279 * </p>
280 *
281 * <p>
282 * Typically resume() is used after a call to {@link #suspend(ServletResponse)} with
283 * a possibly wrapped response. The async handler should use the response
284 * provided by {@link #getServletResponse()} to write the response before
285 * calling {@link #complete()}. If the request was suspended with a
286 * call to {@link #suspend()} then no response object will be available via
287 * {@link #getServletResponse()}.
288 * </p>
289 *
290 * <p>
291 * Once complete has been called and any thread calling the filter chain
292 * and/or servlet chain has returned to the container, the request lifecycle
293 * is complete. The container is able to recycle request objects, so it is
294 * not valid hold a request or continuation reference after the end of the
295 * life cycle.
296 *
297 * @see {@link #suspend()}
298 * @exception IllegalStateException
299 * if the request is not suspended.
300 *
301 */
302 void complete();
303
304 /* ------------------------------------------------------------ */
305 /**
306 * @return true after {@link #suspend(long)} has been called and before the
307 * request has been redispatched due to being resumed, completed or
308 * timed out.
309 */
310 boolean isSuspended();
311
312 /* ------------------------------------------------------------ */
313 /**
314 * @return true if the request has been redispatched by a call to
315 * {@link #resume()}. Returns false after any subsequent call to
316 * suspend
317 */
318 boolean isResumed();
319
320 /* ------------------------------------------------------------ */
321 /**
322 * @return true after a request has been redispatched as the result of a
323 * timeout. Returns false after any subsequent call to suspend.
324 */
325 boolean isExpired();
326
327 /* ------------------------------------------------------------ */
328 /**
329 * @return true while the request is within the initial dispatch to the
330 * filter chain and/or servlet. Will return false once the calling
331 * thread has returned to the container after suspend has been
332 * called and during any subsequent redispatch.
333 */
334 boolean isInitial();
335
336 /* ------------------------------------------------------------ */
337 /**
338 * Is the suspended response wrapped.
339 * <p>
340 * Filters that wrap the response object should check this method to
341 * determine if they should destroy/finish the wrapped response. If
342 * the request was suspended with a call to {@link #suspend(ServletResponse)}
343 * that passed the wrapped response, then the filter should register
344 * a {@link ContinuationListener} to destroy/finish the wrapped response
345 * during a call to {@link ContinuationListener#onComplete(Continuation)}.
346 * @return True if {@link #suspend(ServletResponse)} has been passed a
347 * {@link ServletResponseWrapper} instance.
348 */
349 boolean isResponseWrapped();
350
351
352 /* ------------------------------------------------------------ */
353 /**
354 * Get the suspended response.
355 * @return the {@link ServletResponse} passed to {@link #suspend(ServletResponse)}.
356 */
357 ServletResponse getServletResponse();
358
359 /* ------------------------------------------------------------ */
360 /**
361 * Add a ContinuationListener.
362 *
363 * @param listener
364 */
365 void addContinuationListener(ContinuationListener listener);
366
367 /* ------------------------------------------------------------ */
368 /** Set a request attribute.
369 * This method is a convenience method to call the {@link ServletRequest#setAttribute(String, Object)}
370 * method on the associated request object.
371 * This is a thread safe call and may be called by any thread.
372 * @param name the attribute name
373 * @param attribute the attribute value
374 */
375 public void setAttribute(String name, Object attribute);
376
377 /* ------------------------------------------------------------ */
378 /** Get a request attribute.
379 * This method is a convenience method to call the {@link ServletRequest#getAttribute(String)}
380 * method on the associated request object.
381 * This is a thread safe call and may be called by any thread.
382 * @param name the attribute name
383 * @return the attribute value
384 */
385 public Object getAttribute(String name);
386
387 /* ------------------------------------------------------------ */
388 /** Remove a request attribute.
389 * This method is a convenience method to call the {@link ServletRequest#removeAttribute(String)}
390 * method on the associated request object.
391 * This is a thread safe call and may be called by any thread.
392 * @param name the attribute name
393 */
394 public void removeAttribute(String name);
395
396 /* ------------------------------------------------------------ */
397 /**
398 * Undispatch the request.
399 * <p>
400 * This method can be called on a suspended continuation in order
401 * to exit the dispatch to the filter/servlet by throwing a {@link ContinuationThrowable}
402 * which is caught either by the container or the {@link ContinuationFilter}.
403 * This is an alternative to simply returning from the dispatch in the case
404 * where filters in the filter chain may not be prepared to handle a suspended
405 * request.
406 * </p>
407 * This method should only be used as a last resort and a normal return is a prefereable
408 * solution if filters can be updated to handle that case.
409 *
410 * @throws ContinuationThrowable thrown if the request is suspended. The instance of the
411 * exception may be reused on subsequent calls, so the stack frame may not be accurate.
412 */
413 public void undispatch() throws ContinuationThrowable;
414 }