001: /*
002: * Copyright 2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package compressionFilters;
017:
018: import java.io.IOException;
019: import java.io.OutputStream;
020: import java.util.zip.GZIPOutputStream;
021: import javax.servlet.ServletOutputStream;
022: import javax.servlet.http.HttpServletResponse;
023:
024: /**
025: * Implementation of <b>ServletOutputStream</b> that works with
026: * the CompressionServletResponseWrapper implementation.
027: *
028: * @author Amy Roh
029: * @author Dmitri Valdin
030: * @version $Revision: 1.3 $, $Date: 2004/03/18 16:40:33 $
031: */
032:
033: public class CompressionResponseStream extends ServletOutputStream {
034:
035: // ----------------------------------------------------------- Constructors
036:
037: /**
038: * Construct a servlet output stream associated with the specified Response.
039: *
040: * @param response The associated response
041: */
042: public CompressionResponseStream(HttpServletResponse response)
043: throws IOException {
044:
045: super ();
046: closed = false;
047: this .response = response;
048: this .output = response.getOutputStream();
049:
050: }
051:
052: // ----------------------------------------------------- Instance Variables
053:
054: /**
055: * The threshold number which decides to compress or not.
056: * Users can configure in web.xml to set it to fit their needs.
057: */
058: protected int compressionThreshold = 0;
059:
060: /**
061: * Debug level
062: */
063: private int debug = 0;
064:
065: /**
066: * The buffer through which all of our output bytes are passed.
067: */
068: protected byte[] buffer = null;
069:
070: /**
071: * The number of data bytes currently in the buffer.
072: */
073: protected int bufferCount = 0;
074:
075: /**
076: * The underlying gzip output stream to which we should write data.
077: */
078: protected GZIPOutputStream gzipstream = null;
079:
080: /**
081: * Has this stream been closed?
082: */
083: protected boolean closed = false;
084:
085: /**
086: * The content length past which we will not write, or -1 if there is
087: * no defined content length.
088: */
089: protected int length = -1;
090:
091: /**
092: * The response with which this servlet output stream is associated.
093: */
094: protected HttpServletResponse response = null;
095:
096: /**
097: * The underlying servket output stream to which we should write data.
098: */
099: protected ServletOutputStream output = null;
100:
101: // --------------------------------------------------------- Public Methods
102:
103: /**
104: * Set debug level
105: */
106: public void setDebugLevel(int debug) {
107: this .debug = debug;
108: }
109:
110: /**
111: * Set the compressionThreshold number and create buffer for this size
112: */
113: protected void setBuffer(int threshold) {
114: compressionThreshold = threshold;
115: buffer = new byte[compressionThreshold];
116: if (debug > 1) {
117: System.out.println("buffer is set to "
118: + compressionThreshold);
119: }
120: }
121:
122: /**
123: * Close this output stream, causing any buffered data to be flushed and
124: * any further output data to throw an IOException.
125: */
126: public void close() throws IOException {
127:
128: if (debug > 1) {
129: System.out.println("close() @ CompressionResponseStream");
130: }
131: if (closed)
132: throw new IOException(
133: "This output stream has already been closed");
134:
135: if (gzipstream != null) {
136: flushToGZip();
137: gzipstream.close();
138: gzipstream = null;
139: } else {
140: if (bufferCount > 0) {
141: if (debug > 2) {
142: System.out.print("output.write(");
143: System.out.write(buffer, 0, bufferCount);
144: System.out.println(")");
145: }
146: output.write(buffer, 0, bufferCount);
147: bufferCount = 0;
148: }
149: }
150:
151: output.close();
152: closed = true;
153:
154: }
155:
156: /**
157: * Flush any buffered data for this output stream, which also causes the
158: * response to be committed.
159: */
160: public void flush() throws IOException {
161:
162: if (debug > 1) {
163: System.out.println("flush() @ CompressionResponseStream");
164: }
165: if (closed) {
166: throw new IOException("Cannot flush a closed output stream");
167: }
168:
169: if (gzipstream != null) {
170: gzipstream.flush();
171: }
172:
173: }
174:
175: public void flushToGZip() throws IOException {
176:
177: if (debug > 1) {
178: System.out
179: .println("flushToGZip() @ CompressionResponseStream");
180: }
181: if (bufferCount > 0) {
182: if (debug > 1) {
183: System.out
184: .println("flushing out to GZipStream, bufferCount = "
185: + bufferCount);
186: }
187: writeToGZip(buffer, 0, bufferCount);
188: bufferCount = 0;
189: }
190:
191: }
192:
193: /**
194: * Write the specified byte to our output stream.
195: *
196: * @param b The byte to be written
197: *
198: * @exception IOException if an input/output error occurs
199: */
200: public void write(int b) throws IOException {
201:
202: if (debug > 1) {
203: System.out.println("write " + b
204: + " in CompressionResponseStream ");
205: }
206: if (closed)
207: throw new IOException(
208: "Cannot write to a closed output stream");
209:
210: if (bufferCount >= buffer.length) {
211: flushToGZip();
212: }
213:
214: buffer[bufferCount++] = (byte) b;
215:
216: }
217:
218: /**
219: * Write <code>b.length</code> bytes from the specified byte array
220: * to our output stream.
221: *
222: * @param b The byte array to be written
223: *
224: * @exception IOException if an input/output error occurs
225: */
226: public void write(byte b[]) throws IOException {
227:
228: write(b, 0, b.length);
229:
230: }
231:
232: /**
233: * Write <code>len</code> bytes from the specified byte array, starting
234: * at the specified offset, to our output stream.
235: *
236: * @param b The byte array containing the bytes to be written
237: * @param off Zero-relative starting offset of the bytes to be written
238: * @param len The number of bytes to be written
239: *
240: * @exception IOException if an input/output error occurs
241: */
242: public void write(byte b[], int off, int len) throws IOException {
243:
244: if (debug > 1) {
245: System.out.println("write, bufferCount = " + bufferCount
246: + " len = " + len + " off = " + off);
247: }
248: if (debug > 2) {
249: System.out.print("write(");
250: System.out.write(b, off, len);
251: System.out.println(")");
252: }
253:
254: if (closed)
255: throw new IOException(
256: "Cannot write to a closed output stream");
257:
258: if (len == 0)
259: return;
260:
261: // Can we write into buffer ?
262: if (len <= (buffer.length - bufferCount)) {
263: System.arraycopy(b, off, buffer, bufferCount, len);
264: bufferCount += len;
265: return;
266: }
267:
268: // There is not enough space in buffer. Flush it ...
269: flushToGZip();
270:
271: // ... and try again. Note, that bufferCount = 0 here !
272: if (len <= (buffer.length - bufferCount)) {
273: System.arraycopy(b, off, buffer, bufferCount, len);
274: bufferCount += len;
275: return;
276: }
277:
278: // write direct to gzip
279: writeToGZip(b, off, len);
280: }
281:
282: public void writeToGZip(byte b[], int off, int len)
283: 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: // -------------------------------------------------------- Package Methods
305:
306: /**
307: * Has this response stream been closed?
308: */
309: public boolean closed() {
310:
311: return (this.closed);
312:
313: }
314:
315: }
|