/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2017-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.servlet;

/**
 * Class representing the execution context for an asynchronous operation
 * that was initiated on a ServletRequest.
 *
 * <p>An AsyncContext is created and initialized by a call to
 * {@link ServletRequest#startAsync()} or
 * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
 * Repeated invocations of these methods will return the same AsyncContext
 * instance, reinitialized as appropriate.
 *
 * <p>In the event that an asynchronous operation has timed out, the
 * container must run through these steps:
 * <ol>
 * <li>Invoke, at their {@link AsyncListener#onTimeout onTimeout} method, all
 * {@link AsyncListener} instances registered with the ServletRequest
 * on which the asynchronous operation was initiated.</li>
 * <li>If none of the listeners called {@link #complete} or any of the
 * {@link #dispatch} methods, perform an error dispatch with a status code
 * equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>.</li>
 * <li>If no matching error page was found, or the error page did not call
 * {@link #complete} or any of the {@link #dispatch} methods, call
 * {@link #complete}.</li>
 * </ol>
 *
 * @since Servlet 3.0
 */
public interface AsyncContext {

    /**
     * The name of the request attribute under which the original
     * request URI is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)} 
     */
    static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";

    /**
     * The name of the request attribute under which the original
     * context path is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)} 
     */
    static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";

    /**
     * The name of the request attribute under which the original
     * {@link javax.servlet.http.HttpServletMapping} is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)} 
     */
    static final String ASYNC_MAPPING = "javax.servlet.async.mapping";

    /**
     * The name of the request attribute under which the original
     * path info is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)} 
     */
    static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";

    /**
     * The name of the request attribute under which the original
     * servlet path is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}  
     */
    static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";

    /**
     * The name of the request attribute under which the original
     * query string is made available to the target of a
     * {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)} 
     */
    static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";


    /**
     * Gets the request that was used to initialize this AsyncContext
     * by calling {@link ServletRequest#startAsync()} or
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
     *
     * @return the request that was used to initialize this AsyncContext
     *
     * @exception IllegalStateException  if {@link #complete} or any of the
     *                                  {@link #dispatch} methods has been
     *                                  called in the asynchronous cycle
     */
    public ServletRequest getRequest();


    /**
     * Gets the response that was used to initialize this AsyncContext
     * by calling {@link ServletRequest#startAsync()} or
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
     *
     * @return the response that was used to initialize this AsyncContext
     *
     * @exception IllegalStateException  if {@link #complete} or any of the
     *                                  {@link #dispatch} methods has been
     *                                  called in the asynchronous cycle
     */
    public ServletResponse getResponse();


    /**
     * Checks if this AsyncContext was initialized with the original or
     * application-wrapped request and response objects.
     * 
     * <p>This information may be used by filters invoked in the
     * <i>outbound</i> direction, after a request was put into
     * asynchronous mode, to determine whether any request and/or response
     * wrappers that they added during their <i>inbound</i> invocation need
     * to be preserved for the duration of the asynchronous operation, or may
     * be released.
     *
     * @return true if this AsyncContext was initialized with the original
     * request and response objects by calling
     * {@link ServletRequest#startAsync()}, or if it was initialized by
     * calling
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
     * and neither the ServletRequest nor ServletResponse arguments 
     * carried any application-provided wrappers; false otherwise
     */
    public boolean hasOriginalRequestAndResponse();


