content: package ro.isdc.wro.http.support;
  
  import static org.apache.commons.lang3.Validate.notNull;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  
  import javax.servlet.ServletOutputStream;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpServletResponseWrapper;
  
  import org.apache.commons.io.IOUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import ro.isdc.wro.model.resource.locator.UriLocator;
  import ro.isdc.wro.model.resource.locator.UrlUriLocator;
  
  
  /**
   * A specialized {@link HttpServletResponseWrapper} responsible for streaming response to provided {@link OutputStream}.
   *
   * @author Alex Objelean
   * @since 1.4.5
   * @created 18 Mar 2012
   */
  public class RedirectedStreamServletResponseWrapper
      extends HttpServletResponseWrapper {
    private static final Logger LOG = LoggerFactory.getLogger(RedirectedStreamServletResponseWrapper.class);
    /**
     * PrintWrapper of wrapped response.
     */
    private PrintWriter printWriter;
    /**
     * Servlet output stream of wrapped response.
     */
    private ServletOutputStream servletOutputStream;
    /**
     * Used to locate external resources.
     */
    private final UriLocator externalResourceLocator = newExternalResourceLocator();
  
    /**
     * @return {@link UriLocator} responsible for resolving external resources.
     */
    protected UriLocator newExternalResourceLocator() {
      return new UrlUriLocator().setEnableWildcards(false);
    }
  
    /**
     * @param outputStream
     *          the stream where the response will be written.
     * @param response
     *          decorated response.
     */
    public RedirectedStreamServletResponseWrapper(final OutputStream outputStream, final HttpServletResponse response) {
      super(response);
      notNull(outputStream);
      // Both servletOutputStream and PrintWriter must be overridden in order to be sure that dispatched servlet will write
      // to the pipe.
      printWriter = new PrintWriter(outputStream);
      servletOutputStream = new DelegatingServletOutputStream(outputStream);
    }
  
    @Override
    public void sendError(final int sc)
        throws IOException {
      onError(sc, "");
      super.sendError(sc);
    }
  
    @Override
    public void sendError(final int sc, final String msg)
        throws IOException {
      onError(sc, msg);
      super.sendError(sc, msg);
    }
  
    /**
     * Use an empty stream to avoid container writing unwanted message when a resource is missing.
     *
     * @param sc
     *          status code.
     * @param msg
     */
    private void onError(final int sc, final String msg) {
      LOG.debug("Error detected with code: {} and message: {}", sc, msg);
      final OutputStream emptyStream = new ByteArrayOutputStream();
      printWriter = new PrintWriter(emptyStream);
      servletOutputStream = new DelegatingServletOutputStream(emptyStream);
    }
  
    /**
     * By default, redirect does not allow writing to output stream its content. In order to support this use-case, we
     * need to open a new connection and read the content manually.
     */
    @Override
    public void sendRedirect(final String location)
        throws IOException {
      try {
        LOG.debug("redirecting to: {}", location);
        final InputStream is = externalResourceLocator.locate(location);
        IOUtils.copy(is, servletOutputStream);
        is.close();
        servletOutputStream.close();
      } catch (final IOException e) {
        LOG.warn("{}: Invalid response for location: {}", e.getClass().getName(), location);
        throw e;
      }
    }
  
    @Override
    public ServletOutputStream getOutputStream()
        throws IOException {
      return servletOutputStream;
    }
  
    @Override
    public void setStatus(final int sc) {
      super.setStatus(sc);
    }
  
    @Override
    public PrintWriter getWriter()
        throws IOException {
      return printWriter;
    }
  }
  