You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/05/21 06:04:10 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/core OutputBuffer.java

costin      01/05/20 21:04:09

  Modified:    src/share/org/apache/tomcat/core OutputBuffer.java
  Log:
  Small refactoring, the API is unchanged but the char->byte encoder
  and the buffers have been moved in util/buf and are now used for the
  url encoder.
  
  The benefit here is cleaner code ( I also added few more comments
  in the new util classes, including a better treatement of the limit )
  
  Revision  Changes    Path
  1.14      +96 -275   jakarta-tomcat/src/share/org/apache/tomcat/core/OutputBuffer.java
  
  Index: OutputBuffer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/OutputBuffer.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- OutputBuffer.java	2001/02/27 02:45:02	1.13
  +++ OutputBuffer.java	2001/05/21 04:04:08	1.14
  @@ -66,6 +66,8 @@
   
   import java.util.Hashtable;
   
  +import org.apache.tomcat.util.buf.*;
  +
   /**
    * The buffer used by tomcat response. It allows writting chars and
    * bytes. It does the mixing in order to implement ServletOutputStream
  @@ -75,10 +77,14 @@
    *
    * @author Costin Manolache
    */
  -public final class OutputBuffer extends Writer {
  +public final class OutputBuffer extends Writer
  +    implements ByteBuffer.ByteOutputChannel, CharBuffer.CharOutputChannel 
  +        // sink for conversion bytes[]
  +{
  +    public static final String DEFAULT_ENCODING="8859_1";
       public static final int DEFAULT_BUFFER_SIZE = 8*1024;
  -    int defaultBufferSize = DEFAULT_BUFFER_SIZE;
  -    int defaultCharBufferSize = DEFAULT_BUFFER_SIZE / 2 ;
  +    private int defaultBufferSize = DEFAULT_BUFFER_SIZE;
  +    private int defaultCharBufferSize = DEFAULT_BUFFER_SIZE / 2 ;
   
       // The buffer can be used for byte[] and char[] writing
       // ( this is needed to support ServletOutputStream and for
  @@ -86,40 +92,44 @@
       public final int INITIAL_STATE=0;
       public final int CHAR_STATE=1;
       public final int BYTE_STATE=2;
  -    int state=0;
  +    private int state=0;
   
       static final int debug=0;
  -    int bytesWritten = 0;
  -    boolean closed=false;
  +
  +    // statistics
  +    private int bytesWritten = 0;
  +    private int charsWritten;
  +    
  +    private boolean closed=false;
   
       /** The buffer
        */
  -    public byte buf[];
  +    private ByteBuffer bb;
  +    private CharBuffer cb;
       
  -    /**
  -     * The index one greater than the index of the last valid byte in 
  -     * the buffer. count==-1 for end of stream
  -     */
  -    public int count;
  -
  +    String enc;
  +    boolean gotEnc=false;
   
  -    Response resp;
  -    Request req;
  -    ContextManager cm;
  +    private Response resp;
  +    private Request req;
  +    private ContextManager cm;
       
       public OutputBuffer() {
  -	buf=new byte[defaultBufferSize];
  - 	cbuf=new char[defaultCharBufferSize];
  +	this( DEFAULT_BUFFER_SIZE );
       }
   
       public OutputBuffer(int size) {
  -	buf=new byte[size];
  - 	cbuf=new char[size];
  +	//	buf=new byte[size];
  +	bb=new ByteBuffer( size );
  +	bb.setLimit( size );
  +	bb.setByteOutputChannel( this );
  + 	cb=new CharBuffer( size );
  +	cb.setCharOutputChannel( this );
  +	cb.setLimit( size );
       }
   
       public OutputBuffer(Response resp) {
  -	buf=new byte[defaultBufferSize];
  - 	cbuf=new char[defaultCharBufferSize];
  +	this( DEFAULT_BUFFER_SIZE );
   	setResponse( resp );
       }
   
  @@ -130,20 +140,22 @@
       }
   
       public byte[] getBuffer() {
  -	return buf;
  +	return bb.getBuffer();
       }
   
       /** Return the first available position in the byte buffer
  -	( or the number of bytes written ).
  -    */
  +     *( or the number of bytes written ).
  +     *  @deprecated Used only in Ajp13Packet for a hack
  +     */
       public int getByteOff() {
  -	return count;
  +	return bb.getPos();
       }
   
       /** Set the write position in the byte buffer
  +     *  @deprecated Used only in Ajp13Packet for a hack
        */
       public void setByteOff(int c) {
  -	count=c;
  +	bb.setPos(c);
       }
   
       void log( String s ) {
  @@ -153,13 +165,14 @@
       /** Sends the buffer data to the client output, checking the
   	state of Response and calling the right interceptors.
       */
  -    private void realWrite(Request req, Response res, byte buf[], int off, int cnt )
  +    public void realWriteBytes(byte buf[], int off, int cnt )
   	throws IOException
       {
  -	if( res==null ) return;
  +	if( debug > 2 ) log( "realWrite(b, " + off + ", " + cnt + ") " + resp);
  +	if( resp==null ) return;
   	// If this is the first write ( or flush )
  -	if (!res.isBufferCommitted()) {
  -	    res.endHeaders();
  +	if (!resp.isBufferCommitted()) {
  +	    resp.endHeaders();
   	}
   	// If we really have something to write
   	if( cnt>0 ) {
  @@ -167,11 +180,11 @@
   	    BaseInterceptor reqI[]= req.getContainer().
   		getInterceptors(Container.H_beforeCommit);
   	    for( int i=0; i< reqI.length; i++ ) {
  -		reqI[i].beforeCommit( req, res );
  +		reqI[i].beforeCommit( req, resp );
   	    }
   
   	    // real write to the adapter
  -	    res.doWrite( buf, off, cnt );
  +	    resp.doWrite( buf, off, cnt );
   	}
       }
       
  @@ -180,13 +193,12 @@
   	state=INITIAL_STATE;
   	bytesWritten=0;
   	charsWritten=0;
  -	ccount=0;
  -	count=0;
  +	
  +	cb.recycle();
  +	bb.recycle(); 
           closed=false;
   	if( conv!= null ) {
  -	    conv.reset(); // reset ?
  -	} else {
  -	    //	    log( "Recycle without conv ??");
  +	    conv.recycle();
   	}
       }
   
  @@ -195,58 +207,22 @@
   
       public void write(byte b[], int off, int len) throws IOException {
   	if( state==CHAR_STATE )
  -	    flushChars();
  +	    cb.flushBuffer();
   	state=BYTE_STATE;
   	writeBytes( b, off, len );
       }
   
  -    public void writeBytes(byte b[], int off, int len) throws IOException {
  +    private void writeBytes(byte b[], int off, int len) throws IOException {
           if( closed  ) return;
   	if( debug > 0 ) log("write(b,off,len)");
  -	int avail=buf.length - count;
   
  +	bb.append( b, off, len );
  +	bytesWritten +=len;
   
  -	// fit in buffer, great.
  -	if( len <= avail ) {
  -	  // ??? should be < since if it's just barely full, we still
  -	  // want to flush now
  -	    System.arraycopy(b, off, buf, count, len);
  -	    count += len;
  -	    bytesWritten += len;
  -	}
  -
  -	// Optimization:
  -	// If len-avail < length ( i.e. after we fill the buffer with
  -	// what we can, the remaining will fit in the buffer ) we'll just
  -	// copy the first part, flush, then copy the second part - 1 write
  -	// and still have some space for more. We'll still have 2 writes, but
  -	// we write more on the first.
  -
  -	else if (len - avail < buf.length) {
  -	    /* If the request length exceeds the size of the output buffer,
  -    	       flush the output buffer and then write the data directly.
  -	       We can't avoid 2 writes, but we can write more on the second
  -	    */
  -	    System.arraycopy(b, off, buf, count, avail);
  -	    count += avail;
  -	    flushBytes();
  -
  -	    System.arraycopy(b, off+avail, buf, count, len - avail);
  -	    count+= len - avail;
  -	    bytesWritten += len - avail;
  -	
  -	} else {	// len > buf.length + avail
  -	    // long write - flush the buffer and write the rest
  -	    // directly from source
  -	    flushBytes();
  -	    bytesWritten += len;
  -	    realWrite( req, resp, b, off, len );
  -	}
  -
   	// if called from within flush(), then immediately flush
   	// remaining bytes
   	if (doFlush) {
  -	  flushBytes();
  +	    bb.flushBuffer();
   	}
   
   	return;
  @@ -255,33 +231,21 @@
       // XXX Char or byte ?
       public void writeByte(int b) throws IOException {
   	if( state==CHAR_STATE )
  -	    flushChars();
  +	    cb.flushBuffer();
   	state=BYTE_STATE;
   	if( debug > 0 ) log("write(b)");
  -	if (count >= buf.length) {
  -	    flushBytes();
  -	}
  -	buf[count++] = (byte)b;
  +
  +	bb.append( (byte)b );
   	bytesWritten++;
       }
   
   
       // -------------------- Adding chars to the buffer
  -    String enc;
  -    boolean gotEnc=false;
  -    public char cbuf[];
  -    /** character count - first free possition */
  -    public int ccount;
  -    int charsWritten;
   
  -
       public void write( int c ) throws IOException {
   	state=CHAR_STATE;
   	if( debug > 0 ) log("writeChar(b)");
  -	if (ccount >= cbuf.length) {
  -	    flushChars();
  -	}
  -	cbuf[ccount++] = (char)c;
  +	cb.append( (char )c );
   	charsWritten++;
       }
   
  @@ -291,88 +255,33 @@
   
       public void write(char c[], int off, int len) throws IOException {
   	state=CHAR_STATE;
  -	if( debug > 0 ) log("write(c,off,len)" + ccount + " " + len);
  -	int avail=cbuf.length - ccount;
  +	if( debug > 0 ) log("write(c,off,len)" + cb.getPos() + " " +
  +			    cb.getLimit());
   
  +	cb.append( c, off, len );
   	charsWritten += len;
  -
  -	// fit in buffer, great.
  -	if( len <= avail ) {
  -	  // ??? should be < since if it's just barely full, we still
  -	  // want to flush now
  -	    System.arraycopy(c, off, cbuf, ccount, len);
  -	    ccount += len;
  -	    return;
  -	}
  -
  -	if (len - avail < cbuf.length) {
  -	    /* If the request length exceeds the size of the output buffer,
  -    	       flush the output buffer and then write the data directly.
  -	       We can't avoid 2 writes, but we can write more on the second
  -	    */
  -	    System.arraycopy(c, off, cbuf, ccount, avail);
  -	    ccount += avail;
  -	    flushChars();
  -	    
  -	    System.arraycopy(c, off+avail, cbuf, ccount, len - avail);
  -	    ccount+= len - avail;
  -	    charsWritten += len - avail;
  -	    return;
  -	}
  -
  -	// len > buf.length + avail
  -	flushChars();
  -	cWrite(  c, off, len );
  -
  -	return;
       }
   
  -    private int min(int a, int b) {
  -	if (a < b) return a;
  -	return b;
  -    }
  -
       public void write( StringBuffer sb ) throws IOException {
   	state=CHAR_STATE;
   	if( debug > 1 ) log("write(s,off,len)");
   	int len=sb.length();
   	charsWritten += len;
   
  -	int off=0;
  -	int b = off;
  -	int t = off + len;
  -	while (b < t) {
  -	    int d = min(cbuf.length - ccount, t - b);
  -	    sb.getChars( b, b+d, cbuf, ccount);
  -	    b += d;
  -	    ccount += d;
  -	    if (ccount >= cbuf.length)
  -		flushChars();
  -	}
  -	return;
  +	cb.append( sb );
       }
   
  +    /** Append a string to the buffer
  +     */
       public void write(String s, int off, int len) throws IOException {
   	state=CHAR_STATE;
   	if( debug > 1 ) log("write(s,off,len)");
   	charsWritten += len;
   	if (s==null) s="null";
  -	
  -	// different strategy: we can't write more then cbuf[]
  -	// because of conversions. Writing in 8k chunks is not bad.
  -	int b = off;
  -	int t = off + len;
  -	while (b < t) {
  -	    int d = min(cbuf.length - ccount, t - b);
  -	    s.getChars( b, b+d, cbuf, ccount);
  -	    b += d;
  -	    ccount += d;
  -	    if (ccount >= cbuf.length)
  -		flushChars();
  -	}
  -	return;
  +
  +	cb.append( s, off, len );
       }
  -    
  +
       public void write(String s) throws IOException {
   	state=CHAR_STATE;
   	if (s==null) s="null";
  @@ -380,11 +289,8 @@
       } 
   
       public void flushChars() throws IOException {
  -	if( debug > 0 ) log("flushChars() " + ccount);
  -	if( ccount > 0) {
  -	    cWrite(  cbuf, 0, ccount );
  -	    ccount=0;
  -	}
  +     	if( debug > 0 ) log("flushChars() " + cb.getPos());
  +	cb.flushBuffer();
       }
   
       public void close() throws IOException {
  @@ -392,54 +298,53 @@
         closed =true;
       }
   
  -  private boolean doFlush = false;
  +    private boolean doFlush = false;
   
       synchronized public void flush() throws IOException {
           doFlush = true;
           if( state==CHAR_STATE ){
  -            flushChars();
  -            flushBytes();
  +            cb.flushBuffer();
  +            bb.flushBuffer();
           }else if (state==BYTE_STATE)
  -            flushBytes();
  +            bb.flushBuffer();
           else if (state==INITIAL_STATE)
  -            realWrite( req, resp, null, 0, 0 );       // nothing written yet
  +            realWriteBytes( null, 0, 0 );       // nothing written yet
           doFlush = false;
       }
   
       Hashtable encoders=new Hashtable();
  -    WriteConvertor conv;
  +    C2BConverter conv;
   
       public void setEncoding(String s) {
   	enc=s;
       }
   
  -    void cWrite( char c[], int off, int len ) throws IOException {
  -	if( debug > 0 ) log("cWrite(c,o,l) " + ccount + " " + len);
  +    public void realWriteChars( char c[], int off, int len ) throws IOException {
  +	if( debug > 0 ) log("realWrite(c,o,l) " + cb.getPos() + " " + len);
   	if( !gotEnc ) setConverter();
   	
   	if( debug > 0 ) log("encoder:  " + conv + " " + gotEnc);
  -	conv.write(c, off, len);
  -	conv.flush();	// ???
  +	conv.convert(c, off, len);
  +	conv.flushBuffer();	// ???
       }
   
       private void setConverter() {
   	if( resp!=null ) 
   	    enc = resp.getCharacterEncoding();
   	gotEnc=true;
  -	if(enc==null) enc="8859_1";
  -	conv=(WriteConvertor)encoders.get(enc);
  +	if(enc==null) enc=DEFAULT_ENCODING;
  +	conv=(C2BConverter)encoders.get(enc);
   	if(conv==null) {
  -	    IntermediateOutputStream ios=new IntermediateOutputStream(this);
   	    try {
  -		conv=new WriteConvertor(ios,enc);
  +		conv=new C2BConverter(bb,enc);
   		encoders.put(enc, conv);
  -	    } catch(UnsupportedEncodingException ex ) {
  -		conv=(WriteConvertor)encoders.get("8859_1");
  +	    } catch(IOException ex ) {
  +		conv=(C2BConverter)encoders.get(DEFAULT_ENCODING);
   		if(conv==null) {
   		    try {
  -			conv=new WriteConvertor(ios, "8859_1");
  -			encoders.put("8859_1", conv);
  -		    } catch( UnsupportedEncodingException e ) {}
  +			conv=new C2BConverter(bb, DEFAULT_ENCODING);
  +			encoders.put(DEFAULT_ENCODING, conv);
  +		    } catch( IOException e ) {}
   		}
   	    }
   	}
  @@ -450,11 +355,8 @@
       /** Real write - this buffer will be sent to the client
        */
       public void flushBytes() throws IOException {
  -	if( debug > 0 ) log("flushBytes() " + count);
  -	if( count > 0) {
  -	    realWrite( req, resp, buf, 0, count );
  -	    count=0;
  -	}
  +	if( debug > 0 ) log("flushBytes() " + bb.getLen());
  +	bb.flushBuffer();
       }
       
       public int getBytesWritten() {
  @@ -473,103 +375,22 @@
       }
       
       public void setBufferSize(int size) {
  -	if( size > buf.length ) {
  -	    buf=new byte[size];
  +	if( size > bb.getLimit() ) {// ??????
  +	    bb.setLimit( size );
   	}
       }
   
       public void reset() {
  -	count=0;
  +	//count=0;
  +	bb.recycle();
   	bytesWritten=0;
  -        ccount=0;
  +        cb.recycle();
           charsWritten=0;
   	gotEnc=false;
   	enc=null;
       }
   
       public int getBufferSize() {
  -	return buf.length;
  -    }
  -
  -
  -    // -------------------- Utils
  -
  -}
  -
  -class WriteConvertor extends OutputStreamWriter {
  -    IntermediateOutputStream ios;
  -    
  -    /* Has a private, internal byte[8192]
  -     */
  -    public WriteConvertor( IntermediateOutputStream out, String enc )
  -	throws UnsupportedEncodingException
  -    {
  -	super( out, enc );
  -	ios=out;
  -    }
  -
  -    public void close() throws IOException {
  -	// NOTHING
  -	// Calling super.close() would reset out and cb.
  -    }
  -
  -    public void flush() throws IOException {
  -	// Will flushBuffer and out()
  -	// flushBuffer put any remaining chars in the byte[] 
  -	super.flush();
  +	return bb.getLimit();
       }
  -
  -    public void write(char cbuf[], int off, int len) throws IOException {
  -	// will do the conversion and call write on the output stream
  -	super.write( cbuf, off, len );
  -    }
  -
  -    void reset() {
  -	ios.resetFlag=true;
  -	try {
  -	    //	    System.out.println("Reseting writer");
  -	    flush();
  -	} catch( Exception ex ) {
  -	    ex.printStackTrace();
  -	}
  -	ios.resetFlag=false;
  -    }
  -	
  -}
  -
  -
  -class IntermediateOutputStream extends OutputStream {
  -    OutputBuffer tbuff;
  -    boolean resetFlag=false;
  -    
  -    public IntermediateOutputStream(OutputBuffer tbuff) {
  -	this.tbuff=tbuff;
  -    }
  -
  -    public void close() throws IOException {
  -	// shouldn't be called - we filter it out in writer
  -	System.out.println("close() called - shouldn't happen ");
  -	throw new IOException("close() called - shouldn't happen ");
  -    }
  -
  -    public void flush() throws IOException {
  -	// nothing - write will go directly to the buffer,
  -	// we don't keep any state
  -    }
  -
  -    public void write(byte cbuf[], int off, int len) throws IOException {
  -	//	System.out.println("IOS: " + len );
  -	// will do the conversion and call write on the output stream
  -	if( resetFlag ) {
  -	    //	    System.out.println("Reseting buffer ");
  -	} else {
  -	    tbuff.writeBytes( cbuf, off, len );
  -	}
  -    }
  -
  -    public void write( int i ) throws IOException {
  -	System.out.println("write(int ) called - shouldn't happen ");
  -	throw new IOException("write(int ) called - shouldn't happen ");
  -    }
   }
  -