    /**
     * Dispatches the request and response objects of this AsyncContext
     * to the servlet container.
     * 
     * <p>If the asynchronous cycle was started with
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
     * and the request passed is an instance of HttpServletRequest,
     * then the dispatch is to the URI returned by
     * {@link javax.servlet.http.HttpServletRequest#getRequestURI}.
     * Otherwise, the dispatch is to the URI of the request when it was
     * last dispatched by the container.
     *
     * <p>The following sequence illustrates how this will work:
     * <pre>{@code
     * // REQUEST dispatch to /url/A
     * AsyncContext ac = request.startAsync();
     * ...
     * ac.dispatch(); // ASYNC dispatch to /url/A
     * 
     * // REQUEST to /url/A
     * // FORWARD dispatch to /url/B
     * request.getRequestDispatcher("/url/B").forward(request,response);
     * // Start async operation from within the target of the FORWARD
     * // dispatch
     * ac = request.startAsync();
     * ...
     * ac.dispatch(); // ASYNC dispatch to /url/A
     * 
     * // REQUEST to /url/A
     * // FORWARD dispatch to /url/B
     * request.getRequestDispatcher("/url/B").forward(request,response);
     * // Start async operation from within the target of the FORWARD
     * // dispatch
     * ac = request.startAsync(request,response);
     * ...
     * ac.dispatch(); // ASYNC dispatch to /url/B
     * }</pre>
     *
     * <p>This method returns immediately after passing the request
     * and response objects to a container managed thread, on which the
     * dispatch operation will be performed.
     * If this method is called before the container-initiated dispatch
     * that called <tt>startAsync</tt> has returned to the container, the
     * dispatch operation will be delayed until after the container-initiated
     * dispatch has returned to the container.
     *
     * <p>The dispatcher type of the request is set to
     * <tt>DispatcherType.ASYNC</tt>. Unlike
     * {@link RequestDispatcher#forward(ServletRequest, ServletResponse)
     * forward dispatches}, the response buffer and
     * headers will not be reset, and it is legal to dispatch even if the
     * response has already been committed.
     *
     * <p>Control over the request and response is delegated
     * to the dispatch target, and the response will be closed when the
     * dispatch target has completed execution, unless
     * {@link ServletRequest#startAsync()} or
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}
     * are called.
     * 
     * <p>Any errors or exceptions that may occur during the execution
     * of this method must be caught and handled by the container, as
     * follows:
     * <ol>
     * <li>Invoke, at their {@link AsyncListener#onError onError} method, all
     * {@link AsyncListener} instances registered with the ServletRequest
     * for which this AsyncContext was created, and make the caught 
     * <tt>Throwable</tt> available via {@link AsyncEvent#getThrowable}.</li>
     * <li>If none of the listeners called {@link #complete} or any of the
     * {@link #dispatch} methods, perform an error dispatch with a status code
     * equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>, and
     * make the above <tt>Throwable</tt> available as the value of the
     * <tt>RequestDispatcher.ERROR_EXCEPTION</tt> request attribute.</li>
     * <li>If no matching error page was found, or the error page did not call
     * {@link #complete} or any of the {@link #dispatch} methods, call
     * {@link #complete}.</li>
     * </ol>
     *
     * <p>There can be at most one asynchronous dispatch operation per
     * asynchronous cycle, which is started by a call to one of the
     * {@link ServletRequest#startAsync} methods. Any attempt to perform an
     * additional asynchronous dispatch operation within the same
     * asynchronous cycle will result in an IllegalStateException.
     * If startAsync is subsequently called on the dispatched request,
     * then any of the dispatch or {@link #complete} methods may be called.
     *
     * @throws IllegalStateException if one of the dispatch methods
     * has been called and the startAsync method has not been
     * called during the resulting dispatch, or if {@link #complete}
     * was called
     *
     * @see ServletRequest#getDispatcherType
     */
    public void dispatch();


    /**
     * Dispatches the request and response objects of this AsyncContext
     * to the given <tt>path</tt>.
     *
     * <p>The <tt>path</tt> parameter is interpreted in the same way 
     * as in {@link ServletRequest#getRequestDispatcher(String)}, within
     * the scope of the {@link ServletContext} from which this
     * AsyncContext was initialized.
     *
     * <p>All path related query methods of the request must reflect the
     * dispatch target, while the original request URI, context path,
     * path info, servlet path, and query string may be recovered from
     * the {@link #ASYNC_REQUEST_URI}, {@link #ASYNC_CONTEXT_PATH},
     * {@link #ASYNC_PATH_INFO}, {@link #ASYNC_SERVLET_PATH}, and
     * {@link #ASYNC_QUERY_STRING} attributes of the request. These
     * attributes will always reflect the original path elements, even under
     * repeated dispatches.
     *
     * <p>There can be at most one asynchronous dispatch operation per
     * asynchronous cycle, which is started by a call to one of the
     * {@link ServletRequest#startAsync} methods. Any attempt to perform an
     * additional asynchronous dispatch operation within the same
     * asynchronous cycle will result in an IllegalStateException.
     * If startAsync is subsequently called on the dispatched request,
     * then any of the dispatch or {@link #complete} methods may be called.
     *
     * <p>See {@link #dispatch()} for additional details, including error
     * handling.
     *
     * @param path the path of the dispatch target, scoped to the
     * ServletContext from which this AsyncContext was initialized
     *
     * @throws IllegalStateException if one of the dispatch methods
     * has been called and the startAsync method has not been
     * called during the resulting dispatch, or if {@link #complete}
     * was called
     *
     * @see ServletRequest#getDispatcherType
     */
    public void dispatch(String path);


