View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   //CabaWeb
3   //Copyright (C) 2004  Thomas Vogt <Thomas.Vogt@TVC-Software.com>
4   //
5   //This library is free software; you can redistribute it and/or
6   //modify it under the terms of the GNU Lesser General Public
7   //License as published by the Free Software Foundation; either
8   //version 2.1 of the License, or (at your option) any later version.
9   //
10  //This library is distributed in the hope that it will be useful,
11  //but WITHOUT ANY WARRANTY; without even the implied warranty of
12  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  //Lesser General Public License for more details.
14  //
15  //You should have received a copy of the GNU Lesser General Public
16  //License along with this library; if not, write to the Free Software
17  //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package org.fhw.cabaweb.webfrontend.filters;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.util.zip.GZIPOutputStream;
25  
26  import javax.servlet.ServletOutputStream;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  /***
33   * <strong>Filter</strong>-Klasse die im Falle das der Client das unterst&uuml;tzt,
34   * den Stream GZIP komprimiert. Antwort Stream Klasse.
35   *
36   * ORIGINAL aus dem Buch Servlets and JavaServer Pages
37   * von Jayson Falkner, Kevin Jones (http://www.jspbook.com/)
38   *
39   * @author Jayson Falkner, Kevin Jones
40   *
41   * @version Version 1.0 24.07.2004
42   */
43  public class GZIPResponseStream extends ServletOutputStream
44  {
45      /***
46       * The <code>Log</code> instance for this application.
47       */
48      private Log log = LogFactory.getLog("org.fhw.cabaweb.webfrontend.filters");
49  
50      // abstraction of the output stream used for compression
51      protected OutputStream bufferedOutput = null;
52      // state keeping variable for if close() has been called
53      protected boolean closed = false;
54      // reference to original response
55      protected HttpServletResponse response = null;
56      // reference to the output stream to the client's browser
57      protected ServletOutputStream output = null;
58      // default size of the in-memory buffer
59      private int bufferSize = 50000;
60  
61      public GZIPResponseStream(HttpServletResponse response) throws IOException
62      {
63          super();
64          closed = false;
65          this.response = response;
66          this.output = response.getOutputStream();
67          bufferedOutput = new ByteArrayOutputStream();
68      }
69  
70      public void close() throws IOException
71      {
72          // This hack shouldn't be needed, but it seems to make JBoss and Struts
73          // like the code more without hurting anything.
74          /*
75          // verify the stream is yet to be closed
76          if (closed) {
77            throw new IOException("This output stream has already been closed");
78          }
79          */
80          // if we buffered everything in memory, gzip it
81          if (bufferedOutput instanceof ByteArrayOutputStream)
82          {
83              // get the content
84              ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput;
85              // prepare a gzip stream
86              ByteArrayOutputStream compressedContent = new ByteArrayOutputStream();
87              GZIPOutputStream gzipstream = new GZIPOutputStream(compressedContent);
88              byte[] bytes = baos.toByteArray();
89              gzipstream.write(bytes);
90              gzipstream.finish();
91              // get the compressed content
92              byte[] compressedBytes = compressedContent.toByteArray();
93              // set appropriate HTTP headers
94              response.setContentLength(compressedBytes.length);
95              response.addHeader("Content-Encoding", "gzip");
96              output.write(compressedBytes);
97              output.flush();
98              output.close();
99              closed = true;
100         }
101         // if things were not buffered in memory, finish the GZIP stream and response
102         else if (bufferedOutput instanceof GZIPOutputStream)
103         {
104             // cast to appropriate type
105             GZIPOutputStream gzipstream = (GZIPOutputStream) bufferedOutput;
106             // finish the compression
107             gzipstream.finish();
108             // finish the response
109             output.flush();
110             output.close();
111             closed = true;
112         }
113     }
114 
115     public void flush() throws IOException
116     {
117         if (closed)
118         {
119             log.error(" GZIP Cannot flush a closed output stream");
120             throw new IOException("Cannot flush a closed output stream");
121         }
122         bufferedOutput.flush();
123     }
124 
125     public void write(int b) throws IOException
126     {
127         if (closed)
128         {
129             throw new IOException("Cannot write to a closed output stream");
130         }
131         // make sure we aren't over the buffer's limit
132         checkBufferSize(1);
133         // write the byte to the temporary output
134         bufferedOutput.write((byte) b);
135     }
136 
137     private void checkBufferSize(int length) throws IOException
138     {
139         if (log.isDebugEnabled())
140         {
141             log.debug(" GZIP checking Buffer Size...");
142         }
143 
144         // check if we are buffering too large of a file
145         if (bufferedOutput instanceof ByteArrayOutputStream)
146         {
147             ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput;
148             if (baos.size() + length > bufferSize)
149             {
150                 // files too large to keep in memory are sent to the client without Content-Length specified
151                 response.addHeader("Content-Encoding", "gzip");
152                 // get existing bytes
153                 byte[] bytes = baos.toByteArray();
154                 // make new gzip stream using the response output stream
155                 GZIPOutputStream gzipstream = new GZIPOutputStream(output);
156                 gzipstream.write(bytes);
157                 // we are no longer buffering, send content via gzipstream
158                 bufferedOutput = gzipstream;
159             }
160         }
161     }
162 
163     public void write(byte b[]) throws IOException
164     {
165         write(b, 0, b.length);
166     }
167 
168     public void write(byte b[], int off, int len) throws IOException
169     {
170         if (log.isDebugEnabled())
171         {
172             log.debug(" GZIP writing...");
173         }
174 
175         if (closed)
176         {
177             log.error(" GZIP Cannot write to a closed output stream");
178             throw new IOException("Cannot write to a closed output stream");
179         }
180 
181         // make sure we aren't over the buffer's limit
182         checkBufferSize(len);
183         // write the content to the buffer
184         bufferedOutput.write(b, off, len);
185     }
186 
187     public boolean closed()
188     {
189         return (this.closed);
190     }
191 
192     public void reset()
193     {
194         //noop
195     }
196 }