javadoc and ContinuationThrowable
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@336 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
c8f300bc52
commit
36a156d4cf
|
@ -7,6 +7,7 @@ jetty-7.0.0.M3-SNAPSHOT
|
|||
+ 277798 Denial of Service Filter
|
||||
+ Portable continuations for jetty6 and servlet3
|
||||
+ Refactored continuations to only support response wrapping
|
||||
+ Added ContinuationThrowable
|
||||
|
||||
jetty-7.0.0.M2 18 May 2009
|
||||
+ JETTY-937 Work around Sun JVM bugs
|
||||
|
|
|
@ -18,6 +18,7 @@ import javax.servlet.FilterChain;
|
|||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletResponseWrapper;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -26,28 +27,131 @@ import javax.servlet.ServletResponse;
|
|||
* A continuation is a mechanism by which a HTTP Request can be suspended and
|
||||
* restarted after a timeout or an asynchronous event has occurred.
|
||||
* <p>
|
||||
* Continuations will use the asynchronous APIs if they used by a
|
||||
* webapp deployed in Jetty or a Servlet 3.0 container. For other
|
||||
* containers, the {@link ContinuationFilter} may be used to
|
||||
* simulate asynchronous features.
|
||||
* The continuation mechanism is a portable mechansim that will work
|
||||
* asychronously without additional configuration of all jetty-7,
|
||||
* jetty-8 and Servlet 3.0 containers. With the addition of
|
||||
* the {@link ContinuationFilter}, the mechansism will also work
|
||||
* asynchronously on jetty-6 and non-asynchronously on any
|
||||
* servlet 2.5 container.
|
||||
* <p>
|
||||
* The Continuation API is a simplification of the richer async API
|
||||
* provided by the servlet-3.0 and an enhancement of the continuation
|
||||
* API that was introduced with jetty-6.
|
||||
* </p>
|
||||
* <h1>Continuation Usage</h1>
|
||||
* <p>
|
||||
* A continuation object is obtained for a request by calling the
|
||||
* factory method {@link ContinuationSupport#getContinuation(ServletRequest)}.
|
||||
* The continuation type returned will depend on the servlet container
|
||||
* being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* There are two distinct style of operation of the continuation API.
|
||||
* </p>
|
||||
* <h3>Suspend/Resume Usage</h3>
|
||||
* <p>The suspend/resume style is used when a servlet and/or
|
||||
* filter is used to generate the response after a asynchronous wait that is
|
||||
* terminated by an asynchronous handler.
|
||||
* </p>
|
||||
* <pre>
|
||||
* <b>Filter/Servlet:</b>
|
||||
* // if we need to get asynchronous results
|
||||
* Object results = request.getAttribute("results);
|
||||
* if (results==null)
|
||||
* {
|
||||
* Continuation continuation = ContinuationSupport.getContinuation(request);
|
||||
* continuation.suspend();
|
||||
* myAsyncHandler.register(continuation);
|
||||
* return; // or continuation.undispatch();
|
||||
* }
|
||||
*
|
||||
* async wait ...
|
||||
*
|
||||
* <b>Async Handler:</b>
|
||||
* // when the waited for event happens
|
||||
* continuation.setAttribute("results",event);
|
||||
* continuation.resume();
|
||||
*
|
||||
* <b>Filter/Servlet:</b>
|
||||
* // when the request is redispatched
|
||||
* if (results==null)
|
||||
* {
|
||||
* ... // see above
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* response.getOutputStream().write(process(results));
|
||||
* }
|
||||
* </pre>
|
||||
* <h3>Suspend/Complete Usage</h3>
|
||||
* <p>
|
||||
* The suspend/complete style is used when an asynchronous handler is used to
|
||||
* generate the response:
|
||||
* </p>
|
||||
* <pre>
|
||||
* <b>Filter/Servlet:</b>
|
||||
* // when we want to enter asynchronous mode
|
||||
* Continuation continuation = ContinuationSupport.getContinuation(request);
|
||||
* continuation.suspend(response); // response may be wrapped
|
||||
* myAsyncHandler.register(continuation);
|
||||
* return; // or continuation.undispatch();
|
||||
*
|
||||
* <b>Wrapping Filter:</b>
|
||||
* // any filter that had wrapped the response should be implemented like:
|
||||
* try
|
||||
* {
|
||||
* chain.doFilter(request,wrappedResponse);
|
||||
* }
|
||||
* finally
|
||||
* {
|
||||
* if (!continuation.isResponseWrapped())
|
||||
* wrappedResponse.finish()
|
||||
* else
|
||||
* continuation.addContinuationListener(myCompleteListener)
|
||||
* }
|
||||
*
|
||||
* async wait ...
|
||||
*
|
||||
* <b>Async Handler:</b>
|
||||
* // when the async event happens
|
||||
* continuation.getServletResponse().getOutputStream().write(process(event));
|
||||
* continuation.complete()
|
||||
* </pre>
|
||||
*
|
||||
* <h1>Continuation Timeout</h1>
|
||||
* <p>
|
||||
* If a continuation is suspended, but neither {@link #complete()} or {@link #resume()} is
|
||||
* called during the period set by {@link #setTimeout(long)}, then the continuation will
|
||||
* expire and {@link #isExpired()} will return true.
|
||||
* </p>
|
||||
* <p>
|
||||
* When a continuation expires, the {@link ContinuationListener#onTimeout(Continuation)}
|
||||
* method is called on any {@link ContinuationListener} that has been registered via the
|
||||
* {@link #addContinuationListener(ContinuationListener)} method. The onTimeout handlers
|
||||
* may write a response and call {@link #complete()}. If {@link #complete()} is not called,
|
||||
* then the container will redispatch the request as if {@link #resume()} had been called,
|
||||
* except that {@link #isExpired()} will be true and {@link #isResumed()} will be false.
|
||||
* </p>
|
||||
*
|
||||
* @see ContinuationSupport
|
||||
* @see ContinuationListener
|
||||
*
|
||||
*/
|
||||
public interface Continuation
|
||||
{
|
||||
public final static String ATTRIBUTE = "org.eclipse.jetty.continuation";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the continuation timeout
|
||||
* Set the continuation timeout.
|
||||
*
|
||||
* @param timeoutMs
|
||||
* The time in milliseconds to wait before expiring this
|
||||
* continuation.
|
||||
* continuation after a call to {@link #suspend()} or {@link #suspend(ServletResponse)}
|
||||
*/
|
||||
void setTimeout(long timeoutMs);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Suspend the processing of the request and associated
|
||||
* {@link ServletResponse}.
|
||||
|
@ -57,81 +161,105 @@ public interface Continuation
|
|||
* extended beyond the return to the container from the
|
||||
* {@link Servlet#service(ServletRequest, ServletResponse)} method and
|
||||
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
* calls. If a request is suspended, then the container will not commit the
|
||||
* associated response when the call to the filter chain and/or servlet
|
||||
* service method returns to the container. Any exceptions thrown to the
|
||||
* container by a filter chain and/or servlet for a suspended requests are
|
||||
* silently ignored.
|
||||
* calls. When a suspended request is returned to the container after
|
||||
* a dispatch, then the container will not commit the associated response
|
||||
* (unless an exception other than {@link ContinuationThrowable} is thrown).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When the thread calling the filter chain and/or servlet has returned to
|
||||
* the container with a suspended request, the thread is freed for other
|
||||
* tasks and the request is held pending either:
|
||||
* tasks and the request is held until either:
|
||||
* <ul>
|
||||
* <li>a call to {@link ServletRequest#resume()}.</li>
|
||||
* <li>a call to {@link ServletRequest#complete()}.</li>
|
||||
* <li>the passed or default timeout expires.</li>
|
||||
* <li>there is IO activity on the connection that received the request,
|
||||
* such as the close of the connection or the receipt of a pipelined
|
||||
* request.
|
||||
* <li>the timeout expires.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* After any of the events listed above, the suspended request will be
|
||||
* redispatched via the filter and servlet processing.
|
||||
* Typically suspend with no arguments is uses when a call to {@link #resume()}
|
||||
* is expected. If a call to {@link #complete()} is expected, then the
|
||||
* {@link #suspend(ServletResponse)} method should be used instead of this method.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Suspend may only be called by a thread that is within the service calling
|
||||
* stack of
|
||||
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
* and/or {@link Servlet#service(ServletRequest, ServletResponse)}. A
|
||||
* request that has been dispatched for error handling may not be suspended.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #resume()}
|
||||
* @see {@link #complete()}
|
||||
*
|
||||
* @exception IllegalStateException
|
||||
* If the calling thread is not within the calling stack of
|
||||
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
* and/or
|
||||
* {@link Servlet#service(ServletRequest, ServletResponse)}
|
||||
* or if the request has been dispatched for error handling.
|
||||
* If the request cannot be suspended
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Suspend the processing of the request and associated
|
||||
* {@link ServletResponse}.
|
||||
*
|
||||
* <p>
|
||||
* After this method has been called, the lifecycle of the request will be
|
||||
* extended beyond the return to the container from the
|
||||
* {@link Servlet#service(ServletRequest, ServletResponse)} method and
|
||||
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
* calls. When a suspended request is returned to the container after
|
||||
* a dispatch, then the container will not commit the associated response
|
||||
* (unless an exception other than {@link ContinuationThrowable} is thrown).
|
||||
* </p>
|
||||
* <p>
|
||||
* When the thread calling the filter chain and/or servlet has returned to
|
||||
* the container with a suspended request, the thread is freed for other
|
||||
* tasks and the request is held until either:
|
||||
* <ul>
|
||||
* <li>a call to {@link ServletRequest#resume()}.</li>
|
||||
* <li>a call to {@link ServletRequest#complete()}.</li>
|
||||
* <li>the timeout expires.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Typically suspend with a response argument is uses when a call to {@link #complete()}
|
||||
* is expected. If a call to {@link #resume()} is expected, then the
|
||||
* {@link #suspend()} method should be used instead of this method.
|
||||
* </p>
|
||||
* <p>
|
||||
* Filters that may wrap the response object should check {@link #isResponseWrapped()}
|
||||
* to decide if they should destroy/finish the wrapper. If {@link #isResponseWrapped()}
|
||||
* returns true, then the wrapped request has been passed to the asynchronous
|
||||
* handler and the wrapper should not be destroyed/finished until after a call to
|
||||
* {@link #complete()} (potentially using a {@link ContinuationListener#onComplete(Continuation)}
|
||||
* listener).
|
||||
*
|
||||
* @param response The response to return via a call to {@link #getServletResponse()}
|
||||
* @exception IllegalStateException
|
||||
* If the request cannot be suspended
|
||||
*/
|
||||
void suspend(ServletResponse response);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Resume a suspended request.
|
||||
*
|
||||
* <p>
|
||||
* This method can be called by any thread that has been passed a reference
|
||||
* to a suspended request. When called the request is redispatched to the
|
||||
* normal filter chain and servlet processing.
|
||||
* to a continuation. When called the request is redispatched to the
|
||||
* normal filter chain and servlet processing with {@link #isInitial()} false.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If resume is called before a suspended request is returned to the
|
||||
* container (ie the thread that called {@link #suspend(long)} is still
|
||||
* within the filter chain and/or servlet service method), then the resume
|
||||
* does not take effect until the call to the filter chain and/or servlet
|
||||
* returns to the container. In this case both {@link #isSuspended()} and
|
||||
* {@link isResumed()} return true.
|
||||
* {@link isResumed()} return true. Multiple calls to resume are ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Multiple calls to resume are ignored
|
||||
* Typically resume() is used after a call to {@link #suspend()} with
|
||||
* no arguments. The dispatch after a resume call will use the original
|
||||
* request and response objects, even if {@link #suspend(ServletResponse)}
|
||||
* had been passed a wrapped response.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #suspend()}
|
||||
* @exception IllegalStateException
|
||||
* if the request is not suspended.
|
||||
* @exception IllegalStateException if the request is not suspended.
|
||||
*
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Complete a suspended request.
|
||||
*
|
||||
|
@ -151,11 +279,20 @@ public interface Continuation
|
|||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Typically resume() is used after a call to {@link #suspend(ServletResponse)} with
|
||||
* a possibly wrapped response. The async handler should use the response
|
||||
* provided by {@link #getServletResponse()} to write the response before
|
||||
* calling {@link #complete()}. If the request was suspended with a
|
||||
* call to {@link #suspend()} then no response object will be available via
|
||||
* {@link #getServletResponse()}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Once complete has been called and any thread calling the filter chain
|
||||
* and/or servlet chain has returned to the container, the request lifecycle
|
||||
* is complete. The container is able to recycle request objects, so it is
|
||||
* not valid hold a request reference after the end of the life cycle or to
|
||||
* call any request methods.
|
||||
* not valid hold a request or continuation reference after the end of the
|
||||
* life cycle.
|
||||
*
|
||||
* @see {@link #suspend()}
|
||||
* @exception IllegalStateException
|
||||
|
@ -164,6 +301,7 @@ public interface Continuation
|
|||
*/
|
||||
void complete();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true after {@link #suspend(long)} has been called and before the
|
||||
* request has been redispatched due to being resumed, completed or
|
||||
|
@ -171,6 +309,7 @@ public interface Continuation
|
|||
*/
|
||||
boolean isSuspended();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true if the request has been redispatched by a call to
|
||||
* {@link #resume()}. Returns false after any subsequent call to
|
||||
|
@ -178,12 +317,14 @@ public interface Continuation
|
|||
*/
|
||||
boolean isResumed();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true after a request has been redispatched as the result of a
|
||||
* timeout. Returns false after any subsequent call to suspend.
|
||||
*/
|
||||
boolean isExpired();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true while the request is within the initial dispatch to the
|
||||
* filter chain and/or servlet. Will return false once the calling
|
||||
|
@ -192,20 +333,82 @@ public interface Continuation
|
|||
*/
|
||||
boolean isInitial();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return True if {@link #keepWrappers()} has been called.
|
||||
* Is the suspended response wrapped.
|
||||
* <p>
|
||||
* Filters that wrap the response object should check this method to
|
||||
* determine if they should destroy/finish the wrapped response. If
|
||||
* the request was suspended with a call to {@link #suspend(ServletResponse)}
|
||||
* that passed the wrapped response, then the filter should register
|
||||
* a {@link ContinuationListener} to destroy/finish the wrapped response
|
||||
* during a call to {@link ContinuationListener#onComplete(Continuation)}.
|
||||
* @return True if {@link #suspend(ServletResponse)} has been passed a
|
||||
* {@link ServletResponseWrapper} instance.
|
||||
*/
|
||||
boolean isResponseWrapped();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Get the suspended response.
|
||||
* @return the {@link ServletResponse} passed to {@link #suspend(ServletResponse)}.
|
||||
*/
|
||||
ServletResponse getServletResponse();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Add a ContinuationListener.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
void addContinuationListener(ContinuationListener listener);
|
||||
|
||||
public void removeAttribute(String name);
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set a request attribute.
|
||||
* This method is a convenience method to call the {@link ServletRequest#setAttribute(String, Object)}
|
||||
* method on the associated request object.
|
||||
* This is a thread safe call and may be called by any thread.
|
||||
* @param name the attribute name
|
||||
* @param attribute the attribute value
|
||||
*/
|
||||
public void setAttribute(String name, Object attribute);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get a request attribute.
|
||||
* This method is a convenience method to call the {@link ServletRequest#getAttribute(String)}
|
||||
* method on the associated request object.
|
||||
* This is a thread safe call and may be called by any thread.
|
||||
* @param name the attribute name
|
||||
* @return the attribute value
|
||||
*/
|
||||
public Object getAttribute(String name);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remove a request attribute.
|
||||
* This method is a convenience method to call the {@link ServletRequest#removeAttribute(String)}
|
||||
* method on the associated request object.
|
||||
* This is a thread safe call and may be called by any thread.
|
||||
* @param name the attribute name
|
||||
*/
|
||||
public void removeAttribute(String name);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Undispatch the request.
|
||||
* <p>
|
||||
* This method can be called on a suspended continuation in order
|
||||
* to exit the dispatch to the filter/servlet by throwing a {@link ContinuationThrowable}
|
||||
* which is caught either by the container or the {@link ContinuationFilter}.
|
||||
* This is an alternative to simply returning from the dispatch in the case
|
||||
* where filters in the filter chain may not be prepared to handle a suspended
|
||||
* request.
|
||||
* </p>
|
||||
* This method should only be used as a last resort and a normal return is a prefereable
|
||||
* solution if filters can be updated to handle that case.
|
||||
*
|
||||
* @throws ContinuationThrowable thrown if the request is suspended. The instance of the
|
||||
* exception may be reused on subsequent calls, so the stack frame may not be accurate.
|
||||
*/
|
||||
public void undispatch() throws ContinuationThrowable;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class ContinuationFilter implements Filter
|
|||
{
|
||||
if (_faux)
|
||||
{
|
||||
final FauxContinuation fc = new FauxContinuation(request);
|
||||
final FauxContinuation fc = new FauxContinuation(request,_debug);
|
||||
request.setAttribute(Continuation.ATTRIBUTE,fc);
|
||||
boolean complete=false;
|
||||
|
||||
|
@ -74,6 +74,10 @@ public class ContinuationFilter implements Filter
|
|||
{
|
||||
chain.doFilter(request,response);
|
||||
}
|
||||
catch (ContinuationThrowable e)
|
||||
{
|
||||
debug("faux",e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
complete=fc.handleSuspension();
|
||||
|
@ -99,13 +103,27 @@ public class ContinuationFilter implements Filter
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
chain.doFilter(request,response);
|
||||
}
|
||||
catch (ContinuationThrowable e)
|
||||
{
|
||||
debug("caught",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(String string, Exception e)
|
||||
private void debug(String string, Throwable th)
|
||||
{
|
||||
if (_debug)
|
||||
_context.log("DEBUG",e);
|
||||
{
|
||||
if (th instanceof ContinuationThrowable)
|
||||
_context.log("DEBUG: "+th);
|
||||
else
|
||||
_context.log("DEBUG",th);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
|
||||
package org.eclipse.jetty.continuation;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** ContinuationThrowable
|
||||
* <p>
|
||||
* A ContinuationThrowable is throw by {@link Continuation#undispatch()}
|
||||
* in order to exit the dispatch to a Filter or Servlet. Use of
|
||||
* ContinuationThrowable is discouraged and it is preferable to
|
||||
* allow return to be used. ContinuationThrowables should only be
|
||||
* used when there is a Filter/Servlet which cannot be modified
|
||||
* to avoid committing a response when {@link Continuation#isSuspended()}
|
||||
* is true.
|
||||
* </p>
|
||||
* <p>
|
||||
* ContinuationThrowable instances are often reused so that the
|
||||
* stack trace may be entirely unrelated to the calling stack.
|
||||
* A real stack trace may be obtained by enabling debug.
|
||||
* </p>
|
||||
* <p>
|
||||
* ContinuationThrowable extends Error as this is more likely
|
||||
* to be uncaught (or rethrown) by a Filter/Servlet. A ContinuationThrowable
|
||||
* does not represent and error condition.
|
||||
* </p>
|
||||
*/
|
||||
public class ContinuationThrowable extends Error
|
||||
{}
|
|
@ -22,6 +22,8 @@ import javax.servlet.ServletResponseWrapper;
|
|||
|
||||
class FauxContinuation implements Continuation
|
||||
{
|
||||
private final static ContinuationThrowable __exception = new ContinuationThrowable();
|
||||
|
||||
private static final int __HANDLING=1; // Request dispatched to filter/servlet
|
||||
private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container
|
||||
private static final int __RESUMING=3; // resumed while suspending
|
||||
|
@ -39,12 +41,14 @@ class FauxContinuation implements Continuation
|
|||
private boolean _timeout=false;
|
||||
private boolean _responseWrapped=false;
|
||||
private long _timeoutMs=30000; // TODO configure
|
||||
private boolean _debug;
|
||||
|
||||
private ArrayList<ContinuationListener> _listeners;
|
||||
|
||||
FauxContinuation(final ServletRequest request)
|
||||
FauxContinuation(final ServletRequest request, boolean debug)
|
||||
{
|
||||
_request=request;
|
||||
_debug=debug;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -457,5 +461,14 @@ class FauxContinuation implements Continuation
|
|||
_request.setAttribute(name,attribute);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.continuation.Continuation#undispatch()
|
||||
*/
|
||||
public void undispatch()
|
||||
{
|
||||
if (isSuspended())
|
||||
throw _debug?new ContinuationThrowable():__exception;
|
||||
throw new IllegalStateException("!suspended");
|
||||
}
|
||||
}
|
|
@ -159,6 +159,25 @@ public class Jetty6Continuation implements ContinuationFilter.PartialContinuatio
|
|||
return _responseWrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.continuation.Continuation#undispatch()
|
||||
*/
|
||||
public void undispatch()
|
||||
{
|
||||
Throwable th=_retry;
|
||||
if (th instanceof ThreadDeath)
|
||||
throw (ThreadDeath)th;
|
||||
if (th instanceof Error)
|
||||
throw (Error)th;
|
||||
if (th instanceof RuntimeException)
|
||||
throw (RuntimeException)th;
|
||||
|
||||
throw new IllegalStateException("!suspended");
|
||||
}
|
||||
|
||||
public boolean enter()
|
||||
{
|
||||
_expired=!_j6Continuation.isResumed();
|
||||
|
|
|
@ -13,6 +13,8 @@ import javax.servlet.ServletResponseWrapper;
|
|||
|
||||
public class Servlet3Continuation implements Continuation
|
||||
{
|
||||
private final static ContinuationThrowable __exception = new ContinuationThrowable();
|
||||
|
||||
private final ServletRequest _request;
|
||||
private ServletResponse _response;
|
||||
private AsyncContext _context;
|
||||
|
@ -160,4 +162,16 @@ public class Servlet3Continuation implements Continuation
|
|||
{
|
||||
_request.setAttribute(name,attribute);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.continuation.Continuation#undispatch()
|
||||
*/
|
||||
public void undispatch()
|
||||
{
|
||||
if (isSuspended())
|
||||
throw __exception;
|
||||
throw new IllegalStateException("!suspended");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import javax.servlet.ServletRequest;
|
|||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletResponseWrapper;
|
||||
|
||||
import org.eclipse.jetty.continuation.ContinuationThrowable;
|
||||
import org.eclipse.jetty.continuation.ContinuationListener;
|
||||
import org.eclipse.jetty.continuation.Continuation;
|
||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||
|
@ -34,28 +35,31 @@ import org.eclipse.jetty.util.thread.Timeout;
|
|||
*/
|
||||
public class AsyncContinuation implements AsyncContext, Continuation
|
||||
{
|
||||
private final static ContinuationThrowable __exception = new ContinuationThrowable();
|
||||
|
||||
// STATES:
|
||||
// handling() suspend() unhandle() resume() complete() doComplete()
|
||||
// startAsync() dispatch()
|
||||
// IDLE DISPATCHED
|
||||
// DISPATCHED ASYNCSTARTED UNCOMPLETED
|
||||
// ASYNCSTARTED ASYNCWAIT REDISPATCHING COMPLETING
|
||||
// REDISPATCHING REDISPATCHED
|
||||
// ASYNCWAIT REDISPATCH COMPLETING
|
||||
// REDISPATCH REDISPATCHED
|
||||
// REDISPATCHED ASYNCSTARTED UNCOMPLETED
|
||||
// COMPLETING UNCOMPLETED UNCOMPLETED
|
||||
// UNCOMPLETED COMPLETED
|
||||
// COMPLETED
|
||||
private static final int __IDLE=0; // Idle request
|
||||
private static final int __DISPATCHED=1; // Request dispatched to filter/servlet
|
||||
private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container
|
||||
private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container
|
||||
private static final int __REDISPATCHING=3;// resumed while dispatched
|
||||
private static final int __SUSPENDED=4; // Suspended and parked
|
||||
private static final int __UNSUSPENDING=5; // Has been scheduled
|
||||
private static final int __ASYNCWAIT=4; // Suspended and parked
|
||||
private static final int __REDISPATCH=5; // Has been scheduled
|
||||
private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
|
||||
private static final int __COMPLETING=7; // complete while dispatched
|
||||
private static final int __UNCOMPLETED=8; // Request is completable
|
||||
private static final int __COMPLETE=9; // Request is complete
|
||||
|
||||
// State table
|
||||
// __HANDLE __UNHANDLE __SUSPEND __REDISPATCH
|
||||
// IDLE */ { __DISPATCHED, __Illegal, __Illegal, __Illegal },
|
||||
// DISPATCHED */ { __Illegal, __UNCOMPLETED, __SUSPENDING, __Ignore },
|
||||
// SUSPENDING */ { __Illegal, __SUSPENDED, __Illegal,__REDISPATCHING },
|
||||
// REDISPATCHING */ { __Illegal, _REDISPATCHED, __Ignored, __Ignore },
|
||||
// COMPLETING */ { __Illegal, __UNCOMPLETED, __Illegal, __Illegal },
|
||||
// SUSPENDED */ { __REDISPATCHED, __Illegal, __Illegal, __UNSUSPENDING },
|
||||
// UNSUSPENDING */ { __REDISPATCHED, __Illegal, __Illegal, __Ignore },
|
||||
// REDISPATCHED */ { __Illegal, __UNCOMPLETED, __SUSPENDING, __Ignore },
|
||||
private static final int __COMPLETED=9; // Request is complete
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -169,10 +173,10 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
{
|
||||
switch(_state)
|
||||
{
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
case __REDISPATCHING:
|
||||
case __COMPLETING:
|
||||
case __SUSPENDED:
|
||||
case __ASYNCWAIT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -198,14 +202,14 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
return
|
||||
((_state==__IDLE)?"IDLE":
|
||||
(_state==__DISPATCHED)?"DISPATCHED":
|
||||
(_state==__SUSPENDING)?"SUSPENDING":
|
||||
(_state==__SUSPENDED)?"SUSPENDED":
|
||||
(_state==__ASYNCSTARTED)?"ASYNCSTARTED":
|
||||
(_state==__ASYNCWAIT)?"ASYNCWAIT":
|
||||
(_state==__REDISPATCHING)?"REDISPATCHING":
|
||||
(_state==__UNSUSPENDING)?"UNSUSPENDING":
|
||||
(_state==__REDISPATCH)?"REDISPATCH":
|
||||
(_state==__REDISPATCHED)?"REDISPATCHED":
|
||||
(_state==__COMPLETING)?"COMPLETING":
|
||||
(_state==__UNCOMPLETED)?"UNCOMPLETED":
|
||||
(_state==__COMPLETE)?"COMPLETE":
|
||||
(_state==__COMPLETED)?"COMPLETE":
|
||||
("UNKNOWN?"+_state))+
|
||||
(_initial?",initial":"")+
|
||||
(_resumed?",resumed":"")+
|
||||
|
@ -230,7 +234,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
{
|
||||
case __DISPATCHED:
|
||||
case __REDISPATCHED:
|
||||
case __COMPLETE:
|
||||
case __COMPLETED:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case __IDLE:
|
||||
|
@ -238,7 +242,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
_state=__DISPATCHED;
|
||||
return true;
|
||||
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
case __REDISPATCHING:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
|
@ -246,10 +250,10 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
_state=__UNCOMPLETED;
|
||||
return false;
|
||||
|
||||
case __SUSPENDED:
|
||||
case __ASYNCWAIT:
|
||||
return false;
|
||||
|
||||
case __UNSUSPENDING:
|
||||
case __REDISPATCH:
|
||||
_state=__REDISPATCHED;
|
||||
return true;
|
||||
|
||||
|
@ -286,20 +290,20 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
{
|
||||
case __DISPATCHED:
|
||||
case __REDISPATCHED:
|
||||
_state=__SUSPENDING;
|
||||
_state=__ASYNCSTARTED;
|
||||
return;
|
||||
|
||||
case __IDLE:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
case __REDISPATCHING:
|
||||
return;
|
||||
|
||||
case __COMPLETING:
|
||||
case __SUSPENDED:
|
||||
case __UNSUSPENDING:
|
||||
case __COMPLETE:
|
||||
case __ASYNCWAIT:
|
||||
case __REDISPATCH:
|
||||
case __COMPLETED:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
default:
|
||||
|
@ -331,11 +335,11 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
case __IDLE:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
_initial=false;
|
||||
_state=__SUSPENDED;
|
||||
_state=__ASYNCWAIT;
|
||||
scheduleTimeout(); // could block and change state.
|
||||
if (_state==__SUSPENDED)
|
||||
if (_state==__ASYNCWAIT)
|
||||
return true;
|
||||
else if (_state==__COMPLETING)
|
||||
{
|
||||
|
@ -356,8 +360,8 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
_state=__UNCOMPLETED;
|
||||
return true;
|
||||
|
||||
case __SUSPENDED:
|
||||
case __UNSUSPENDING:
|
||||
case __ASYNCWAIT:
|
||||
case __REDISPATCH:
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
|
@ -378,22 +382,22 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
case __IDLE:
|
||||
case __REDISPATCHING:
|
||||
case __COMPLETING:
|
||||
case __COMPLETE:
|
||||
case __COMPLETED:
|
||||
case __UNCOMPLETED:
|
||||
return;
|
||||
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
_state=__REDISPATCHING;
|
||||
_resumed=true;
|
||||
return;
|
||||
|
||||
case __SUSPENDED:
|
||||
case __ASYNCWAIT:
|
||||
dispatch=!_expired;
|
||||
_state=__UNSUSPENDING;
|
||||
_state=__REDISPATCH;
|
||||
_resumed=true;
|
||||
break;
|
||||
|
||||
case __UNSUSPENDING:
|
||||
case __REDISPATCH:
|
||||
return;
|
||||
|
||||
default:
|
||||
|
@ -417,8 +421,8 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
// _history.append('E');
|
||||
switch(_state)
|
||||
{
|
||||
case __SUSPENDING:
|
||||
case __SUSPENDED:
|
||||
case __ASYNCSTARTED:
|
||||
case __ASYNCWAIT:
|
||||
listeners=_listeners;
|
||||
break;
|
||||
default:
|
||||
|
@ -453,8 +457,8 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
// _history.append('e');
|
||||
switch(_state)
|
||||
{
|
||||
case __SUSPENDING:
|
||||
case __SUSPENDED:
|
||||
case __ASYNCSTARTED:
|
||||
case __ASYNCWAIT:
|
||||
dispatch();
|
||||
}
|
||||
}
|
||||
|
@ -476,21 +480,21 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
switch(_state)
|
||||
{
|
||||
case __IDLE:
|
||||
case __COMPLETE:
|
||||
case __COMPLETED:
|
||||
case __REDISPATCHING:
|
||||
case __COMPLETING:
|
||||
case __UNSUSPENDING:
|
||||
case __REDISPATCH:
|
||||
return;
|
||||
|
||||
case __DISPATCHED:
|
||||
case __REDISPATCHED:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
_state=__COMPLETING;
|
||||
return;
|
||||
|
||||
case __SUSPENDED:
|
||||
case __ASYNCWAIT:
|
||||
_state=__COMPLETING;
|
||||
dispatch=!_expired;
|
||||
break;
|
||||
|
@ -521,7 +525,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
switch(_state)
|
||||
{
|
||||
case __UNCOMPLETED:
|
||||
_state=__COMPLETE;
|
||||
_state=__COMPLETED;
|
||||
listeners=_listeners;
|
||||
break;
|
||||
|
||||
|
@ -670,7 +674,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _state==__COMPLETE;
|
||||
return _state==__COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -682,10 +686,10 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
{
|
||||
switch(_state)
|
||||
{
|
||||
case __SUSPENDING:
|
||||
case __ASYNCSTARTED:
|
||||
case __REDISPATCHING:
|
||||
case __UNSUSPENDING:
|
||||
case __SUSPENDED:
|
||||
case __REDISPATCH:
|
||||
case __ASYNCWAIT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -878,6 +882,22 @@ public class AsyncContinuation implements AsyncContext, Continuation
|
|||
_connection.getRequest().setAttribute(name,attribute);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.continuation.Continuation#undispatch()
|
||||
*/
|
||||
public void undispatch()
|
||||
{
|
||||
if (isSuspended())
|
||||
{
|
||||
if (Log.isDebugEnabled())
|
||||
throw new ContinuationThrowable();
|
||||
else
|
||||
throw __exception;
|
||||
}
|
||||
throw new IllegalStateException("!suspended");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
public class AsyncEventState
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.servlet.ServletInputStream;
|
|||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.continuation.ContinuationThrowable;
|
||||
import org.eclipse.jetty.http.AbstractGenerator;
|
||||
import org.eclipse.jetty.http.EncodedHttpURI;
|
||||
import org.eclipse.jetty.http.Generator;
|
||||
|
@ -547,9 +548,13 @@ public class HttpConnection implements Connection
|
|||
server.handleAsync(this);
|
||||
}
|
||||
}
|
||||
catch (ContinuationThrowable e)
|
||||
{
|
||||
Log.debug(e);
|
||||
}
|
||||
catch (EofException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
Log.debug(e);
|
||||
error=true;
|
||||
}
|
||||
catch (HttpException e)
|
||||
|
|
|
@ -34,6 +34,7 @@ import javax.servlet.UnavailableException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.continuation.ContinuationThrowable;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.HttpException;
|
||||
|
@ -493,6 +494,10 @@ public class ServletHandler extends ScopedHandler
|
|||
else
|
||||
if(Log.isDebugEnabled())Log.debug("Response already committed for handling "+th);
|
||||
}
|
||||
catch(ContinuationThrowable e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch(Error e)
|
||||
{
|
||||
if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
|
||||
|
|
|
@ -136,6 +136,26 @@ public abstract class ContinuationBase extends TestCase
|
|||
assertEquals(1,count(response,"history: onComplete"));
|
||||
assertContains("TIMEOUT",response);
|
||||
|
||||
|
||||
response=process("suspend=200&resume=10&undispatch=true",null);
|
||||
assertContains("RESUMED",response);
|
||||
assertNotContains("history: onTimeout",response);
|
||||
assertContains("history: onComplete",response);
|
||||
|
||||
response=process("suspend=200&resume=0&undispatch=true",null);
|
||||
assertContains("RESUMED",response);
|
||||
assertNotContains("history: onTimeout",response);
|
||||
assertContains("history: onComplete",response);
|
||||
|
||||
response=process("suspend=200&complete=10&undispatch=true",null);
|
||||
assertContains("COMPLETED",response);
|
||||
assertNotContains("history: onTimeout",response);
|
||||
assertContains("history: onComplete",response);
|
||||
|
||||
response=process("suspend=200&complete=0&undispatch=true",null);
|
||||
assertContains("COMPLETED",response);
|
||||
assertNotContains("history: onTimeout",response);
|
||||
assertContains("history: onComplete",response);
|
||||
}
|
||||
|
||||
|
||||
|
@ -218,6 +238,7 @@ public abstract class ContinuationBase extends TestCase
|
|||
long resume2_after=-1;
|
||||
long complete_after=-1;
|
||||
long complete2_after=-1;
|
||||
boolean undispatch=false;
|
||||
|
||||
if (request.getParameter("read")!=null)
|
||||
read_before=Integer.parseInt(request.getParameter("read"));
|
||||
|
@ -235,6 +256,8 @@ public abstract class ContinuationBase extends TestCase
|
|||
complete_after=Integer.parseInt(request.getParameter("complete"));
|
||||
if (request.getParameter("complete2")!=null)
|
||||
complete2_after=Integer.parseInt(request.getParameter("complete2"));
|
||||
if (request.getParameter("undispatch")!=null)
|
||||
undispatch=Boolean.parseBoolean(request.getParameter("undispatch"));
|
||||
|
||||
if (continuation.isInitial())
|
||||
{
|
||||
|
@ -308,6 +331,9 @@ public abstract class ContinuationBase extends TestCase
|
|||
((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
if (undispatch)
|
||||
continuation.undispatch();
|
||||
}
|
||||
else if (sleep_for>=0)
|
||||
{
|
||||
|
@ -387,6 +413,8 @@ public abstract class ContinuationBase extends TestCase
|
|||
((HttpServletResponse)response).addHeader("history","resume");
|
||||
continuation.resume();
|
||||
}
|
||||
if (undispatch)
|
||||
continuation.undispatch();
|
||||
return;
|
||||
}
|
||||
else if (continuation.isExpired())
|
||||
|
|
Loading…
Reference in New Issue