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