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 /** 026 * Implementation of <b>ServletOutputStream</b> that works with 027 * the CompressionServletResponseWrapper implementation. 028 * 029 * @author Amy Roh 030 * @author Dmitri Valdin 031 * @version $Revision: 267129 $, $Date: 2004-03-18 08:40:35 -0800 (Thu, 18 Mar 2004) $ 032 */ 033 034 public class CompressionResponseStream 035 extends ServletOutputStream { 036 037 038 // ----------------------------------------------------------- Constructors 039 040 041 /** 042 * Construct a servlet output stream associated with the specified Response. 043 * 044 * @param response The associated response 045 */ 046 public CompressionResponseStream(HttpServletResponse response) throws IOException{ 047 048 super(); 049 closed = false; 050 this.response = response; 051 this.output = response.getOutputStream(); 052 053 } 054 055 056 // ----------------------------------------------------- Instance Variables 057 058 059 /** 060 * The threshold number which decides to compress or not. 061 * Users can configure in web.xml to set it to fit their needs. 062 */ 063 protected int compressionThreshold = 0; 064 065 /** 066 * Debug level 067 */ 068 private int debug = 0; 069 070 /** 071 * The buffer through which all of our output bytes are passed. 072 */ 073 protected byte[] buffer = null; 074 075 /** 076 * The number of data bytes currently in the buffer. 077 */ 078 protected int bufferCount = 0; 079 080 /** 081 * The underlying gzip output stream to which we should write data. 082 */ 083 protected GZIPOutputStream gzipstream = null; 084 085 /** 086 * Has this stream been closed? 087 */ 088 protected boolean closed = false; 089 090 /** 091 * The content length past which we will not write, or -1 if there is 092 * no defined content length. 093 */ 094 protected int length = -1; 095 096 /** 097 * The response with which this servlet output stream is associated. 098 */ 099 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 }