    /**
     * Dispatches the request and response objects of this AsyncContext
     * to the given <tt>path</tt> scoped to the given <tt>context</tt>.
     *
     * <p>The <tt>path</tt> parameter is interpreted in the same way 
     * as in {@link ServletRequest#getRequestDispatcher(String)}, except that
     * it is scoped to the given <tt>context</tt>.
     *
     * <p>All path related query methods of the request must reflect the
     * dispatch target, while the original request URI, context path,
     * path info, servlet path, and query string may be recovered from
     * the {@link #ASYNC_REQUEST_URI}, {@link #ASYNC_CONTEXT_PATH},
     * {@link #ASYNC_PATH_INFO}, {@link #ASYNC_SERVLET_PATH}, and
     * {@link #ASYNC_QUERY_STRING} attributes of the request. These
     * attributes will always reflect the original path elements, even under
     * repeated dispatches.
     *
     * <p>There can be at most one asynchronous dispatch operation per
     * asynchronous cycle, which is started by a call to one of the
     * {@link ServletRequest#startAsync} methods. Any attempt to perform an
     * additional asynchronous dispatch operation within the same
     * asynchronous cycle will result in an IllegalStateException.
     * If startAsync is subsequently called on the dispatched request,
     * then any of the dispatch or {@link #complete} methods may be called.
     *
     * <p>See {@link #dispatch()} for additional details, including error
     * handling.
     *
     * @param context the ServletContext of the dispatch target
     * @param path the path of the dispatch target, scoped to the given
     * ServletContext
     *
     * @throws IllegalStateException if one of the dispatch methods
     * has been called and the startAsync method has not been
     * called during the resulting dispatch, or if {@link #complete}
     * was called
     *
     * @see ServletRequest#getDispatcherType
     */
    public void dispatch(ServletContext context, String path);


    /**
     * Completes the asynchronous operation that was started on the request
     * that was used to initialze this AsyncContext, closing the response
     * that was used to initialize this AsyncContext.
     *
     * <p>Any listeners of type {@link AsyncListener} that were registered
     * with the ServletRequest for which this AsyncContext was created will
     * be invoked at their {@link AsyncListener#onComplete(AsyncEvent)
     * onComplete} method.
     *
     * <p>It is legal to call this method any time after a call to
     * {@link ServletRequest#startAsync()} or
     * {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
     * and before a call to one of the <tt>dispatch</tt> methods
     * of this class. 
     * If this method is called before the container-initiated dispatch
     * that called <tt>startAsync</tt> has returned to the container, then
     * the call will not take effect (and any invocations of
     * {@link AsyncListener#onComplete(AsyncEvent)} will be delayed) until
     * after the container-initiated dispatch has returned to the container.
     */
    public void complete();


    /**
     * Causes the container to dispatch a thread, possibly from a managed
     * thread pool, to run the specified <tt>Runnable</tt>. The container may
     * propagate appropriate contextual information to the <tt>Runnable</tt>. 
     *
     * @param run the asynchronous handler
     */
    public void start(Runnable run);


    /**
     * Registers the given {@link AsyncListener} with the most recent
     * asynchronous cycle that was started by a call to one of the
     * {@link ServletRequest#startAsync} methods.
     *
     * <p>The given AsyncListener will receive an {@link AsyncEvent} when
     * the asynchronous cycle completes successfully, times out, results
     * in an error, or a new asynchronous cycle is being initiated via
     * one of the {@link ServletRequest#startAsync} methods.
     *
     * <p>AsyncListener instances will be notified in the order in which
     * they were added.
     *
     * <p>If {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}
     * or {@link ServletRequest#startAsync} is called,
     * the exact same request and response objects are available from the
     * {@link AsyncEvent} when the {@link AsyncListener} is notified.
     *
     * @param listener the AsyncListener to be registered
     * 
     * @throws IllegalStateException if this method is called after
     * the container-initiated dispatch, during which one of the
     * {@link ServletRequest#startAsync} methods was called, has
     * returned to the container
     */
    public void addListener(AsyncListener listener);


