1 /* 2 * Copyright 1999,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 18 package org.apache.commons.messagelet.impl; 19 20 21 import java.io.IOException; 22 import java.io.OutputStream; 23 import java.io.OutputStreamWriter; 24 import java.io.PrintWriter; 25 import java.util.Locale; 26 27 import javax.servlet.ServletOutputStream; 28 import javax.servlet.ServletResponse; 29 30 31 /** 32 * Based on the ResponseBase code from Catalina. 33 * 34 * @author Craig R. McClanahan 35 * @author James Strachan 36 * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $ 37 */ 38 39 public class ServletResponseImpl implements ServletResponse { 40 41 42 // ----------------------------------------------------- Instance Variables 43 44 45 /** 46 * The buffer through which all of our output bytes are passed. 47 */ 48 protected byte[] buffer = new byte[1024]; 49 50 51 /** 52 * The number of data bytes currently in the buffer. 53 */ 54 protected int bufferCount = 0; 55 56 57 /** 58 * Has this response been committed yet? 59 */ 60 protected boolean committed = false; 61 62 63 /** 64 * The actual number of bytes written to this Response. 65 */ 66 protected int contentCount = 0; 67 68 69 /** 70 * The content length associated with this Response. 71 */ 72 protected int contentLength = -1; 73 74 75 /** 76 * The content type associated with this Response. 77 */ 78 protected String contentType = null; 79 80 81 /** 82 * The character encoding associated with this Response. 83 */ 84 protected String encoding = null; 85 86 87 /** 88 * Are we currently processing inside a RequestDispatcher.include()? 89 */ 90 protected boolean included = false; 91 92 93 /** 94 * The Locale associated with this Response. 95 */ 96 protected Locale locale = Locale.getDefault(); 97 98 99 /** 100 * The output stream associated with this Response. 101 */ 102 protected OutputStream output = null; 103 104 105 /** 106 * The ServletOutputStream that has been returned by 107 * <code>getOutputStream()</code>, if any. 108 */ 109 protected ServletOutputStream stream = null; 110 111 112 /** 113 * The PrintWriter that has been returned by 114 * <code>getWriter()</code>, if any. 115 */ 116 protected PrintWriter writer = null; 117 118 119 /** 120 * Error flag. True if the response is an error report. 121 */ 122 protected boolean error = false; 123 124 125 // ------------------------------------------------------------- Properties 126 127 128 129 /** 130 * Return the number of bytes actually written to the output stream. 131 */ 132 public int getContentCount() { 133 134 return (this.contentCount); 135 136 } 137 138 139 140 141 /** 142 * Return the output stream associated with this Response. 143 */ 144 public OutputStream getStream() { 145 146 return (this.output); 147 148 } 149 150 151 /** 152 * Set the output stream associated with this Response. 153 * 154 * @param stream The new output stream 155 */ 156 public void setStream(OutputStream stream) { 157 158 this.output = stream; 159 160 } 161 162 163 164 // --------------------------------------------------------- Public Methods 165 166 167 /** 168 * Create and return a ServletOutputStream to write the content 169 * associated with this Response. 170 * 171 * @exception IOException if an input/output error occurs 172 */ 173 public ServletOutputStream createOutputStream() throws IOException { 174 175 //return (new ResponseStream(this)); 176 return new BufferedServletOutputStream(); 177 } 178 179 180 /** 181 * Perform whatever actions are required to flush and close the output 182 * stream or writer, in a single operation. 183 * 184 * @exception IOException if an input/output error occurs 185 */ 186 public void finishResponse() throws IOException { 187 188 // If no stream has been requested yet, get one so we can 189 // flush the necessary headers 190 if (this.stream == null) { 191 ServletOutputStream sos = getOutputStream(); 192 sos.flush(); 193 sos.close(); 194 return; 195 } 196 197 // If our stream is closed, no action is necessary 198 /* 199 if ( ((ResponseStream) stream).closed() ) 200 return; 201 */ 202 203 // Flush and close the appropriate output mechanism 204 if (writer != null) { 205 writer.flush(); 206 writer.close(); 207 } else { 208 stream.flush(); 209 stream.close(); 210 } 211 212 // The underlying output stream (perhaps from a socket) 213 // is not our responsibility 214 215 } 216 217 218 /** 219 * Return the content length that was set or calculated for this Response. 220 */ 221 public int getContentLength() { 222 223 return (this.contentLength); 224 225 } 226 227 228 /** 229 * Return the content type that was set or calculated for this response, 230 * or <code>null</code> if no content type was set. 231 */ 232 public String getContentType() { 233 234 return (this.contentType); 235 236 } 237 238 239 240 // -------------------------------------------------------- Package Methods 241 242 243 244 // ------------------------------------------------ ServletResponse Methods 245 246 247 /** 248 * Flush the buffer and commit this response. 249 * 250 * @exception IOException if an input/output error occurs 251 */ 252 public void flushBuffer() throws IOException { 253 254 committed = true; 255 if (bufferCount > 0) { 256 try { 257 output.write(buffer, 0, bufferCount); 258 } finally { 259 bufferCount = 0; 260 } 261 } 262 263 } 264 265 266 /** 267 * Return the actual buffer size used for this Response. 268 */ 269 public int getBufferSize() { 270 271 return (buffer.length); 272 273 } 274 275 276 /** 277 * Return the character encoding used for this Response. 278 */ 279 public String getCharacterEncoding() { 280 281 if (encoding == null) 282 return ("ISO-8859-1"); 283 else 284 return (encoding); 285 286 } 287 288 289 /** 290 * Return the servlet output stream associated with this Response. 291 * 292 * @exception IllegalStateException if <code>getWriter</code> has 293 * already been called for this response 294 * @exception IOException if an input/output error occurs 295 */ 296 public ServletOutputStream getOutputStream() throws IOException { 297 298 if (writer != null) { 299 throw new IllegalStateException( "getWriter() has already been called" ); 300 } 301 302 if (stream == null) 303 stream = createOutputStream(); 304 /* 305 ((ResponseStream) stream).setCommit(true); 306 */ 307 return (stream); 308 309 } 310 311 312 /** 313 * Return the Locale assigned to this response. 314 */ 315 public Locale getLocale() { 316 317 return (locale); 318 319 } 320 321 322 /** 323 * Return the writer associated with this Response. 324 * 325 * @exception IllegalStateException if <code>getOutputStream</code> has 326 * already been called for this response 327 * @exception IOException if an input/output error occurs 328 */ 329 public PrintWriter getWriter() throws IOException { 330 331 if (writer != null) 332 return (writer); 333 334 if (stream != null) { 335 throw new IllegalStateException( "getOutputStream() has already been called" ); 336 } 337 338 stream = createOutputStream(); 339 340 // a slight hack which slightly breaks the Servlet contract... 341 // see commented out section below for what it should be... 342 writer = new PrintWriter( new OutputStreamWriter(stream, getCharacterEncoding()) ); 343 return writer; 344 345 /* 346 ((ResponseStream) stream).setCommit(false); 347 OutputStreamWriter osr = 348 new OutputStreamWriter(stream, getCharacterEncoding()); 349 writer = new ResponseWriter(osr, (ResponseStream) stream); 350 return (writer); 351 */ 352 } 353 354 355 /** 356 * Has the output of this response already been committed? 357 */ 358 public boolean isCommitted() { 359 360 return (committed); 361 362 } 363 364 365 /** 366 * Clear any content written to the buffer. 367 * 368 * @exception IllegalStateException if this response has already 369 * been committed 370 */ 371 public void reset() { 372 373 if (committed) { 374 throw new IllegalStateException( "response has already been committed" ); 375 } 376 377 if (included) 378 return; // Ignore any call from an included servlet 379 380 /* 381 if (stream != null) 382 ((ResponseStream) stream).reset(); 383 */ 384 bufferCount = 0; 385 contentLength = -1; 386 contentType = null; 387 388 } 389 390 391 /** 392 * Reset the data buffer but not any status or header information. 393 * 394 * @exception IllegalStateException if the response has already 395 * been committed 396 */ 397 public void resetBuffer() { 398 399 if (committed) { 400 throw new IllegalStateException( "response has already been committed" ); 401 } 402 403 bufferCount = 0; 404 405 } 406 407 408 /** 409 * Set the buffer size to be used for this Response. 410 * 411 * @param size The new buffer size 412 * 413 * @exception IllegalStateException if this method is called after 414 * output has been committed for this response 415 */ 416 public void setBufferSize(int size) { 417 418 if (committed || (bufferCount > 0)) { 419 throw new IllegalStateException( "Output has already been committed" ); 420 } 421 422 if (buffer.length >= size) 423 return; 424 buffer = new byte[size]; 425 426 } 427 428 429 /** 430 * Set the content length (in bytes) for this Response. 431 * 432 * @param length The new content length 433 */ 434 public void setContentLength(int length) { 435 436 if (isCommitted()) 437 return; 438 439 if (included) 440 return; // Ignore any call from an included servlet 441 442 this.contentLength = length; 443 444 } 445 446 447 /** 448 * Set the content type for this Response. 449 * 450 * @param type The new content type 451 */ 452 public void setContentType(String type) { 453 454 if (isCommitted()) 455 return; 456 457 if (included) 458 return; // Ignore any call from an included servlet 459 460 this.contentType = type; 461 /* 462 if (type.indexOf(';') >= 0) { 463 encoding = RequestUtil.parseCharacterEncoding(type); 464 if (encoding == null) 465 encoding = "ISO-8859-1"; 466 } 467 */ 468 } 469 470 471 /** 472 * Set the Locale that is appropriate for this response, including 473 * setting the appropriate character encoding. 474 * 475 * @param locale The new locale 476 */ 477 public void setLocale(Locale locale) { 478 479 if (isCommitted()) 480 return; 481 482 if (included) 483 return; // Ignore any call from an included servlet 484 485 this.locale = locale; 486 /* 487 if ((this.encoding == null) && (this.context != null)) { 488 CharsetMapper mapper = context.getCharsetMapper(); 489 this.encoding = mapper.getCharset(locale); 490 } 491 */ 492 } 493 494 495 }