Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » coyote » tomcat4 » [javadoc | source]
    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   package org.apache.coyote.tomcat4;
   18   
   19   import java.io.IOException;
   20   import java.io.Writer;
   21   import java.util.Hashtable;
   22   
   23   import org.apache.catalina.connector.ClientAbortException;
   24   import org.apache.coyote.ActionCode;
   25   import org.apache.coyote.Response;
   26   import org.apache.tomcat.util.buf.ByteChunk;
   27   import org.apache.tomcat.util.buf.C2BConverter;
   28   import org.apache.tomcat.util.buf.CharChunk;
   29   
   30   
   31   /**
   32    * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
   33    * OutputBuffer, with the removal of some of the state handling (which in 
   34    * Coyote is mostly the Processor's responsability).
   35    *
   36    * @author Costin Manolache
   37    * @author Remy Maucherat
   38    */
   39   public class OutputBuffer extends Writer
   40       implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
   41   
   42   
   43       private static org.apache.commons.logging.Log log=
   44           org.apache.commons.logging.LogFactory.getLog( OutputBuffer.class );
   45   
   46       // -------------------------------------------------------------- Constants
   47   
   48   
   49       public static final String DEFAULT_ENCODING = 
   50           org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
   51       public static final int DEFAULT_BUFFER_SIZE = 8*1024;
   52       static final int debug = 0;
   53   
   54   
   55       // The buffer can be used for byte[] and char[] writing
   56       // ( this is needed to support ServletOutputStream and for
   57       // efficient implementations of templating systems )
   58       public final int INITIAL_STATE = 0;
   59       public final int CHAR_STATE = 1;
   60       public final int BYTE_STATE = 2;
   61   
   62   
   63       // ----------------------------------------------------- Instance Variables
   64   
   65   
   66       /**
   67        * The byte buffer.
   68        */
   69       private ByteChunk bb;
   70   
   71   
   72       /**
   73        * The chunk buffer.
   74        */
   75       private CharChunk cb;
   76   
   77   
   78       /**
   79        * State of the output buffer.
   80        */
   81       private int state = 0;
   82   
   83   
   84       /**
   85        * Number of bytes written.
   86        */
   87       private int bytesWritten = 0;
   88   
   89   
   90       /**
   91        * Number of chars written.
   92        */
   93       private int charsWritten = 0;
   94   
   95   
   96       /**
   97        * Flag which indicates if the output buffer is closed.
   98        */
   99       private boolean closed = false;
  100   
  101   
  102       /**
  103        * Do a flush on the next operation.
  104        */
  105       private boolean doFlush = false;
  106   
  107   
  108       /**
  109        * Byte chunk used to output bytes.
  110        */
  111       private ByteChunk outputChunk = new ByteChunk();
  112   
  113   
  114       /**
  115        * Encoding to use.
  116        */
  117       private String enc;
  118   
  119   
  120       /**
  121        * Encoder is set.
  122        */
  123       private boolean gotEnc = false;
  124   
  125   
  126       /**
  127        * List of encoders.
  128        */
  129       protected Hashtable encoders = new Hashtable();
  130   
  131   
  132       /**
  133        * Current char to byte converter.
  134        */
  135       protected C2BConverter conv;
  136   
  137   
  138       /**
  139        * Associated Coyote response.
  140        */
  141       private Response coyoteResponse;
  142   
  143   
  144       /**
  145        * Suspended flag. All output bytes will be swallowed if this is true.
  146        */
  147       private boolean suspended = false;
  148   
  149   
  150       // ----------------------------------------------------------- Constructors
  151   
  152   
  153       /**
  154        * Default constructor. Allocate the buffer with the default buffer size.
  155        */
  156       public OutputBuffer() {
  157   
  158           this(DEFAULT_BUFFER_SIZE);
  159   
  160       }
  161   
  162   
  163       /**
  164        * Alternate constructor which allows specifying the initial buffer size.
  165        * 
  166        * @param size Buffer size to use
  167        */
  168       public OutputBuffer(int size) {
  169   
  170           bb = new ByteChunk(size);
  171           bb.setLimit(size);
  172           bb.setByteOutputChannel(this);
  173           cb = new CharChunk(size);
  174           cb.setCharOutputChannel(this);
  175           cb.setLimit(size);
  176   
  177       }
  178   
  179   
  180       // ------------------------------------------------------------- Properties
  181   
  182   
  183       /**
  184        * Associated Coyote response.
  185        * 
  186        * @param coyoteResponse Associated Coyote response
  187        */
  188       public void setResponse(Response coyoteResponse) {
  189   	this.coyoteResponse = coyoteResponse;
  190       }
  191   
  192   
  193       /**
  194        * Get associated Coyote response.
  195        * 
  196        * @return the associated Coyote response
  197        */
  198       public Response getResponse() {
  199           return this.coyoteResponse;
  200       }
  201   
  202   
  203       /**
  204        * Is the response output suspended ?
  205        * 
  206        * @return suspended flag value
  207        */
  208       public boolean isSuspended() {
  209           return this.suspended;
  210       }
  211   
  212   
  213       /**
  214        * Set the suspended flag.
  215        * 
  216        * @param suspended New suspended flag value
  217        */
  218       public void setSuspended(boolean suspended) {
  219           this.suspended = suspended;
  220       }
  221   
  222   
  223       // --------------------------------------------------------- Public Methods
  224   
  225   
  226       /**
  227        * Recycle the output buffer.
  228        */
  229       public void recycle() {
  230   
  231   	if (debug > 0)
  232               log("recycle()");
  233   
  234   	state = INITIAL_STATE;
  235   	bytesWritten = 0;
  236   	charsWritten = 0;
  237   
  238           cb.recycle();
  239           bb.recycle(); 
  240           closed = false;
  241           suspended = false;
  242   
  243           if (conv!= null) {
  244               conv.recycle();
  245           }
  246   
  247           gotEnc = false;
  248           enc = null;
  249   
  250       }
  251   
  252   
  253       /**
  254        * Close the output buffer. This tries to calculate the response size if 
  255        * the response has not been committed yet.
  256        * 
  257        * @throws IOException An underlying IOException occurred
  258        */
  259       public void close()
  260           throws IOException {
  261   
  262           if (closed)
  263               return;
  264           if (suspended)
  265               return;
  266   
  267           if ((!coyoteResponse.isCommitted()) 
  268               && (coyoteResponse.getContentLengthLong() == -1)) {
  269               // Flushing the char buffer
  270               if (state == CHAR_STATE) {
  271                   cb.flushBuffer();
  272                   state = BYTE_STATE;
  273               }
  274               // If this didn't cause a commit of the response, the final content
  275               // length can be calculated
  276               if (!coyoteResponse.isCommitted()) {
  277                   coyoteResponse.setContentLength(bb.getLength());
  278               }
  279           }
  280   
  281           doFlush(false);
  282           closed = true;
  283   
  284           coyoteResponse.finish();
  285   
  286       }
  287   
  288   
  289       /**
  290        * Flush bytes or chars contained in the buffer.
  291        * 
  292        * @throws IOException An underlying IOException occurred
  293        */
  294       public void flush()
  295           throws IOException {
  296           doFlush(true);
  297       }
  298   
  299   
  300       /**
  301        * Flush bytes or chars contained in the buffer.
  302        * 
  303        * @throws IOException An underlying IOException occurred
  304        */
  305       protected void doFlush(boolean realFlush)
  306           throws IOException {
  307   
  308           if (suspended)
  309               return;
  310   
  311           doFlush = true;
  312           if (state == CHAR_STATE) {
  313               cb.flushBuffer();
  314               bb.flushBuffer();
  315               state = BYTE_STATE;
  316           } else if (state == BYTE_STATE) {
  317               bb.flushBuffer();
  318           } else if (state == INITIAL_STATE)
  319               realWriteBytes(null, 0, 0);       // nothing written yet
  320           doFlush = false;
  321   
  322           if (realFlush) {
  323               coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 
  324                                     coyoteResponse);
  325               // If some exception occurred earlier, or if some IOE occurred
  326               // here, notify the servlet with an IOE
  327               if (coyoteResponse.isExceptionPresent()) {
  328                   throw new ClientAbortException
  329                       (coyoteResponse.getErrorException());
  330               }
  331           }
  332   
  333       }
  334   
  335   
  336       // ------------------------------------------------- Bytes Handling Methods
  337   
  338   
  339       /** 
  340        * Sends the buffer data to the client output, checking the
  341        * state of Response and calling the right interceptors.
  342        * 
  343        * @param buf Byte buffer to be written to the response
  344        * @param off Offset
  345        * @param cnt Length
  346        * 
  347        * @throws IOException An underlying IOException occurred
  348        */
  349       public void realWriteBytes(byte buf[], int off, int cnt)
  350   	throws IOException {
  351   
  352           if (debug > 2)
  353               log("realWrite(b, " + off + ", " + cnt + ") " + coyoteResponse);
  354   
  355           if (closed)
  356               return;
  357           if (coyoteResponse == null)
  358               return;
  359   
  360           // If we really have something to write
  361           if (cnt > 0) {
  362               // real write to the adapter
  363               outputChunk.setBytes(buf, off, cnt);
  364               try {
  365                   coyoteResponse.doWrite(outputChunk);
  366               } catch (IOException e) {
  367                   // An IOException on a write is almost always due to
  368                   // the remote client aborting the request.  Wrap this
  369                   // so that it can be handled better by the error dispatcher.
  370                   throw new ClientAbortException(e);
  371               }
  372           }
  373   
  374       }
  375   
  376   
  377       public void write(byte b[], int off, int len) throws IOException {
  378   
  379           if (suspended)
  380               return;
  381   
  382           if (state == CHAR_STATE)
  383               cb.flushBuffer();
  384           state = BYTE_STATE;
  385           writeBytes(b, off, len);
  386   
  387       }
  388   
  389   
  390       private void writeBytes(byte b[], int off, int len) 
  391           throws IOException {
  392   
  393           if (closed)
  394               return;
  395           if (debug > 0)
  396               log("write(b,off,len)");
  397   
  398           bb.append(b, off, len);
  399           bytesWritten += len;
  400   
  401           // if called from within flush(), then immediately flush
  402           // remaining bytes
  403           if (doFlush) {
  404               bb.flushBuffer();
  405           }
  406   
  407       }
  408   
  409   
  410       // XXX Char or byte ?
  411       public void writeByte(int b)
  412           throws IOException {
  413   
  414           if (suspended)
  415               return;
  416   
  417           if (state == CHAR_STATE)
  418               cb.flushBuffer();
  419           state = BYTE_STATE;
  420   
  421           if (debug > 0)
  422               log("write(b)");
  423   
  424           bb.append( (byte)b );
  425           bytesWritten++;
  426   
  427       }
  428   
  429   
  430       // ------------------------------------------------- Chars Handling Methods
  431   
  432   
  433       public void write(int c)
  434           throws IOException {
  435   
  436           if (suspended)
  437               return;
  438   
  439           state = CHAR_STATE;
  440   
  441           if (debug > 0)
  442               log("writeChar(b)");
  443   
  444           cb.append((char) c);
  445           charsWritten++;
  446   
  447       }
  448   
  449   
  450       public void write(char c[])
  451           throws IOException {
  452   
  453           if (suspended)
  454               return;
  455   
  456           write(c, 0, c.length);
  457   
  458       }
  459   
  460   
  461       public void write(char c[], int off, int len)
  462           throws IOException {
  463   
  464           if (suspended)
  465               return;
  466   
  467           state = CHAR_STATE;
  468   
  469           if (debug > 0)
  470               log("write(c,off,len)" + cb.getLength() + " " + cb.getLimit());
  471   
  472           cb.append(c, off, len);
  473           charsWritten += len;
  474   
  475       }
  476   
  477   
  478       public void write(StringBuffer sb)
  479           throws IOException {
  480   
  481           if (suspended)
  482               return;
  483   
  484           state = CHAR_STATE;
  485   
  486           if (debug > 1)
  487               log("write(s,off,len)");
  488   
  489           int len = sb.length();
  490           charsWritten += len;
  491           cb.append(sb);
  492   
  493       }
  494   
  495   
  496       /**
  497        * Append a string to the buffer
  498        */
  499       public void write(String s, int off, int len)
  500           throws IOException {
  501   
  502           if (suspended)
  503               return;
  504   
  505           state=CHAR_STATE;
  506   
  507           if (debug > 1)
  508               log("write(s,off,len)");
  509   
  510           charsWritten += len;
  511           if (s==null)
  512               s="null";
  513           cb.append( s, off, len );
  514   
  515       }
  516   
  517   
  518       public void write(String s)
  519           throws IOException {
  520   
  521           if (suspended)
  522               return;
  523   
  524           state = CHAR_STATE;
  525           if (s==null)
  526               s="null";
  527           write(s, 0, s.length());
  528   
  529       } 
  530   
  531   
  532       public void flushChars()
  533           throws IOException {
  534   
  535           if (debug > 0)
  536               log("flushChars() " + cb.getLength());
  537   
  538           cb.flushBuffer();
  539           state = BYTE_STATE;
  540   
  541       }
  542   
  543   
  544       public boolean flushCharsNeeded() {
  545           return state == CHAR_STATE;
  546       }
  547   
  548   
  549       public void setEncoding(String s) {
  550           enc = s;
  551       }
  552   
  553   
  554       public void realWriteChars(char c[], int off, int len) 
  555           throws IOException {
  556   
  557           if (debug > 0)
  558               log("realWrite(c,o,l) " + cb.getOffset() + " " + len);
  559   
  560           if (!gotEnc)
  561               setConverter();
  562   
  563           if (debug > 0)
  564               log("encoder:  " + conv + " " + gotEnc);
  565   
  566           conv.convert(c, off, len);
  567           conv.flushBuffer();	// ???
  568   
  569       }
  570   
  571   
  572       protected void setConverter() {
  573   
  574           if (coyoteResponse != null)
  575               enc = coyoteResponse.getCharacterEncoding();
  576   
  577           if (debug > 0)
  578               log("Got encoding: " + enc);
  579   
  580           gotEnc = true;
  581           if (enc == null)
  582               enc = DEFAULT_ENCODING;
  583           conv = (C2BConverter) encoders.get(enc);
  584           if (conv == null) {
  585               try {
  586                   conv = new C2BConverter(bb, enc);
  587                   encoders.put(enc, conv);
  588               } catch (IOException e) {
  589                   conv = (C2BConverter) encoders.get(DEFAULT_ENCODING);
  590                   if (conv == null) {
  591                       try {
  592                           conv = new C2BConverter(bb, DEFAULT_ENCODING);
  593                           encoders.put(DEFAULT_ENCODING, conv);
  594                       } catch (IOException ex) {
  595                           // Ignore
  596                       }
  597                   }
  598               }
  599           }
  600       }
  601   
  602       
  603       // --------------------  BufferedOutputStream compatibility
  604   
  605   
  606       /**
  607        * Real write - this buffer will be sent to the client
  608        */
  609       public void flushBytes()
  610           throws IOException {
  611   
  612           if (debug > 0)
  613               log("flushBytes() " + bb.getLength());
  614           bb.flushBuffer();
  615   
  616       }
  617   
  618   
  619       public int getBytesWritten() {
  620           return bytesWritten;
  621       }
  622   
  623   
  624       public int getCharsWritten() {
  625           return charsWritten;
  626       }
  627   
  628   
  629       public int getContentWritten() {
  630           return bytesWritten + charsWritten;
  631       }
  632   
  633   
  634       /** 
  635        * True if this buffer hasn't been used ( since recycle() ) -
  636        * i.e. no chars or bytes have been added to the buffer.  
  637        */
  638       public boolean isNew() {
  639           return (bytesWritten == 0) && (charsWritten == 0);
  640       }
  641   
  642   
  643       public void setBufferSize(int size) {
  644           if (size > bb.getLimit()) {// ??????
  645   	    bb.setLimit(size);
  646   	}
  647       }
  648   
  649   
  650       public void reset() {
  651   
  652           //count=0;
  653           bb.recycle();
  654           bytesWritten = 0;
  655           cb.recycle();
  656           charsWritten = 0;
  657           gotEnc = false;
  658           enc = null;
  659   
  660       }
  661   
  662   
  663       public int getBufferSize() {
  664   	return bb.getLimit();
  665       }
  666   
  667   
  668   
  669       protected void log( String s ) {
  670           if (log.isDebugEnabled()) 
  671               log.debug("OutputBuffer: " + s);
  672       }
  673   
  674   
  675   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » coyote » tomcat4 » [javadoc | source]