001    package org.rakeshv.filters;
002    
003    import java.io.File;
004    import java.io.IOException;
005    import java.io.PrintWriter;
006    import java.util.Locale;
007    
008    import javax.servlet.ServletOutputStream;
009    import javax.servlet.http.HttpServletResponse;
010    import javax.servlet.http.HttpServletResponseWrapper;
011    
012    /**
013     * A <code>HttpServletResponseWrapper</code> class that is used to
014     * create a <code>cached</code> output stream.  This class
015     * over-rides appropriate methods in the parent class to create
016     * cached OutputStreams and PrintWriters.
017     *
018     * <p>&copy; Copyright 2005, Rakesh Vidyadharan</p>
019     * @author Rakesh Vidyadharan 15<sup><small>th</small></sup> September 2005
020     *
021     * @version $Id: CachingResponseWrapper.java,v 1.4 2005/10/16 02:19:57 rakesh Exp $
022     */
023    public class CachingResponseWrapper 
024      extends HttpServletResponseWrapper 
025    {
026      /**
027       * Original response
028       */
029      protected HttpServletResponse servletResponse = null;
030    
031      /**
032       * The <code>ServletOutputStream</code> that has been returned by 
033       * {@link #getOutputStream()}, if any.
034       */
035      protected ServletOutputStream stream = null;
036    
037      /**
038       * The PrintWriter that has been returned by getWriter(), if any.
039       */
040      protected PrintWriter writer = null;
041    
042      /**
043       * The file to which the cached contents are to be written.
044       */
045      protected File cacheFile = null;
046    
047      /**
048       * The {@link CachingFilter.FileProperties} that is to be
049       * modified if the content properties for the response are
050       * modified by objects down the filter chain.
051       */
052      private CachingFilter.FileProperties fileProperties;
053    
054      /**
055       * Constructs a cached response adaptor wrapping the given 
056       * response.  Calls the <code>super-class</code> constructor, and
057       * set the {@link #servletResponse} reference.
058       *
059       * @param response The response that is to be cached.
060       * @param file The file to which the contents are to be cached.
061       * @param fileProperties The <code>FileProperties</code> that will
062       *   be passed along to the {@link CachingResponseStream}.
063       * @throws IllegalArgumentException If the specified response is 
064       *   <code>null</code>.
065       */
066      public CachingResponseWrapper( HttpServletResponse response,
067          File file, CachingFilter.FileProperties fileProperties ) 
068        throws IllegalArgumentException
069      {
070        super( response );
071        servletResponse = response;
072        cacheFile = file;
073        this.fileProperties = fileProperties;
074      }
075        
076      /**
077       * Create a new instance of the {@link CachingResponseWrapper}
078       * class, that compresses the response.
079       *
080       * @return ServletOutputStream - The appropriate instance of the
081       *   ServletOutputStream that wraps a <code>DeflaterOutputStream
082       *   </code>.
083       * @throws IOException If exceptions are encountered while
084       *   creating the cached response stream.
085       */
086      public ServletOutputStream createOutputStream() throws IOException
087      {
088        CachingResponseStream crs = new CachingResponseStream( 
089            servletResponse, cacheFile );
090    
091        return crs;
092      }
093    
094      /**
095       * Forces any content in the buffer to be written to the client. A 
096       * call to this method automatically commits the response, meaning 
097       * the status code and headers will be written.
098       */
099      public void flushBuffer() throws IOException 
100      {
101        ( (CachingResponseStream) stream ).flush();
102      }
103    
104      /**
105       * Returns a ServletOutputStream suitable for writing binary data in 
106       * the response. If a <code>Writer</code> has not already been
107       * created for the response (through a call to {@link #getWriter()}
108       * method) a new instance of the {@link CachingResponseStream}
109       * is created, and the instance variable {@link #stream} set to the
110       * new instance created.  If the {@link #stream} has already been
111       * initialised, then the reference is returned.
112       *
113       * @throws IOException If errors are encountered while creating
114       *   the cached response stream.
115       * @throws IllegalStateException If {@link #getWriter()} has
116       *   already been called on this instance.
117       */
118      public ServletOutputStream getOutputStream() throws IOException 
119      {
120        if ( writer != null ) 
121        {
122          throw new IllegalStateException( 
123              "getWriter() has already been called for this response" );
124        }
125    
126        if (stream == null) 
127        {
128          stream = createOutputStream();
129        }
130    
131        return stream;
132      }
133    
134      /**
135       * Returns a PrintWriter object that can send character text to the 
136       * client. The PrintWriter is created on top of the appropriate
137       * {@link CachingResponseStream} class.  The character encoding 
138       * used is the one specified in the charset= property of the 
139       * <code>setContentType( java.lang.String )</code> method, which must 
140       * be called before calling this method for the charset to take 
141       * effect. 
142       *
143       * <p>If necessary, the MIME type of the response is modified to 
144       * reflect the character encoding used.</p>
145       *
146       * <p>Either this method or {@link #getOutputStream()} may be called 
147       * to write the body, not both.
148       *
149       * @throws IOException If errors are encountered while creating
150       *   the cached response stream.
151       * @throws IllegalStateException If {@link #getWriter()} has
152       *   already been called on this instance.
153       */
154      public PrintWriter getWriter() throws IOException 
155      {
156        if ( writer != null ) 
157        {
158          return writer;
159        }
160    
161        if ( stream != null ) 
162        {
163          throw new IllegalStateException( 
164              "getOutputStream() has already been called for this response" );
165        }
166    
167        stream = createOutputStream();
168        writer = new PrintWriter( stream );
169        return writer;
170      }
171    
172      /**
173       * Closes the cached file in the {@link CachingResponseStream} without
174       * closing the underlying stream. 
175       *
176       * <p><b>Note:</b> This method must be invoked by to ensure that the
177       * cache file that was generated is properly closed after the actual
178       * response was generated.</p>
179       *
180       * @throws IOException - If errors are encountered while closing
181       *   the file.
182       */
183      public void closeCacheFile() throws IOException
184      {
185                    if ( stream != null )
186                    {
187                            ( (CachingResponseStream) stream ).closeCacheFile();
188                    }
189      }
190    
191      /**
192       * Add the specified header to the {@link #fileProperties}
193       *
194       * @param name The name of the header
195       * @param value The value of the header
196       */
197      public void addHeader( String name, String value )
198      {
199        super.addHeader( name, value );
200        fileProperties.setHeader( name, value );
201      }
202    
203      /**
204       * Add the specified header to the {@link #fileProperties}
205       *
206       * @param name The name of the header
207       * @param value The value of the header
208       */
209      public void setHeader( String name, String value )
210      {
211        super.setHeader( name, value );
212        fileProperties.setHeader( name, value );
213      }
214    
215      /**
216       * Update the property in the {@link #fileProperties} and then pass
217       * along to the super class implementation.
218       *
219       * @param charset A String specifying only the character set defined 
220       *   by IANA Character Sets (http://www.iana.org/assignments/character-sets)
221       */
222      public void setCharacterEncoding( String charset )
223      {
224        super.setCharacterEncoding( charset );
225        fileProperties.setCharacterEncoding( charset );
226      }
227    
228      /**
229       * Update the property in the {@link #fileProperties} and then pass
230       * along to the super class implementation.
231       *
232       * @param type A String specifying the MIME type of the content
233       */
234      public void setContentType( String type )
235      {
236        super.setContentType( type );
237        fileProperties.setContentType( type );
238      }
239    
240      /**
241       * Update the property in the {@link #fileProperties} and then pass
242       * along to the super class implementation.
243       *
244       * @param locale The locale of the response
245       */
246      public void setLocale( Locale locale )
247      {
248        super.setLocale( locale );
249        fileProperties.setLocale( locale );
250      }
251    
252      /**
253       * If the status is not equal to <code>SC_OK</code>, indicate to the
254       * {@link #fileProperties} that the response should not be cached.
255       *
256       * @param status The status code to set for the response.
257       */
258      public void setStatus( int status )
259      {
260        super.setStatus( status );
261        if ( status != HttpServletResponse.SC_OK )
262        {
263          fileProperties.setValidResponse( false );
264        }
265      }
266    }