/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2013 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
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/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 packager/legal/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 org.glassfish.jersey.server.spi;

import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ContainerException;
import org.glassfish.jersey.server.ContainerResponse;

/**
 * A suspendable, request-scoped I/O container response writer.
 *
 * I/O container sends a new instance of the response writer with every request as part
 * of the call to the Jersey application
 * {@link ApplicationHandler#apply(org.glassfish.jersey.server.ContainerRequest)}  apply(...)}
 * method. Each container response writer represents an open connection to the client
 * (waiting for a response).
 * <p>
 * For each request the Jersey runtime will make sure to directly call either
 * {@link #suspend(long, TimeUnit, TimeoutHandler) suspend(...)}
 * or {@code commit()} method on a response writer supplied to the
 * {@code JerseyApplication.apply(...)} method before the method has finished. Therefore
 * the I/O container implementations may assume that when the {@code JerseyApplication.apply(...)}
 * method is finished, the container response writer is either {@link #commit() commited},
 * or {@link #suspend(long, TimeUnit, TimeoutHandler) suspended}.
 * </p>
 *
 * @author Marek Potociar
 */
public interface ContainerResponseWriter {

    /**
     * Time-out handler can be registered when the container response writer gets
     * suspended.
     *
     * Should the suspend operation time out, the container is responsible for
     * invoking the {@link TimeoutHandler#onTimeout(ContainerResponseWriter)}
     * callback method to get the response that should be returned to the client.
     */
    public interface TimeoutHandler {

        /**
         * Method is called, when {@link ContainerResponseWriter#suspend(long, TimeUnit,
         * ContainerResponseWriter.TimeoutHandler) ContainerResponseWriter.suspend(...)}
         * operation times out.
         *
         * The custom time-out handler implementation is responsible for making
         * sure a (time-out) response is written to the context and that the
         * container context is properly closed.
         *
         * @param responseWriter suspended container response writer that timed out.
         */
        public void onTimeout(ContainerResponseWriter responseWriter);
    }

    /**
     * Write the status and headers of the response and return an output stream
     * for the web application to write the entity of the response.
     * <p>
     * If the response content length is declared to be greater or equal to 0, it
     * means that the content length in bytes of the entity to be written is known,
     * otherwise -1. I/O containers may use this value to determine whether the
     * {@code "Content-Length"} header can be set or utilize chunked transfer encoding.
     * </p>
     *
     * @param contentLength greater or equal to 0 if the content length in bytes
     *     of the entity to be written is known, otherwise -1. Containers
     *     may use this value to determine whether the {@code "Content-Length"}
     *     header can be set or utilize chunked transfer encoding.
     * @param responseContext the JAX-RS response to be written. The status and headers
     *     are obtained from the response.
     * @return the output stream to write the entity (if any).
     * @throws ContainerException if an error occurred when writing out the
     *                            status and headers or obtaining the output stream.
     */
    public OutputStream writeResponseStatusAndHeaders(long contentLength, ContainerResponse responseContext)
            throws ContainerException;

    /**
     * Suspend the request/response processing.
     *
     * The method returns {@code true} to indicate the response writer was suspended successfully.
     * In case the provider has already been suspended earlier, the method returns {@code false}.
     * <p>
     * I/O container must not automatically {@link #commit() commit} the response writer
     * when the processing on the I/O container thread is finished and the thread is
     * released. Instead, the Jersey runtime will make sure to manually close
     * the container response writer instance by explicitly calling the {@link #commit()}
     * or {@link #failure(Throwable)} method at some later point in time.
     * </p>
     * <p>
     * Once suspended, the specified suspend timeout can be further updated using
     * {@link #setSuspendTimeout(long, java.util.concurrent.TimeUnit) } method.
     * </p>
     *
     * @param timeOut        time-out value. Value less or equal to 0, indicates that
     *                       the processing is suspended indefinitely.
     * @param timeUnit       time-out time unit.
     * @param timeoutHandler time-out handler to process a time-out event if it
     *                       occurs.
     * @return {@code true} if the suspend operation completed successfully, {@code false} otherwise.
     *
     * @see #setSuspendTimeout(long, TimeUnit)
     * @see #commit()
     */
    public boolean suspend(long timeOut, TimeUnit timeUnit, TimeoutHandler timeoutHandler);

    /**
     * Set the suspend timeout.
     *
     * Once the container response writer is suspended, the suspend timeout value
     * can be further updated by the method.
     *
     * @param timeOut  time-out value. Value less or equal to 0, indicates that
     *                 the processing is suspended indefinitely.
     * @param timeUnit time-out time unit.
     * @throws IllegalStateException in case the response writer has not been suspended
     *                               yet.
     */
    public void setSuspendTimeout(long timeOut, TimeUnit timeUnit) throws IllegalStateException;

    /**
     * Commit the response & close the container response writer.
     *
     * Indicates to the I/O container that request has been fully processed and response
     * has been fully written. This signals the I/O  container to finish the request/response
     * processing, clean up any state, flush any streams, release resources etc.
     *
     * @see #suspend(long, TimeUnit, TimeoutHandler)
     * @see #failure(Throwable)
     */
    public void commit();

    /**
     * Propagate an unhandled error to the I/O container.
     *
     * Indicates to the I/O container that the request processing has finished with an error
     * that could not be processed by the Jersey runtime. The I/O container is expected to process
     * the exception in a container-specific way. This method also signals the I/O container to
     * finish the request/response processing, clean up any state, flush any streams, release
     * resources etc.
     *
     * @param error unhandled request processing error.
     *
     * @see #suspend(long, TimeUnit, TimeoutHandler)
     * @see #commit()
     */
    public void failure(Throwable error);

    /**
     * Return {@code true} if the entity buffering should be enabled in Jersey.
     *
     * If enabled, the outbound entity is buffered by Jersey runtime up to a configured amount of bytes
     * prior to being written to the output stream to determine its size that may be used to set the value
     * of HTTP <tt>{@value javax.ws.rs.core.HttpHeaders#CONTENT_LENGTH}</tt> header.
     * <p>
     * Containers that provide it's own solution for determining the message payload size may decide to
     * return {@code false} to prevent Jersey from buffering message entities unnecessarily.
     * </p>
     *
     * @return {@code true} to enable the entity buffering should by Jersey runtime, {@code false} otherwise.
     */
    public boolean enableResponseBuffering();
}
