View Javadoc

1   /*
2   * Copyright 2004 The Apache Software Foundation
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *     http://www.apache.org/licenses/LICENSE-2.0
9   *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16  package compressionFilters;
17  
18  import java.io.IOException;
19  import java.io.OutputStream;
20  import java.util.zip.GZIPOutputStream;
21  import javax.servlet.ServletOutputStream;
22  import javax.servlet.http.HttpServletResponse;
23  
24  
25  /**
26   * Implementation of <b>ServletOutputStream</b> that works with
27   * the CompressionServletResponseWrapper implementation.
28   *
29   * @author Amy Roh
30   * @author Dmitri Valdin
31   * @version $Revision: 267129 $, $Date: 2004-03-18 08:40:35 -0800 (Thu, 18 Mar 2004) $
32   */
33  
34  public class CompressionResponseStream
35      extends ServletOutputStream {
36  
37  
38      // ----------------------------------------------------------- Constructors
39  
40  
41      /**
42       * Construct a servlet output stream associated with the specified Response.
43       *
44       * @param response The associated response
45       */
46      public CompressionResponseStream(HttpServletResponse response) throws IOException{
47  
48          super();
49          closed = false;
50          this.response = response;
51          this.output = response.getOutputStream();
52  
53      }
54  
55  
56      // ----------------------------------------------------- Instance Variables
57  
58  
59      /**
60       * The threshold number which decides to compress or not.
61       * Users can configure in web.xml to set it to fit their needs.
62       */
63      protected int compressionThreshold = 0;
64  
65      /**
66       * Debug level
67       */
68      private int debug = 0;
69  
70      /**
71       * The buffer through which all of our output bytes are passed.
72       */
73      protected byte[] buffer = null;
74  
75      /**
76       * The number of data bytes currently in the buffer.
77       */
78      protected int bufferCount = 0;
79  
80      /**
81       * The underlying gzip output stream to which we should write data.
82       */
83      protected GZIPOutputStream gzipstream = null;
84  
85      /**
86       * Has this stream been closed?
87       */
88      protected boolean closed = false;
89  
90      /**
91       * The content length past which we will not write, or -1 if there is
92       * no defined content length.
93       */
94      protected int length = -1;
95  
96      /**
97       * The response with which this servlet output stream is associated.
98       */
99      protected HttpServletResponse response = null;
100 
101     /**
102      * The underlying servket output stream to which we should write data.
103      */
104     protected ServletOutputStream output = null;
105 
106 
107     // --------------------------------------------------------- Public Methods
108 
109     /**
110      * Set debug level
111      */
112     public void setDebugLevel(int debug) {
113         this.debug = debug;
114     }
115 
116 
117     /**
118      * Set the compressionThreshold number and create buffer for this size
119      */
120     protected void setBuffer(int threshold) {
121         compressionThreshold = threshold;
122         buffer = new byte[compressionThreshold];
123         if (debug > 1) {
124             System.out.println("buffer is set to "+compressionThreshold);
125         }
126     }
127 
128     /**
129      * Close this output stream, causing any buffered data to be flushed and
130      * any further output data to throw an IOException.
131      */
132     public void close() throws IOException {
133 
134         if (debug > 1) {
135             System.out.println("close() @ CompressionResponseStream");
136         }
137         if (closed)
138             throw new IOException("This output stream has already been closed");
139 
140         if (gzipstream != null) {
141             flushToGZip();
142             gzipstream.close();
143             gzipstream = null;
144         } else {
145             if (bufferCount > 0) {
146                 if (debug > 2) {
147                     System.out.print("output.write(");
148                     System.out.write(buffer, 0, bufferCount);
149                     System.out.println(")");
150                 }
151                 output.write(buffer, 0, bufferCount);
152                 bufferCount = 0;
153             }
154         }
155 
156         output.close();
157         closed = true;
158 
159     }
160 
161 
162     /**
163      * Flush any buffered data for this output stream, which also causes the
164      * response to be committed.
165      */
166     public void flush() throws IOException {
167 
168         if (debug > 1) {
169             System.out.println("flush() @ CompressionResponseStream");
170         }
171         if (closed) {
172             throw new IOException("Cannot flush a closed output stream");
173         }
174 
175         if (gzipstream != null) {
176             gzipstream.flush();
177         }
178 
179     }
180 
181     public void flushToGZip() throws IOException {
182 
183         if (debug > 1) {
184             System.out.println("flushToGZip() @ CompressionResponseStream");
185         }
186         if (bufferCount > 0) {
187             if (debug > 1) {
188                 System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
189             }
190             writeToGZip(buffer, 0, bufferCount);
191             bufferCount = 0;
192         }
193 
194     }
195 
196     /**
197      * Write the specified byte to our output stream.
198      *
199      * @param b The byte to be written
200      *
201      * @exception IOException if an input/output error occurs
202      */
203     public void write(int b) throws IOException {
204 
205         if (debug > 1) {
206             System.out.println("write "+b+" in CompressionResponseStream ");
207         }
208         if (closed)
209             throw new IOException("Cannot write to a closed output stream");
210 
211         if (bufferCount >= buffer.length) {
212             flushToGZip();
213         }
214 
215         buffer[bufferCount++] = (byte) b;
216 
217     }
218 
219 
220     /**
221      * Write <code>b.length</code> bytes from the specified byte array
222      * to our output stream.
223      *
224      * @param b The byte array to be written
225      *
226      * @exception IOException if an input/output error occurs
227      */
228     public void write(byte b[]) throws IOException {
229 
230         write(b, 0, b.length);
231 
232     }
233 
234 
235     /**
236      * Write <code>len</code> bytes from the specified byte array, starting
237      * at the specified offset, to our output stream.
238      *
239      * @param b The byte array containing the bytes to be written
240      * @param off Zero-relative starting offset of the bytes to be written
241      * @param len The number of bytes to be written
242      *
243      * @exception IOException if an input/output error occurs
244      */
245     public void write(byte b[], int off, int len) throws IOException {
246 
247         if (debug > 1) {
248             System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
249         }
250         if (debug > 2) {
251             System.out.print("write(");
252             System.out.write(b, off, len);
253             System.out.println(")");
254         }
255 
256         if (closed)
257             throw new IOException("Cannot write to a closed output stream");
258 
259         if (len == 0)
260             return;
261 
262         // Can we write into buffer ?
263         if (len <= (buffer.length - bufferCount)) {
264             System.arraycopy(b, off, buffer, bufferCount, len);
265             bufferCount += len;
266             return;
267         }
268 
269         // There is not enough space in buffer. Flush it ...
270         flushToGZip();
271 
272         // ... and try again. Note, that bufferCount = 0 here !
273         if (len <= (buffer.length - bufferCount)) {
274             System.arraycopy(b, off, buffer, bufferCount, len);
275             bufferCount += len;
276             return;
277         }
278 
279         // write direct to gzip
280         writeToGZip(b, off, len);
281     }
282 
283     public void writeToGZip(byte b[], int off, int len) throws IOException {
284 
285         if (debug > 1) {
286             System.out.println("writeToGZip, len = " + len);
287         }
288         if (debug > 2) {
289             System.out.print("writeToGZip(");
290             System.out.write(b, off, len);
291             System.out.println(")");
292         }
293         if (gzipstream == null) {
294             if (debug > 1) {
295                 System.out.println("new GZIPOutputStream");
296             }
297             response.addHeader("Content-Encoding", "gzip");
298             gzipstream = new GZIPOutputStream(output);
299         }
300         gzipstream.write(b, off, len);
301 
302     }
303 
304 
305     // -------------------------------------------------------- Package Methods
306 
307 
308     /**
309      * Has this response stream been closed?
310      */
311     public boolean closed() {
312 
313         return (this.closed);
314 
315     }
316 
317 }