Home » apache-tomcat-6.0.26-src » org.apache » coyote » [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.coyote;
   19   
   20   import java.io.IOException;
   21   import java.util.Locale;
   22   
   23   import org.apache.tomcat.util.buf.ByteChunk;
   24   import org.apache.tomcat.util.http.MimeHeaders;
   25   
   26   /**
   27    * Response object.
   28    * 
   29    * @author James Duncan Davidson [duncan@eng.sun.com]
   30    * @author Jason Hunter [jch@eng.sun.com]
   31    * @author James Todd [gonzo@eng.sun.com]
   32    * @author Harish Prabandham
   33    * @author Hans Bergsten <hans@gefionsoftware.com>
   34    * @author Remy Maucherat
   35    */
   36   public final class Response {
   37   
   38   
   39       // ----------------------------------------------------------- Constructors
   40   
   41   
   42       public Response() {
   43       }
   44   
   45   
   46       // ----------------------------------------------------- Class Variables
   47   
   48       /**
   49        * Default locale as mandated by the spec.
   50        */
   51       private static Locale DEFAULT_LOCALE = Locale.getDefault();
   52   
   53   
   54       // ----------------------------------------------------- Instance Variables
   55   
   56       /**
   57        * Status code.
   58        */
   59       protected int status = 200;
   60   
   61   
   62       /**
   63        * Status message.
   64        */
   65       protected String message = null;
   66   
   67   
   68       /**
   69        * Response headers.
   70        */
   71       protected MimeHeaders headers = new MimeHeaders();
   72   
   73   
   74       /**
   75        * Associated output buffer.
   76        */
   77       protected OutputBuffer outputBuffer;
   78   
   79   
   80       /**
   81        * Notes.
   82        */
   83       protected Object notes[] = new Object[Constants.MAX_NOTES];
   84   
   85   
   86       /**
   87        * Committed flag.
   88        */
   89       protected boolean commited = false;
   90   
   91   
   92       /**
   93        * Action hook.
   94        */
   95       public ActionHook hook;
   96   
   97   
   98       /**
   99        * HTTP specific fields.
  100        */
  101       protected String contentType = null;
  102       protected String contentLanguage = null;
  103       protected String characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
  104       protected long contentLength = -1;
  105       private Locale locale = DEFAULT_LOCALE;
  106   
  107       // General informations
  108       private long bytesWritten=0;
  109   
  110       /**
  111        * Holds request error exception.
  112        */
  113       protected Exception errorException = null;
  114   
  115       /**
  116        * Has the charset been explicitly set.
  117        */
  118       protected boolean charsetSet = false;
  119   
  120       /**
  121        * Request error URI.
  122        */
  123       protected String errorURI = null;
  124   
  125       protected Request req;
  126   
  127       // ------------------------------------------------------------- Properties
  128   
  129       public Request getRequest() {
  130           return req;
  131       }
  132   
  133       public void setRequest( Request req ) {
  134           this.req=req;
  135       }
  136   
  137       public OutputBuffer getOutputBuffer() {
  138           return outputBuffer;
  139       }
  140   
  141   
  142       public void setOutputBuffer(OutputBuffer outputBuffer) {
  143           this.outputBuffer = outputBuffer;
  144       }
  145   
  146   
  147       public MimeHeaders getMimeHeaders() {
  148           return headers;
  149       }
  150   
  151   
  152       public ActionHook getHook() {
  153           return hook;
  154       }
  155   
  156   
  157       public void setHook(ActionHook hook) {
  158           this.hook = hook;
  159       }
  160   
  161   
  162       // -------------------- Per-Response "notes" --------------------
  163   
  164   
  165       public final void setNote(int pos, Object value) {
  166           notes[pos] = value;
  167       }
  168   
  169   
  170       public final Object getNote(int pos) {
  171           return notes[pos];
  172       }
  173   
  174   
  175       // -------------------- Actions --------------------
  176   
  177   
  178       public void action(ActionCode actionCode, Object param) {
  179           if (hook != null) {
  180               if( param==null ) 
  181                   hook.action(actionCode, this);
  182               else
  183                   hook.action(actionCode, param);
  184           }
  185       }
  186   
  187   
  188       // -------------------- State --------------------
  189   
  190   
  191       public int getStatus() {
  192           return status;
  193       }
  194   
  195       
  196       /** 
  197        * Set the response status 
  198        */ 
  199       public void setStatus( int status ) {
  200           this.status = status;
  201       }
  202   
  203   
  204       /**
  205        * Get the status message.
  206        */
  207       public String getMessage() {
  208           return message;
  209       }
  210   
  211   
  212       /**
  213        * Set the status message.
  214        */
  215       public void setMessage(String message) {
  216           this.message = message;
  217       }
  218   
  219   
  220       public boolean isCommitted() {
  221           return commited;
  222       }
  223   
  224   
  225       public void setCommitted(boolean v) {
  226           this.commited = v;
  227       }
  228   
  229   
  230       // -----------------Error State --------------------
  231   
  232   
  233       /** 
  234        * Set the error Exception that occurred during
  235        * request processing.
  236        */
  237       public void setErrorException(Exception ex) {
  238       errorException = ex;
  239       }
  240   
  241   
  242       /** 
  243        * Get the Exception that occurred during request
  244        * processing.
  245        */
  246       public Exception getErrorException() {
  247           return errorException;
  248       }
  249   
  250   
  251       public boolean isExceptionPresent() {
  252           return ( errorException != null );
  253       }
  254   
  255   
  256       /** 
  257        * Set request URI that caused an error during
  258        * request processing.
  259        */
  260       public void setErrorURI(String uri) {
  261           errorURI = uri;
  262       }
  263   
  264   
  265       /** Get the request URI that caused the original error.
  266        */
  267       public String getErrorURI() {
  268           return errorURI;
  269       }
  270   
  271   
  272       // -------------------- Methods --------------------
  273       
  274       
  275       public void reset() 
  276           throws IllegalStateException {
  277           
  278           // Reset the headers only if this is the main request,
  279           // not for included
  280           contentType = null;
  281           locale = DEFAULT_LOCALE;
  282           contentLanguage = null;
  283           characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
  284           contentLength = -1;
  285           charsetSet = false;
  286   
  287           status = 200;
  288           message = null;
  289           headers.clear();
  290           
  291           // Force the PrintWriter to flush its data to the output
  292           // stream before resetting the output stream
  293           //
  294           // Reset the stream
  295           if (commited) {
  296               //String msg = sm.getString("servletOutputStreamImpl.reset.ise"); 
  297               throw new IllegalStateException();
  298           }
  299           
  300           action(ActionCode.ACTION_RESET, this);
  301       }
  302   
  303   
  304       public void finish() throws IOException {
  305           action(ActionCode.ACTION_CLOSE, this);
  306       }
  307   
  308   
  309       public void acknowledge() throws IOException {
  310           action(ActionCode.ACTION_ACK, this);
  311       }
  312   
  313   
  314       // -------------------- Headers --------------------
  315       /**
  316        * Warning: This method always returns <code>false<code> for Content-Type
  317        * and Content-Length.
  318        */
  319       public boolean containsHeader(String name) {
  320           return headers.getHeader(name) != null;
  321       }
  322   
  323   
  324       public void setHeader(String name, String value) {
  325           char cc=name.charAt(0);
  326           if( cc=='C' || cc=='c' ) {
  327               if( checkSpecialHeader(name, value) )
  328               return;
  329           }
  330           headers.setValue(name).setString( value);
  331       }
  332   
  333   
  334       public void addHeader(String name, String value) {
  335           char cc=name.charAt(0);
  336           if( cc=='C' || cc=='c' ) {
  337               if( checkSpecialHeader(name, value) )
  338               return;
  339           }
  340           headers.addValue(name).setString( value );
  341       }
  342   
  343       
  344       /** 
  345        * Set internal fields for special header names. 
  346        * Called from set/addHeader.
  347        * Return true if the header is special, no need to set the header.
  348        */
  349       private boolean checkSpecialHeader( String name, String value) {
  350           // XXX Eliminate redundant fields !!!
  351           // ( both header and in special fields )
  352           if( name.equalsIgnoreCase( "Content-Type" ) ) {
  353               setContentType( value );
  354               return true;
  355           }
  356           if( name.equalsIgnoreCase( "Content-Length" ) ) {
  357               try {
  358                   long cL=Long.parseLong( value );
  359                   setContentLength( cL );
  360                   return true;
  361               } catch( NumberFormatException ex ) {
  362                   // Do nothing - the spec doesn't have any "throws" 
  363                   // and the user might know what he's doing
  364                   return false;
  365               }
  366           }
  367           if( name.equalsIgnoreCase( "Content-Language" ) ) {
  368               // XXX XXX Need to construct Locale or something else
  369           }
  370           return false;
  371       }
  372   
  373   
  374       /** Signal that we're done with the headers, and body will follow.
  375        *  Any implementation needs to notify ContextManager, to allow
  376        *  interceptors to fix headers.
  377        */
  378       public void sendHeaders() throws IOException {
  379           action(ActionCode.ACTION_COMMIT, this);
  380           commited = true;
  381       }
  382   
  383   
  384       // -------------------- I18N --------------------
  385   
  386   
  387       public Locale getLocale() {
  388           return locale;
  389       }
  390   
  391       /**
  392        * Called explicitely by user to set the Content-Language and
  393        * the default encoding
  394        */
  395       public void setLocale(Locale locale) {
  396   
  397           if (locale == null) {
  398               return;  // throw an exception?
  399           }
  400   
  401           // Save the locale for use by getLocale()
  402           this.locale = locale;
  403   
  404           // Set the contentLanguage for header output
  405           contentLanguage = locale.getLanguage();
  406           if ((contentLanguage != null) && (contentLanguage.length() > 0)) {
  407               String country = locale.getCountry();
  408               StringBuffer value = new StringBuffer(contentLanguage);
  409               if ((country != null) && (country.length() > 0)) {
  410                   value.append('-');
  411                   value.append(country);
  412               }
  413               contentLanguage = value.toString();
  414           }
  415   
  416       }
  417   
  418       /**
  419        * Return the content language.
  420        */
  421       public String getContentLanguage() {
  422           return contentLanguage;
  423       }
  424   
  425       /*
  426        * Overrides the name of the character encoding used in the body
  427        * of the response. This method must be called prior to writing output
  428        * using getWriter().
  429        *
  430        * @param charset String containing the name of the chararacter encoding.
  431        */
  432       public void setCharacterEncoding(String charset) {
  433   
  434           if (isCommitted())
  435               return;
  436           if (charset == null)
  437               return;
  438   
  439           characterEncoding = charset;
  440           charsetSet=true;
  441       }
  442   
  443       public String getCharacterEncoding() {
  444           return characterEncoding;
  445       }
  446   
  447       /**
  448        * Sets the content type.
  449        *
  450        * This method must preserve any response charset that may already have 
  451        * been set via a call to response.setContentType(), response.setLocale(),
  452        * or response.setCharacterEncoding().
  453        *
  454        * @param type the content type
  455        */
  456       public void setContentType(String type) {
  457   
  458           int semicolonIndex = -1;
  459   
  460           if (type == null) {
  461               this.contentType = null;
  462               return;
  463           }
  464   
  465           /*
  466            * Remove the charset param (if any) from the Content-Type, and use it
  467            * to set the response encoding.
  468            * The most recent response encoding setting will be appended to the
  469            * response's Content-Type (as its charset param) by getContentType();
  470            */
  471           boolean hasCharset = false;
  472           int len = type.length();
  473           int index = type.indexOf(';');
  474           while (index != -1) {
  475               semicolonIndex = index;
  476               index++;
  477               while (index < len && Character.isSpace(type.charAt(index))) {
  478                   index++;
  479               }
  480               if (index+8 < len
  481                       && type.charAt(index) == 'c'
  482                       && type.charAt(index+1) == 'h'
  483                       && type.charAt(index+2) == 'a'
  484                       && type.charAt(index+3) == 'r'
  485                       && type.charAt(index+4) == 's'
  486                       && type.charAt(index+5) == 'e'
  487                       && type.charAt(index+6) == 't'
  488                       && type.charAt(index+7) == '=') {
  489                   hasCharset = true;
  490                   break;
  491               }
  492               index = type.indexOf(';', index);
  493           }
  494   
  495           if (!hasCharset) {
  496               this.contentType = type;
  497               return;
  498           }
  499   
  500           this.contentType = type.substring(0, semicolonIndex);
  501           String tail = type.substring(index+8);
  502           int nextParam = tail.indexOf(';');
  503           String charsetValue = null;
  504           if (nextParam != -1) {
  505               this.contentType += tail.substring(nextParam);
  506               charsetValue = tail.substring(0, nextParam);
  507           } else {
  508               charsetValue = tail;
  509           }
  510   
  511           // The charset value may be quoted, but must not contain any quotes.
  512           if (charsetValue != null && charsetValue.length() > 0) {
  513               charsetSet=true;
  514               charsetValue = charsetValue.replace('"', ' ');
  515               this.characterEncoding = charsetValue.trim();
  516           }
  517       }
  518   
  519       public String getContentType() {
  520   
  521           String ret = contentType;
  522   
  523           if (ret != null 
  524               && characterEncoding != null
  525               && charsetSet) {
  526               ret = ret + ";charset=" + characterEncoding;
  527           }
  528   
  529           return ret;
  530       }
  531       
  532       public void setContentLength(int contentLength) {
  533           this.contentLength = contentLength;
  534       }
  535   
  536       public void setContentLength(long contentLength) {
  537           this.contentLength = contentLength;
  538       }
  539   
  540       public int getContentLength() {
  541           long length = getContentLengthLong();
  542           
  543           if (length < Integer.MAX_VALUE) {
  544               return (int) length;
  545           }
  546           return -1;
  547       }
  548       
  549       public long getContentLengthLong() {
  550           return contentLength;
  551       }
  552   
  553   
  554       /** 
  555        * Write a chunk of bytes.
  556        */
  557       public void doWrite(ByteChunk chunk/*byte buffer[], int pos, int count*/)
  558           throws IOException
  559       {
  560           outputBuffer.doWrite(chunk, this);
  561           bytesWritten+=chunk.getLength();
  562       }
  563   
  564       // --------------------
  565       
  566       public void recycle() {
  567           
  568           contentType = null;
  569           contentLanguage = null;
  570           locale = DEFAULT_LOCALE;
  571           characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
  572           charsetSet = false;
  573           contentLength = -1;
  574           status = 200;
  575           message = null;
  576           commited = false;
  577           errorException = null;
  578           errorURI = null;
  579           headers.clear();
  580   
  581           // update counters
  582           bytesWritten=0;
  583       }
  584   
  585       public long getBytesWritten() {
  586           return bytesWritten;
  587       }
  588   
  589       public void setBytesWritten(long bytesWritten) {
  590           this.bytesWritten = bytesWritten;
  591       }
  592   }

Home » apache-tomcat-6.0.26-src » org.apache » coyote » [javadoc | source]