1 16 package org.mortbay.http; 17 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.io.OutputStream ; 21 import java.io.OutputStreamWriter ; 22 import java.io.Writer ; 23 import java.util.ArrayList ; 24 25 import org.apache.commons.logging.Log; 26 import org.mortbay.log.LogFactory; 27 import org.mortbay.util.ByteArrayPool; 28 import org.mortbay.util.IO; 29 import org.mortbay.util.LogSupport; 30 import org.mortbay.util.OutputObserver; 31 import org.mortbay.util.StringUtil; 32 33 34 35 52 public class HttpOutputStream extends OutputStream 53 implements OutputObserver, HttpMessage.HeaderWriter 54 { 55 private static Log log = LogFactory.getLog(HttpOutputStream.class); 56 57 58 final static int __BUFFER_SIZE=4096; 59 final static int __FIRST_RESERVE=512; 60 61 public final static Class [] __filterArg = {java.io.OutputStream .class}; 62 63 64 private OutputStream _out; 65 private OutputStream _realOut; 66 private BufferedOutputStream _bufferedOut; 67 private boolean _written; 68 private ArrayList _observers; 69 private int _bufferSize; 70 private int _headerReserve; 71 private HttpWriter _iso8859writer; 72 private HttpWriter _utf8writer; 73 private HttpWriter _asciiwriter; 74 private boolean _nulled; 75 private boolean _closing=false; 76 private int _contentLength=-1; 77 private int _bytes; 78 private boolean _disableFlush; 79 80 81 84 public HttpOutputStream(OutputStream outputStream) 85 { 86 this (outputStream,__BUFFER_SIZE,__FIRST_RESERVE); 87 } 88 89 90 93 public HttpOutputStream(OutputStream outputStream, int bufferSize) 94 { 95 this (outputStream,bufferSize,__FIRST_RESERVE); 96 } 97 98 99 102 public HttpOutputStream(OutputStream outputStream, 103 int bufferSize, 104 int headerReserve) 105 { 106 _written=false; 107 _bufferSize=bufferSize; 108 _headerReserve=headerReserve; 109 _realOut=outputStream; 110 _out=_realOut; 111 } 112 113 114 public void setContentLength(int length) 115 { 116 if (length>=0 && length<_bytes) 117 throw new IllegalStateException (); 118 _contentLength=length; 119 } 120 121 122 public void setBufferedOutputStream(BufferedOutputStream bos) 123 { 124 _bufferedOut=bos; 125 _bufferedOut.setCommitObserver(this); 126 if (_out!=null && _out!=_realOut) 127 _out=_bufferedOut; 128 } 129 130 131 135 public OutputStream getOutputStream() 136 { 137 return _realOut; 138 } 139 140 141 143 public OutputStream getBufferedOutputStream() 144 { 145 return _out; 146 } 147 148 149 152 public boolean isWritten() 153 { 154 return _written; 155 } 156 157 158 161 public int getBufferSize() 162 { 163 return _bufferSize; 164 } 165 166 167 175 public void setBufferSize(int size) 176 throws IllegalStateException 177 { 178 if (size<=_bufferSize) 179 return; 180 181 if (_bufferedOut!=null && _bufferedOut.size()>0) 182 throw new IllegalStateException ("Not Reset"); 183 184 try 185 { 186 _bufferSize=size; 187 if (_bufferedOut!=null) 188 { 189 boolean fixed=_bufferedOut.isFixed(); 190 _bufferedOut.setFixed(false); 191 _bufferedOut.ensureSize(size); 192 _bufferedOut.setFixed(fixed); 193 } 194 195 } 196 catch (IOException e){log.warn(LogSupport.EXCEPTION,e);} 197 } 198 199 200 public int getBytesWritten() 201 { 202 return _bytes; 203 } 204 205 206 211 public void resetBuffer() 212 throws IllegalStateException 213 { 214 216 if (_out!=null && _out!=_realOut) 217 { 218 ArrayList save_observers=_observers; 219 _observers=null; 220 _nulled=true; 221 try 222 { 223 if (_bufferedOut!=null) 225 { 226 _bufferedOut.resetStream(); 227 if (_bufferedOut instanceof ChunkingOutputStream) 228 ((ChunkingOutputStream)_bufferedOut).setChunking(false); 229 } 230 } 231 catch(Exception e) 232 { 233 LogSupport.ignore(log,e); 234 } 235 finally 236 { 237 _observers=save_observers; 238 } 239 } 240 _contentLength=-1; 241 _nulled=false; 242 _bytes=0; 243 _written=false; 244 _out=_realOut; 245 try 246 { 247 notify(OutputObserver.__RESET_BUFFER); 248 } 249 catch(IOException e) 250 { 251 LogSupport.ignore(log,e); 252 } 253 } 254 255 256 263 public void addObserver(OutputObserver observer) 264 { 265 if (_observers==null) 266 _observers=new ArrayList (4); 267 _observers.add(observer); 268 _observers.add(null); 269 } 270 271 272 280 public void addObserver(OutputObserver observer, Object data) 281 { 282 if (_observers==null) 283 _observers=new ArrayList (4); 284 _observers.add(observer); 285 _observers.add(data); 286 } 287 288 289 291 public void resetObservers() 292 { 293 _observers=null; 294 } 295 296 297 301 public void nullOutput() 302 throws IOException 303 { 304 _nulled=true; 305 } 306 307 308 310 public boolean isNullOutput() 311 throws IOException 312 { 313 return _nulled; 314 } 315 316 317 319 public void setChunking() 320 { 321 checkOutput(); 322 if (_bufferedOut instanceof ChunkingOutputStream) 323 ((ChunkingOutputStream)_bufferedOut).setChunking(true); 324 else 325 throw new IllegalStateException (_bufferedOut.getClass().toString()); 326 } 327 328 329 331 public boolean isChunking() 332 { 333 return (_bufferedOut instanceof ChunkingOutputStream) && 334 ((ChunkingOutputStream)_bufferedOut).isChunking(); 335 } 336 337 338 343 public void resetStream() 344 throws IOException , IllegalStateException 345 { 346 if (isChunking()) 347 close(); 348 349 _out=null; 350 _nulled=true; 351 if (_bufferedOut!=null) 352 { 353 _bufferedOut.resetStream(); 354 if (_bufferedOut instanceof ChunkingOutputStream) 355 ((ChunkingOutputStream)_bufferedOut).setChunking(false); 356 } 357 if (_iso8859writer!=null) 358 _iso8859writer.flush(); 359 if (_utf8writer!=null) 360 _utf8writer.flush(); 361 if (_asciiwriter!=null) 362 _asciiwriter.flush(); 363 364 _bytes=0; 365 _written=false; 366 _out=_realOut; 367 _closing=false; 368 _contentLength=-1; 369 _nulled=false; 370 371 if (_observers!=null) 372 _observers.clear(); 373 } 374 375 376 public void destroy() 377 { 378 if (_bufferedOut!=null) 379 _bufferedOut.destroy(); 380 _bufferedOut=null; 381 if (_iso8859writer!=null) 382 _iso8859writer.destroy(); 383 _iso8859writer=null; 384 if (_utf8writer!=null) 385 _utf8writer.destroy(); 386 _utf8writer=null; 387 if (_asciiwriter!=null) 388 _asciiwriter.destroy(); 389 _asciiwriter=null; 390 } 391 392 393 394 public void writeHeader(HttpMessage httpMessage) 395 throws IOException 396 { 397 checkOutput(); 398 _bufferedOut.writeHeader(httpMessage); 399 } 400 401 402 public void write(int b) throws IOException 403 { 404 prepareOutput(1); 405 if (!_nulled) 406 _out.write(b); 407 if (_bytes==_contentLength) 408 flush(); 409 } 410 411 412 public void write(byte b[]) throws IOException 413 { 414 write(b,0,b.length); 415 } 416 417 418 public void write(byte b[], int off, int len) 419 throws IOException 420 { 421 len=prepareOutput(len); 422 if (!_nulled) 423 _out.write(b,off,len); 424 if (_bytes==_contentLength) 425 flush(); 426 } 427 428 429 protected void checkOutput() 430 { 431 if (_out==_realOut) 432 { 433 if (_bufferedOut==null) 434 { 435 _bufferedOut=new ChunkingOutputStream(_realOut, 436 _bufferSize, 437 _headerReserve, 438 false); 439 _bufferedOut.setCommitObserver(this); 440 _bufferedOut.setBypassBuffer(true); 441 _bufferedOut.setFixed(true); 442 } 443 _out=_bufferedOut; 444 } 445 } 446 447 448 protected int prepareOutput(int length) 449 throws IOException 450 { 451 if (_out==null) 452 throw new IOException ("closed"); 453 checkOutput(); 454 if (!_written) 455 { 456 _written=true; 457 notify(OutputObserver.__FIRST_WRITE); 458 } 459 460 if (_contentLength>=0) 461 { 462 if (_bytes+length>=_contentLength) 463 { 464 length=_contentLength-_bytes; 465 if (length==0) 466 _nulled=true; 467 } 468 } 469 _bytes+=length; 470 return length; 471 } 472 473 474 public void flush() 475 throws IOException 476 { 477 if (!_disableFlush && _out!=null && !_closing) 478 _out.flush(); 479 } 480 481 482 485 public boolean isClosed() 486 throws IOException 487 { 488 return _out==null; 489 } 490 491 492 495 public void close() 496 throws IOException 497 { 498 if (_out==null) 500 return; 501 _closing=true; 502 try { 504 notify(OutputObserver.__CLOSING); 505 506 OutputStream out =_out; 507 _out=null; 508 509 if (out!=_bufferedOut) 510 out.close(); 511 else 512 _bufferedOut.close(); 513 514 notify(OutputObserver.__CLOSED); 515 } 516 catch (IOException e) 517 { 518 LogSupport.ignore(log,e); 519 } 520 } 521 522 523 527 public void outputNotify(OutputStream out, int action, Object ignoredData) 528 throws IOException 529 { 530 notify(action); 531 } 532 533 534 538 private void notify(int action) 539 throws IOException 540 { 541 if (_observers!=null) 542 { 543 for (int i=_observers.size();i-->0;) 544 { 545 Object data=_observers.get(i--); 546 ((OutputObserver)_observers.get(i)).outputNotify(this,action,data); 547 } 548 } 549 } 550 551 552 public void write(InputStream in, int len) 553 throws IOException 554 { 555 IO.copy(in,this,len); 556 } 557 558 559 private Writer getISO8859Writer() 560 throws IOException 561 { 562 if (_iso8859writer==null) 563 _iso8859writer=new HttpWriter(StringUtil.__ISO_8859_1, 564 getBufferSize()); 565 return _iso8859writer; 566 } 567 568 569 private Writer getUTF8Writer() 570 throws IOException 571 { 572 if (_utf8writer==null) 573 _utf8writer=new HttpWriter("UTF-8",getBufferSize()); 574 return _utf8writer; 575 } 576 577 578 private Writer getASCIIWriter() 579 throws IOException 580 { 581 if (_asciiwriter==null) 582 _asciiwriter=new HttpWriter("US-ASCII",getBufferSize()); 583 return _asciiwriter; 584 } 585 586 587 public Writer getWriter(String encoding) 588 throws IOException 589 { 590 if (encoding==null || 591 StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding) || 592 "ISO8859_1".equalsIgnoreCase(encoding)) 593 return getISO8859Writer(); 594 595 if ("UTF-8".equalsIgnoreCase(encoding) || 596 "UTF8".equalsIgnoreCase(encoding)) 597 return getUTF8Writer(); 598 599 if ("US-ASCII".equalsIgnoreCase(encoding)) 600 return getASCIIWriter(); 601 602 return new OutputStreamWriter (this,encoding); 603 } 604 605 606 public String toString() 607 { 608 return super.toString() + 609 "\nout="+_out+ 610 "\nrealOut="+_realOut+ 611 "\nbufferedOut="+_bufferedOut; 612 } 613 614 615 616 private class HttpWriter extends Writer 617 { 618 private OutputStreamWriter _writer=null; 619 private boolean _writting=false; 620 private byte[] _buf; 621 private String _encoding; 622 623 624 HttpWriter(String encoding,int bufferSize) 625 { 626 _buf = ByteArrayPool.getByteArray(bufferSize); 627 _encoding=encoding; 628 } 629 630 631 public Object getLock() 632 { 633 return lock; 634 } 635 636 637 public void write(char c) 638 throws IOException 639 { 640 HttpOutputStream.this.prepareOutput(1); 641 if (!_nulled) 642 { 643 if (_writting) 644 _writer.write(c); 645 else if (c>=0&&c<=0x7f) 646 HttpOutputStream.this.write((int)c); 647 else 648 { 649 char[] ca ={c}; 650 writeEncoded(ca,0,1); 651 } 652 653 if (_bytes==_contentLength) 654 flush(); 655 } 656 } 657 658 659 public void write(char[] ca) 660 throws IOException 661 { 662 this.write(ca,0,ca.length); 663 } 664 665 666 public void write(char[] ca,int offset, int len) 667 throws IOException 668 { 669 if (_writting) 670 _writer.write(ca,offset,len); 671 else 672 { 673 int s=0; 674 for (int i=0;i<len;i++) 675 { 676 char c=ca[offset+i]; 677 if (c>=0&&c<=0x7f) 678 { 679 _buf[s++]=(byte)c; 680 if (s==_buf.length) 681 { 682 s=HttpOutputStream.this.prepareOutput(s); 683 if (!_nulled) 684 HttpOutputStream.this._out.write(_buf,0,s); 685 s=0; 686 } 687 } 688 else 689 { 690 if (s>0) 691 { 692 s=HttpOutputStream.this.prepareOutput(s); 693 if (!_nulled) 694 HttpOutputStream.this._out.write(_buf,0,s); 695 s=0; 696 } 697 writeEncoded(ca,offset+i,len-i); 698 break; 699 } 700 } 701 702 if (s>0) 703 { 704 s=HttpOutputStream.this.prepareOutput(s); 705 if (!_nulled) 706 HttpOutputStream.this._out.write(_buf,0,s); 707 s=0; 708 } 709 } 710 711 if (!_nulled && _bytes==_contentLength) 712 flush(); 713 } 714 715 716 public void write(String s) 717 throws IOException 718 { 719 this.write(s,0,s.length()); 720 } 721 722 723 public void write(String str,int offset, int len) 724 throws IOException 725 { 726 if (_writting) 727 _writer.write(str,offset,len); 728 else 729 { 730 int s=0; 731 for (int i=0;i<len;i++) 732 { 733 char c=str.charAt(offset+i); 734 if (c>=0&&c<=0x7f) 735 { 736 _buf[s++]=(byte)c; 737 if (s==_buf.length) 738 { 739 s=HttpOutputStream.this.prepareOutput(s); 740 if (!_nulled) 741 HttpOutputStream.this._out.write(_buf,0,s); 742 s=0; 743 } 744 } 745 else 746 { 747 if (s>0) 748 { 749 s=HttpOutputStream.this.prepareOutput(s); 750 if (!_nulled) 751 HttpOutputStream.this._out.write(_buf,0,s); 752 s=0; 753 } 754 char[] chars = str.toCharArray(); 755 writeEncoded(chars,offset+i,len-i); 756 break; 757 } 758 } 759 if (s>0) 760 { 761 s=HttpOutputStream.this.prepareOutput(s); 762 if (!_nulled) 763 HttpOutputStream.this._out.write(_buf,0,s); 764 s=0; 765 } 766 } 767 768 if (_bytes==_contentLength) 769 flush(); 770 } 771 772 773 private void writeEncoded(char[] ca,int offset, int length) 774 throws IOException 775 { 776 _writting=true; 777 if (_writer==null) 778 _writer = new OutputStreamWriter (HttpOutputStream.this,_encoding); 779 780 try 781 { 782 HttpOutputStream.this._disableFlush=true; 783 _writer.write(ca,offset,length); 784 if (HttpOutputStream.this._contentLength>=0) 785 _writer.flush(); 786 } 787 finally 788 { 789 HttpOutputStream.this._disableFlush=false; 790 } 791 } 792 793 794 public void flush() 795 throws IOException 796 { 797 if (_writting) 798 _writer.flush(); 799 else 800 HttpOutputStream.this.flush(); 801 _writting=false; 802 } 803 804 805 public void close() 806 throws IOException 807 { 808 _closing=true; 809 if (_writting) 810 _writer.flush(); 811 HttpOutputStream.this.close(); 812 _writting=false; 813 } 814 815 816 public void destroy() 817 { 818 ByteArrayPool.returnByteArray(_buf); 819 _buf=null; 820 _writer=null; 821 _encoding=null; 822 } 823 } 824 827 } 828 | Popular Tags |