|
What this is
Other links
The source code
/*
* Copyright 1999-2004 The Apache Sofware Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.core;
import java.io.IOException;
import java.io.Writer;
import java.util.Hashtable;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.C2BConverter;
import org.apache.tomcat.util.buf.CharChunk;
/**
* The buffer used by tomcat response. It allows writting chars and
* bytes. It does the mixing in order to implement ServletOutputStream
* ( which has both byte and char methods ) and to allow a number of
* optimizations (like a jsp pre-computing the byte[], but using char for
* non-static content).
*
* @author Costin Manolache
*/
public class OutputBuffer extends Writer
implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel
// sink for conversion bytes[]
{
public static final String DEFAULT_ENCODING="ISO-8859-1";
public static final int DEFAULT_BUFFER_SIZE = 8*1024;
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
// efficient implementations of templating systems )
public final int INITIAL_STATE=0;
public final int CHAR_STATE=1;
public final int BYTE_STATE=2;
private int state=0;
static final int debug=0;
// statistics
private int bytesWritten = 0;
private int charsWritten;
private boolean closed=false;
/** The buffer
*/
private ByteChunk bb;
private CharChunk cb;
private String enc;
private boolean gotEnc=false;
private Response resp;
private Request req;
private ContextManager cm;
public OutputBuffer() {
this( DEFAULT_BUFFER_SIZE );
}
public OutputBuffer(int size) {
// buf=new byte[size];
bb=new ByteChunk( size );
bb.setLimit( size );
bb.setByteOutputChannel( this );
cb=new CharChunk( size );
cb.setCharOutputChannel( this );
cb.setLimit( size );
}
private OutputBuffer(Response resp) {
this( DEFAULT_BUFFER_SIZE );
setResponse( resp );
}
public void setResponse( Response resp ) {
this.resp=resp;
req=resp.getRequest();
cm=req.getContextManager();
}
public byte[] getBuffer() {
return bb.getBuffer();
}
/** Return the first available position in the byte buffer
*( or the number of bytes written ).
* @deprecated Used only in Ajp13Packet for a hack
*/
public int getByteOff() {
return bb.getEnd();
}
/** Set the write position in the byte buffer
* @deprecated Used only in Ajp13Packet for a hack
*/
public void setByteOff(int c) {
bb.setOffset(c);
}
protected void log( String s ) {
System.out.println("OutputBuffer: " + s );
}
/** Sends the buffer data to the client output, checking the
state of Response and calling the right interceptors.
*/
public void realWriteBytes(byte buf[], int off, int cnt )
throws IOException
{
if( debug > 2 ) log( "realWrite(b, " + off + ", " + cnt + ") " + resp);
if( closed ) return;
if( resp==null ) return;
// If this is the first write ( or flush )
if (!resp.isBufferCommitted()) {
resp.endHeaders();
}
// If we really have something to write
if( cnt>0 ) {
// call the beforeCommit callback
BaseInterceptor reqI[]= req.getContainer().
getInterceptors(Container.H_beforeCommit);
for( int i=0; i< reqI.length; i++ ) {
reqI[i].beforeCommit( req, resp );
}
// real write to the adapter
resp.doWrite( buf, off, cnt );
}
}
public void recycle() {
if( debug > 0 ) log("recycle()");
state=INITIAL_STATE;
bytesWritten=0;
charsWritten=0;
cb.recycle();
bb.recycle();
closed=false;
if( conv!= null ) {
conv.recycle();
}
gotEnc=false;
enc=null;
}
// -------------------- Adding bytes to the buffer --------------------
// Like BufferedOutputStream, without sync
public void write(byte b[], int off, int len) throws IOException {
if( state==CHAR_STATE )
cb.flushBuffer();
state=BYTE_STATE;
writeBytes( b, off, len );
}
private void writeBytes(byte b[], int off, int len) throws IOException {
if( closed ) return;
if( debug > 0 ) log("write(b,off,len)");
bb.append( b, off, len );
bytesWritten +=len;
// if called from within flush(), then immediately flush
// remaining bytes
if (doFlush) {
bb.flushBuffer();
}
return;
}
// XXX Char or byte ?
public void writeByte(int b) throws IOException {
if( state==CHAR_STATE )
cb.flushBuffer();
state=BYTE_STATE;
if( debug > 0 ) log("write(b)");
bb.append( (byte)b );
bytesWritten++;
}
// -------------------- Adding chars to the buffer
public void write( int c ) throws IOException {
state=CHAR_STATE;
if( debug > 0 ) log("writeChar(b)");
cb.append( (char )c );
charsWritten++;
}
public void write( char c[] ) throws IOException {
write( c, 0, c.length );
}
public void write(char c[], int off, int len) throws IOException {
state=CHAR_STATE;
if( debug > 0 ) log("write(c,off,len)" + cb.getLength() + " " +
cb.getLimit());
cb.append( c, off, len );
charsWritten += len;
}
public void write( StringBuffer sb ) throws IOException {
state=CHAR_STATE;
if( debug > 1 ) log("write(s,off,len)");
int len=sb.length();
charsWritten += len;
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";
cb.append( s, off, len );
}
public void write(String s) throws IOException {
state=CHAR_STATE;
if (s==null) s="null";
write( s, 0, s.length() );
}
public void flushChars() throws IOException {
if( debug > 0 ) log("flushChars() " + cb.getLength());
cb.flushBuffer();
state=BYTE_STATE;
}
public boolean flushCharsNeeded() {
return state == CHAR_STATE;
}
public void close() throws IOException {
flush();
closed =true;
}
private boolean doFlush = false;
synchronized public void flush() throws IOException {
doFlush = true;
if( state==CHAR_STATE ){
cb.flushBuffer();
bb.flushBuffer();
state=BYTE_STATE;
}else if (state==BYTE_STATE)
bb.flushBuffer();
else if (state==INITIAL_STATE)
realWriteBytes( null, 0, 0 ); // nothing written yet
doFlush = false;
}
protected Hashtable encoders=new Hashtable();
protected C2BConverter conv;
public void setEncoding(String s) {
enc=s;
}
public void realWriteChars( char c[], int off, int len ) throws IOException {
if( debug > 0 ) log("realWrite(c,o,l) " + cb.getOffset() + " " + len);
if( !gotEnc ) setConverter();
if( debug > 0 ) log("encoder: " + conv + " " + gotEnc);
conv.convert(c, off, len);
conv.flushBuffer(); // ???
}
protected void setConverter() {
if( resp!=null )
enc = resp.getCharacterEncoding();
if( debug > 0 ) log("Got encoding: " + enc );
gotEnc=true;
if(enc==null) enc=DEFAULT_ENCODING;
conv=(C2BConverter)encoders.get(enc);
if(conv==null) {
try {
conv=new C2BConverter(bb,enc);
encoders.put(enc, conv);
} catch(IOException ex ) {
conv=(C2BConverter)encoders.get(DEFAULT_ENCODING);
if(conv==null) {
try {
conv=new C2BConverter(bb, DEFAULT_ENCODING);
encoders.put(DEFAULT_ENCODING, conv);
} catch( IOException e ) {}
}
}
}
}
// -------------------- BufferedOutputStream compatibility
/** Real write - this buffer will be sent to the client
*/
public void flushBytes() throws IOException {
if( debug > 0 ) log("flushBytes() " + bb.getLength());
bb.flushBuffer();
}
public int getBytesWritten() {
return bytesWritten;
}
public int getCharsWritten() {
return charsWritten;
}
/** True if this buffer hasn't been used ( since recycle() ) -
i.e. no chars or bytes have been added to the buffer.
*/
public boolean isNew() {
return bytesWritten==0 && charsWritten==0;
}
public void setBufferSize(int size) {
if( size > bb.getLimit() ) {// ??????
bb.setLimit( size );
}
}
public void reset() {
//count=0;
bb.recycle();
bytesWritten=0;
cb.recycle();
charsWritten=0;
gotEnc=false;
enc=null;
}
public int getBufferSize() {
return bb.getLimit();
}
}
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.