    /**
     * Registers the given {@link AsyncListener} with the most recent
     * asynchronous cycle that was started by a call to one of the
     * {@link ServletRequest#startAsync} methods.
     *
     * <p>The given AsyncListener will receive an {@link AsyncEvent} when
     * the asynchronous cycle completes successfully, times out, results
     * in an error, or a new asynchronous cycle is being initiated via
     * one of the {@link ServletRequest#startAsync} methods.
     *
     * <p>AsyncListener instances will be notified in the order in which
     * they were added.
     *
     * <p>The given ServletRequest and ServletResponse objects will
     * be made available to the given AsyncListener via the
     * {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
     * {@link AsyncEvent#getSuppliedResponse getSuppliedResponse} methods,
     * respectively, of the {@link AsyncEvent} delivered to it. These objects
     * should not be read from or written to, respectively, at the time the
     * AsyncEvent is delivered, because additional wrapping may have
     * occurred since the given AsyncListener was registered, but may be used
     * in order to release any resources associated with them.
     *
     * @param listener the AsyncListener to be registered
     * @param servletRequest the ServletRequest that will be included
     * in the AsyncEvent
     * @param servletResponse the ServletResponse that will be included
     * in the AsyncEvent
     *
     * @throws IllegalStateException if this method is called after
     * the container-initiated dispatch, during which one of the
     * {@link ServletRequest#startAsync} methods was called, has
     * returned to the container
     */
    public void addListener(AsyncListener listener,
                            ServletRequest servletRequest,
                            ServletResponse servletResponse);


    /**
     * Instantiates the given {@link AsyncListener} class.
     *
     * <p>The returned AsyncListener instance may be further customized
     * before it is registered with this AsyncContext via a call to one of 
     * the <code>addListener</code> methods.
     *
     * <p>The given AsyncListener class must define a zero argument
     * constructor, which is used to instantiate it.
     *
     * <p>This method supports resource injection if the given
     * <tt>clazz</tt> represents a Managed Bean.
     * See the Java EE platform and JSR 299 specifications for additional
     * details about Managed Beans and resource injection.

     * <p>This method supports any annotations applicable to AsyncListener.
     *
     * @param <T> the class of the object to instantiate
     * @param clazz the AsyncListener class to instantiate
     *
     * @return the new AsyncListener instance
     *
     * @throws ServletException if the given <tt>clazz</tt> fails to be
     * instantiated
     */
    public <T extends AsyncListener> T createListener(Class<T> clazz)
        throws ServletException; 


    /**
     * Sets the timeout (in milliseconds) for this AsyncContext.
     *
     * <p>The timeout applies to this AsyncContext once the
     * container-initiated dispatch during which one of the
     * {@link ServletRequest#startAsync} methods was called has
     * returned to the container. 
     *
     * <p>The timeout will expire if neither the {@link #complete} method
     * nor any of the dispatch methods are called. A timeout value of
     * zero or less indicates no timeout. 
     * 
     * <p>If {@link #setTimeout} is not called, then the container's
     * default timeout, which is available via a call to
     * {@link #getTimeout}, will apply.
     *
     * <p>The default value is <code>30000</code> ms.
     *
     * @param timeout the timeout in milliseconds
     *
     * @throws IllegalStateException if this method is called after
     * the container-initiated dispatch, during which one of the
     * {@link ServletRequest#startAsync} methods was called, has
     * returned to the container
     */
    public void setTimeout(long timeout);


    /**
     * Gets the timeout (in milliseconds) for this AsyncContext.
     *
     * <p>This method returns the container's default timeout for
     * asynchronous operations, or the timeout value passed to the most
     * recent invocation of {@link #setTimeout}.
     *
     * <p>A timeout value of zero or less indicates no timeout.
     *
     * @return the timeout in milliseconds
     */
    public long getTimeout();

}
