package org.civilian.internal.intercept; import java.io.FilterOutputStream; import java.io.Flushable; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import org.civilian.response.ResponseStreamInterceptor; /** * InterceptedOutputStream is a helper class to implement Response.flushBuffer(). * If the response output is not intercepted, then the response outputstream or writer * is the original servlet outputstream or writer (wrapped in a TemplateWriter). * In this case we just need to call resetBuffer on the underlying ServletResponse. * If the response output is intercepted then the user has obtained the * following chain of OutpuStreams (upto the InterceptedOutputStream) * or writer (upto the TemplateWriter) * <ol> * <li>ServletOutputStream * <li>InterceptedStream 1, e.g. GzipOutputStream, ... * <li>InterceptedStream n * <li>InterceptedOutputStream, returned by Response.getContentStream() * <li>OutputStreamWriter * <li>TemplateWriter, returned by Response.getContentWriter() * </ol> * In case of a resetBuffer() call we still forward it to ServletResponse.resetBuffer(). * But the writer and outputstream chain needs some handling: * <ul> * <li>Stations in the chain may have buffered data (e.g. OutputStreamWriter * may keep an encoding buffer, GzipOutputStream has a deflater buffer, ...). * <li>InterceptedStream may have written initialization data (e.g. GzipOutputStream * initialize writes a gzip header, which is lost when the buffer is reset). * </ul> * Therefore we do the following: * <ol> * <li>InterceptedOutputStream.out is temporarily set to a Nil outputstream. * <li>Any Writer on top of the InterceptedOutputStream is flushed (and the data goes into Nil) * <li>InterceptedOutputStream.out is reconstructed from the interceptor chain. * {@link ResponseStreamInterceptor#intercept(OutputStream)} may be called * multiple times. * </ol> */ public class InterceptedOutputStream extends FilterOutputStream implements InterceptedOutput { public InterceptedOutputStream(OutputStream originalStream, ResponseStreamInterceptor interceptor) throws IOException { super(RespStreamInterceptorChain.intercept(originalStream, interceptor)); originalStream_ = originalStream; interceptor_ = interceptor; } public void setWriter(Writer writer) { writer_ = writer; } @Override public void reset() { try { if (writer_ != null) { out = new Nil(); writer_.flush(); } out = RespStreamInterceptorChain.intercept(originalStream_, interceptor_); } catch(IOException e) { throw new IllegalStateException("could not reset response output", e); } } /** * An OutputStream that does nothing. */ private static class Nil extends OutputStream { @Override public void write(int b) { } @Override public void write(byte b[], int off, int len) { } } private OutputStream originalStream_; private ResponseStreamInterceptor interceptor_; private Flushable writer_; }