001: package com.openedit.servlet.gzip;
002:
003: import java.io.ByteArrayOutputStream;
004: import java.io.IOException;
005: import java.io.OutputStream;
006: import java.util.zip.GZIPOutputStream;
007:
008: import javax.servlet.ServletOutputStream;
009: import javax.servlet.http.HttpServletResponse;
010:
011: public class GzipResponseStream extends ServletOutputStream {
012: // abstraction of the output stream used for compression
013: protected OutputStream bufferedOutput = null;
014: // state keeping variable for if close() has been called
015: protected boolean closed = false;
016: // reference to original response
017: protected HttpServletResponse response = null;
018: // reference to the output stream to the client's browser
019: protected ServletOutputStream output = null;
020: // default size of the in-memory buffer
021: private int bufferSize = 50000;
022:
023: public GzipResponseStream(HttpServletResponse response)
024: throws IOException {
025: super ();
026: closed = false;
027: this .response = response;
028: this .output = response.getOutputStream();
029: bufferedOutput = new ByteArrayOutputStream();
030: }
031:
032: public void close() throws IOException {
033: // This hack shouldn't be needed, but it seems to make JBoss and Struts
034: // like the code more without hurting anything.
035: /*
036: // verify the stream is yet to be closed
037: if (closed) {
038: throw new IOException("This output stream has already been closed");
039: }
040: */
041: // if we buffered everything in memory, gzip it
042: if (bufferedOutput instanceof ByteArrayOutputStream) {
043: // get the content
044: ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput;
045: // prepare a gzip stream
046: ByteArrayOutputStream compressedContent = new ByteArrayOutputStream();
047: GZIPOutputStream gzipstream = new GZIPOutputStream(
048: compressedContent);
049: byte[] bytes = baos.toByteArray();
050: gzipstream.write(bytes);
051: gzipstream.finish();
052: // get the compressed content
053: byte[] compressedBytes = compressedContent.toByteArray();
054: // set appropriate HTTP headers
055: response.setContentLength(compressedBytes.length);
056: response.addHeader("Content-Encoding", "gzip");
057: output.write(compressedBytes);
058: output.flush();
059: output.close();
060: closed = true;
061: }
062: // if things were not buffered in memory, finish the GZIP stream and response
063: else if (bufferedOutput instanceof GZIPOutputStream) {
064: // cast to appropriate type
065: GZIPOutputStream gzipstream = (GZIPOutputStream) bufferedOutput;
066: // finish the compression
067: gzipstream.finish();
068: // finish the response
069: output.flush();
070: output.close();
071: closed = true;
072: }
073: }
074:
075: public void flush() throws IOException {
076: if (closed) {
077: throw new IOException("Cannot flush a closed output stream");
078: }
079: bufferedOutput.flush();
080: }
081:
082: public void write(int b) throws IOException {
083: if (closed) {
084: throw new IOException(
085: "Cannot write to a closed output stream");
086: }
087: // make sure we aren't over the buffer's limit
088: checkBufferSize(1);
089: // write the byte to the temporary output
090: bufferedOutput.write((byte) b);
091: }
092:
093: private void checkBufferSize(int length) throws IOException {
094: // check if we are buffering too large of a file
095: if (bufferedOutput instanceof ByteArrayOutputStream) {
096: ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput;
097: if (baos.size() + length > bufferSize) {
098: // files too large to keep in memory are sent to the client without Content-Length specified
099: response.addHeader("Content-Encoding", "gzip");
100: // get existing bytes
101: byte[] bytes = baos.toByteArray();
102: // make new gzip stream using the response output stream
103: GZIPOutputStream gzipstream = new GZIPOutputStream(
104: output);
105: gzipstream.write(bytes);
106: // we are no longer buffering, send content via gzipstream
107: bufferedOutput = gzipstream;
108: }
109: }
110: }
111:
112: public void write(byte b[]) throws IOException {
113: write(b, 0, b.length);
114: }
115:
116: public void write(byte b[], int off, int len) throws IOException {
117: //System.out.println("writing...");
118: if (closed) {
119: throw new IOException(
120: "Cannot write to a closed output stream");
121: }
122: // make sure we aren't over the buffer's limit
123: checkBufferSize(len);
124: // write the content to the buffer
125: bufferedOutput.write(b, off, len);
126: }
127:
128: public boolean closed() {
129: return (this .closed);
130: }
131:
132: public void reset() {
133: //noop
134: }
135: }
|