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