001    package org.rakeshv.filters;
002    
003    import java.io.IOException;
004    import java.io.PrintWriter;
005    
006    import javax.servlet.ServletOutputStream;
007    import javax.servlet.http.HttpServletResponse;
008    import javax.servlet.http.HttpServletResponseWrapper;
009    
010    /**
011     * A <code>HttpServletResponseWrapper</code> class that is used to
012     * create a <code>compressed</code> output stream.  This class
013     * over-rides appropriate methods in the parent class to create
014     * compressed OutputStreams and PrintWriters.
015     *
016     * <p><b>Note:</b> - It is <b>REQUIRED</b> that you set the value of
017     * the {@link #compressionType} instance variable using the mutator
018     * method {@link #setCompressionType( int )} if the compression is
019     * to work.</p>
020     *
021     * <p>&copy; Copyright 2003, Rakesh Vidyadharan</p>
022     * @author Rakesh Vidyadharan 2<sup><small>nd</small></sup> September 2003
023     *
024     * @version $Id: CompressionResponseWrapper.java,v 1.5 2005/09/17 03:53:05 rakesh Exp $
025     */
026    public class CompressionResponseWrapper 
027      extends HttpServletResponseWrapper 
028    {
029      /**
030       * Original response
031       */
032      protected HttpServletResponse servletResponse = null;
033    
034      /**
035       * The <code>ServletOutputStream</code> that has been returned by 
036       * {@link #getOutputStream()}, if any.
037       */
038      protected ServletOutputStream stream = null;
039    
040      /**
041       * The PrintWriter that has been returned by getWriter(), if any.
042       */
043      protected PrintWriter writer = null;
044    
045      /**
046       * An integer that indicates the type of compression that is to
047       * be used.  The different compression algorithms supported are
048       * the ones supported in the <code>java.util.zip</code> package.
049       */
050      protected int compressionType = -1;
051    
052      /**
053       * Constructs a compressed response adaptor wrapping the given 
054       * response.  Calls the <code>super-class</code> constructor, and
055       * set the {@link #servletResponse} reference.
056       *
057       * @param response - The response that is to
058       *   be compressed.
059       * @throws IllegalArgumentException - If the specified response is 
060       *   <code>null</code>.
061       */
062      public CompressionResponseWrapper( HttpServletResponse response )
063        throws IllegalArgumentException
064      {
065        super( response );
066        servletResponse = response;
067      }
068        
069      /**
070       * Create a new instance of the {@link CompressionResponseWrapper}
071       * class, that compresses the response.
072       *
073       * @return ServletOutputStream - The appropriate instance of the
074       *   ServletOutputStream that wraps a <code>DeflaterOutputStream
075       *   </code>.
076       * @throws IOException - If exceptions are encountered while
077       *   creating the compressed response stream.
078       */
079      public ServletOutputStream createOutputStream() throws IOException
080      {
081        CompressionResponseStream crs = 
082          new CompressionResponseStream( servletResponse );
083        crs.setCompressionType( compressionType );
084    
085        return crs;
086      }
087    
088      /**
089       * Forces any content in the buffer to be written to the client. A 
090       * call to this method automatically commits the response, meaning 
091       * the status code and headers will be written.
092       */
093      public void flushBuffer() throws IOException 
094      {
095        ( (CompressionResponseStream) stream ).flush();
096      }
097    
098      /**
099       * Returns a ServletOutputStream suitable for writing binary data in 
100       * the response. If a <code>Writer</code> has not already been
101       * created for the response (through a call to {@link #getWriter()}
102       * method) a new instance of the {@link CompressionResponseStream}
103       * is created, and the instance variable {@link #stream} set to the
104       * new instance created.  If the {@link #stream} has already been
105       * initialised, then the reference is returned.
106       *
107       * @throws IOException - If errors are encountered while creating
108       *   the compressed response stream.
109       * @throws IllegalStateException - If {@link #getWriter()} has
110       *   already been called on this instance.
111       */
112      public ServletOutputStream getOutputStream() throws IOException 
113      {
114        if ( writer != null ) 
115        {
116          throw new IllegalStateException( 
117              "getWriter() has already been called for this response" );
118        }
119    
120        if (stream == null) 
121        {
122          stream = createOutputStream();
123        }
124    
125        return stream;
126      }
127    
128      /**
129       * Returns a PrintWriter object that can send character text to the 
130       * client. The PrintWriter is created on top of the appropriate
131       * {@link CompressionResponseStream} class.  The character encoding 
132       * used is the one specified in the charset= property of the 
133       * <code>setContentType( java.lang.String )</code> method, which must 
134       * be called before calling this method for the charset to take 
135       * effect. 
136       *
137       * <p>If necessary, the MIME type of the response is modified to 
138       * reflect the character encoding used.</p>
139       *
140       * <p>Either this method or {@link #getOutputStream()} may be called 
141       * to write the body, not both.
142       *
143       * @throws IOException - If errors are encountered while creating
144       *   the compressed response stream.
145       * @throws IllegalStateException - If {@link #getWriter()} has
146       *   already been called on this instance.
147       */
148      public PrintWriter getWriter() throws IOException 
149      {
150        if ( writer != null ) 
151        {
152          return writer;
153        }
154    
155        if ( stream != null ) 
156        {
157          throw new IllegalStateException( 
158              "getOutputStream() has already been called for this response" );
159        }
160    
161        stream = createOutputStream();
162        writer = new PrintWriter( stream );
163        return writer;
164      }
165    
166      /**
167       * Finishes writing compressed data to the {@link 
168       * CompressionResponseStream} output stream without 
169       * closing the underlying stream. 
170       *
171       * <p><b>Note:</b> This method must be invoked by to properly finish
172       * the output if multiple filters are added in succession to the 
173       * same output stream.  This is generally the case, and hence this
174       * method <b>must</b> be called in order for the response to be
175       * completely sent back to the client.</p>
176       *
177       * @throws IOException - If errors are encountered while writing
178       *   to the stream.  An exception will be thrown if this method is
179       *   invoked after the stream has been closed.
180       */
181      public void finish() throws IOException
182      {
183                    if ( stream != null )
184                    {
185                            ( (CompressionResponseStream) stream ).finish();
186                    }
187      }
188      
189      /**
190       * Returns {@link #compressionType}.
191       *
192       * @return int - The value/reference of/to compressionType.
193       */
194      public final int getCompressionType()
195      {
196        return compressionType;
197      }
198      
199      /**
200       * Set {@link #compressionType}.
201       *
202       * @param compressionType - The value to set.
203       */
204      public final void setCompressionType( int compressionType )
205      {
206        this.compressionType = compressionType;
207      }
208    }