1 2 3 27 28 package org.apache.coyote.tomcat5; 29 30 31 import java.io.IOException ; 32 import java.io.Writer ; 33 import java.security.AccessController ; 34 import java.security.PrivilegedActionException ; 35 import java.security.PrivilegedExceptionAction ; 36 import java.util.HashMap ; 37 38 import org.apache.catalina.connector.ClientAbortException; 39 import org.apache.catalina.security.SecurityUtil; 40 import org.apache.coyote.ActionCode; 41 import org.apache.coyote.Response; 42 import org.apache.tomcat.util.buf.ByteChunk; 43 import org.apache.tomcat.util.buf.C2BConverter; 44 import org.apache.tomcat.util.buf.CharChunk; 45 46 47 48 56 public class OutputBuffer extends Writer 57 implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel { 58 59 60 private static com.sun.org.apache.commons.logging.Log log= 61 com.sun.org.apache.commons.logging.LogFactory.getLog( OutputBuffer.class ); 62 63 65 66 public static final String DEFAULT_ENCODING = 67 org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING; 68 public static final int DEFAULT_BUFFER_SIZE = 8*1024; 69 static final int debug = 0; 70 71 72 public final int INITIAL_STATE = 0; 76 public final int CHAR_STATE = 1; 77 public final int BYTE_STATE = 2; 78 79 80 82 83 86 private ByteChunk bb; 87 88 89 92 private CharChunk cb; 93 94 95 98 private int state = 0; 99 100 101 104 private int bytesWritten = 0; 105 106 107 110 private int charsWritten = 0; 111 112 113 116 private boolean closed = false; 117 118 119 122 private boolean doFlush = false; 123 124 125 128 private ByteChunk outputChunk = new ByteChunk(); 129 130 131 134 private String enc; 135 136 137 140 private boolean gotEnc = false; 141 142 143 146 protected HashMap encoders = new HashMap (); 147 148 149 152 protected C2BConverter conv; 153 154 155 158 private Response coyoteResponse; 159 160 161 164 private boolean suspended = false; 165 166 167 169 170 173 public OutputBuffer() { 174 175 this(DEFAULT_BUFFER_SIZE); 176 177 } 178 179 180 public OutputBuffer(boolean chunkingDisabled) { 182 this(DEFAULT_BUFFER_SIZE, chunkingDisabled); 183 } 184 186 187 192 public OutputBuffer(int size) { 193 202 this(size, false); 203 } 205 206 207 public OutputBuffer(int size, boolean chunkingDisabled) { 209 bb = new ByteChunk(size); 210 if (!chunkingDisabled) { 211 bb.setLimit(size); 212 } 213 bb.setByteOutputChannel(this); 214 cb = new CharChunk(size); 215 cb.setCharOutputChannel(this); 216 if (!chunkingDisabled) { 217 cb.setLimit(size); 218 } 219 } 220 222 223 225 226 231 public void setResponse(Response coyoteResponse) { 232 this.coyoteResponse = coyoteResponse; 233 } 234 235 236 241 public Response getResponse() { 242 return this.coyoteResponse; 243 } 244 245 246 251 public boolean isSuspended() { 252 return this.suspended; 253 } 254 255 256 261 public void setSuspended(boolean suspended) { 262 this.suspended = suspended; 263 } 264 265 266 268 269 272 public void recycle() { 273 274 if (log.isDebugEnabled()) 275 log.debug("recycle()"); 276 277 state = INITIAL_STATE; 278 bytesWritten = 0; 279 charsWritten = 0; 280 281 cb.recycle(); 282 bb.recycle(); 283 closed = false; 284 suspended = false; 285 286 if (conv!= null) { 287 conv.recycle(); 288 } 289 290 gotEnc = false; 291 enc = null; 292 } 293 294 295 301 public void close() 302 throws IOException { 303 304 if (closed) 305 return; 306 if (suspended) 307 return; 308 309 if ((!coyoteResponse.isCommitted()) 310 && (coyoteResponse.getContentLength() == -1)) { 311 if (state == CHAR_STATE) { 313 cb.flushBuffer(); 314 state = BYTE_STATE; 315 } 316 if (!coyoteResponse.isCommitted()) { 319 coyoteResponse.setContentLength(bb.getLength()); 320 } 321 } 322 323 doFlush(false); 324 closed = true; 325 326 coyoteResponse.finish(); 327 328 } 329 330 331 336 public void flush() 337 throws IOException { 338 doFlush(true); 339 } 340 341 342 347 protected void doFlush(boolean realFlush) 348 throws IOException { 349 350 if (suspended) 351 return; 352 353 doFlush = true; 354 if (state == CHAR_STATE) { 355 cb.flushBuffer(); 356 bb.flushBuffer(); 357 state = BYTE_STATE; 358 } else if (state == BYTE_STATE) { 359 bb.flushBuffer(); 360 } else if (state == INITIAL_STATE) { 361 coyoteResponse.sendHeaders(); 363 } 364 doFlush = false; 365 366 if (realFlush) { 367 coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 368 coyoteResponse); 369 if (coyoteResponse.isExceptionPresent()) { 372 throw new ClientAbortException 373 (coyoteResponse.getErrorException()); 374 } 375 } 376 377 } 378 379 380 382 383 393 public void realWriteBytes(byte buf[], int off, int cnt) 394 throws IOException { 395 396 if (log.isDebugEnabled()) 397 log.debug("realWrite(b, " + off + ", " + cnt + ") " + coyoteResponse); 398 399 if (closed) 400 return; 401 if (coyoteResponse == null) 402 return; 403 404 if (cnt > 0) { 406 outputChunk.setBytes(buf, off, cnt); 408 try { 409 coyoteResponse.doWrite(outputChunk); 410 } catch (IOException e) { 411 throw new ClientAbortException(e); 415 } 416 } 417 418 } 419 420 421 public void write(byte b[], int off, int len) throws IOException { 422 423 if (suspended) 424 return; 425 426 if (state == CHAR_STATE) 427 cb.flushBuffer(); 428 state = BYTE_STATE; 429 writeBytes(b, off, len); 430 431 } 432 433 434 private void writeBytes(byte b[], int off, int len) 435 throws IOException { 436 437 if (closed) 438 return; 439 if (log.isDebugEnabled()) 440 log.debug("write(b,off,len)"); 441 442 bb.append(b, off, len); 443 bytesWritten += len; 444 445 if (doFlush) { 448 bb.flushBuffer(); 449 } 450 451 } 452 453 454 public void writeByte(int b) 456 throws IOException { 457 458 if (suspended) 459 return; 460 461 if (state == CHAR_STATE) 462 cb.flushBuffer(); 463 state = BYTE_STATE; 464 465 if (log.isDebugEnabled()) 466 log.debug("write(b)"); 467 468 bb.append( (byte)b ); 469 bytesWritten++; 470 471 } 472 473 474 476 477 public void write(int c) 478 throws IOException { 479 480 if (suspended) 481 return; 482 483 state = CHAR_STATE; 484 485 if (log.isDebugEnabled()) 486 log.debug("writeChar(b)"); 487 488 cb.append((char) c); 489 charsWritten++; 490 491 } 492 493 494 public void write(char c[]) 495 throws IOException { 496 497 if (suspended) 498 return; 499 500 write(c, 0, c.length); 501 502 } 503 504 505 public void write(char c[], int off, int len) 506 throws IOException { 507 508 if (suspended) 509 return; 510 511 state = CHAR_STATE; 512 513 if (log.isDebugEnabled()) 514 log.debug("write(c,off,len)" + cb.getLength() + " " + cb.getLimit()); 515 516 cb.append(c, off, len); 517 charsWritten += len; 518 519 } 520 521 522 public void write(StringBuffer sb) 523 throws IOException { 524 525 if (suspended) 526 return; 527 528 state = CHAR_STATE; 529 530 if (log.isDebugEnabled()) 531 log.debug("write(s,off,len)"); 532 533 int len = sb.length(); 534 charsWritten += len; 535 cb.append(sb); 536 537 } 538 539 540 543 public void write(String s, int off, int len) 544 throws IOException { 545 546 if (suspended) 547 return; 548 549 state=CHAR_STATE; 550 551 if (log.isDebugEnabled()) 552 log.debug("write(s,off,len)"); 553 554 charsWritten += len; 555 if (s==null) 556 s="null"; 557 cb.append( s, off, len ); 558 559 } 560 561 562 public void write(String s) 563 throws IOException { 564 565 if (suspended) 566 return; 567 568 state = CHAR_STATE; 569 if (s==null) 570 s="null"; 571 write(s, 0, s.length()); 572 573 } 574 575 576 public void flushChars() 577 throws IOException { 578 579 if (log.isDebugEnabled()) 580 log.debug("flushChars() " + cb.getLength()); 581 582 cb.flushBuffer(); 583 state = BYTE_STATE; 584 585 } 586 587 588 public boolean flushCharsNeeded() { 589 return state == CHAR_STATE; 590 } 591 592 593 public void setEncoding(String s) { 594 enc = s; 595 } 596 597 598 public void realWriteChars(char c[], int off, int len) 599 throws IOException { 600 601 if (log.isDebugEnabled()) 602 log.debug("realWrite(c,o,l) " + cb.getOffset() + " " + len); 603 604 if (!gotEnc) 605 setConverter(); 606 607 if (log.isDebugEnabled()) 608 log.debug("encoder: " + conv + " " + gotEnc); 609 610 conv.convert(c, off, len); 611 conv.flushBuffer(); 613 } 614 615 616 public void checkConverter() 617 throws IOException { 618 619 if (!gotEnc) 620 setConverter(); 621 622 } 623 624 625 protected void setConverter() 626 throws IOException { 627 628 if (coyoteResponse != null) 629 enc = coyoteResponse.getCharacterEncoding(); 630 631 if (log.isDebugEnabled()) 632 log.debug("Got encoding: " + enc); 633 634 gotEnc = true; 635 if (enc == null) 636 enc = DEFAULT_ENCODING; 637 conv = (C2BConverter) encoders.get(enc); 638 if (conv == null) { 639 if (System.getSecurityManager() != null){ 640 try{ 641 conv = (C2BConverter)AccessController.doPrivileged( 642 new PrivilegedExceptionAction (){ 643 644 public Object run() throws IOException { 645 return new C2BConverter(bb, enc); 646 } 647 648 } 649 ); 650 }catch(PrivilegedActionException ex){ 651 Exception e = ex.getException(); 652 if (e instanceof IOException ) 653 throw (IOException )e; 654 655 if (log.isDebugEnabled()) 656 log.debug("setConverter: " + ex.getMessage()); 657 } 658 } else { 659 conv = new C2BConverter(bb, enc); 660 } 661 encoders.put(enc, conv); 662 663 } 664 } 665 666 667 669 670 673 public void flushBytes() 674 throws IOException { 675 676 if (log.isDebugEnabled()) 677 log.debug("flushBytes() " + bb.getLength()); 678 bb.flushBuffer(); 679 680 } 681 682 683 public int getBytesWritten() { 684 return bytesWritten; 685 } 686 687 688 public int getCharsWritten() { 689 return charsWritten; 690 } 691 692 693 public int getContentWritten() { 694 return bytesWritten + charsWritten; 695 } 696 697 698 702 public boolean isNew() { 703 return (bytesWritten == 0) && (charsWritten == 0); 704 } 705 706 707 public void setBufferSize(int size) { 708 if (size > bb.getLimit()) { bb.setLimit(size); 710 } 711 } 712 713 714 public void reset() { 715 716 bb.recycle(); 718 bytesWritten = 0; 719 cb.recycle(); 720 charsWritten = 0; 721 gotEnc = false; 722 enc = null; 723 724 } 725 726 727 public int getBufferSize() { 728 return bb.getLimit(); 729 } 730 731 } 732 | Popular Tags |