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

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