Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » core » [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   
   19   package org.apache.catalina.core;
   20   
   21   
   22   import java.io.IOException;
   23   
   24   import javax.servlet.RequestDispatcher;
   25   import javax.servlet.ServletContext;
   26   import javax.servlet.ServletException;
   27   import javax.servlet.http.HttpServletResponse;
   28   
   29   import org.apache.catalina.CometEvent;
   30   import org.apache.catalina.Context;
   31   import org.apache.catalina.Globals;
   32   import org.apache.catalina.Wrapper;
   33   import org.apache.catalina.connector.ClientAbortException;
   34   import org.apache.catalina.connector.Request;
   35   import org.apache.catalina.connector.Response;
   36   import org.apache.catalina.deploy.ErrorPage;
   37   import org.apache.catalina.util.RequestUtil;
   38   import org.apache.catalina.util.StringManager;
   39   import org.apache.catalina.valves.ValveBase;
   40   import org.apache.juli.logging.Log;
   41   import org.apache.juli.logging.LogFactory;
   42   
   43   
   44   /**
   45    * Valve that implements the default basic behavior for the
   46    * <code>StandardHost</code> container implementation.
   47    * <p>
   48    * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
   49    * when processing HTTP requests.
   50    *
   51    * @author Craig R. McClanahan
   52    * @author Remy Maucherat
   53    * @version $Revision: 487694 $ $Date: 2006-12-15 23:30:51 +0100 (ven., 15 déc. 2006) $
   54    */
   55   
   56   final class StandardHostValve
   57       extends ValveBase {
   58   
   59   
   60       private static Log log = LogFactory.getLog(StandardHostValve.class);
   61   
   62       // ----------------------------------------------------- Instance Variables
   63   
   64   
   65       /**
   66        * The descriptive information related to this implementation.
   67        */
   68       private static final String info =
   69           "org.apache.catalina.core.StandardHostValve/1.0";
   70   
   71   
   72       /**
   73        * The string manager for this package.
   74        */
   75       private static final StringManager sm =
   76           StringManager.getManager(Constants.Package);
   77   
   78   
   79       // ------------------------------------------------------------- Properties
   80   
   81   
   82       /**
   83        * Return descriptive information about this Valve implementation.
   84        */
   85       public String getInfo() {
   86   
   87           return (info);
   88   
   89       }
   90   
   91   
   92       // --------------------------------------------------------- Public Methods
   93   
   94   
   95       /**
   96        * Select the appropriate child Context to process this request,
   97        * based on the specified request URI.  If no matching Context can
   98        * be found, return an appropriate HTTP error.
   99        *
  100        * @param request Request to be processed
  101        * @param response Response to be produced
  102        * @param valveContext Valve context used to forward to the next Valve
  103        *
  104        * @exception IOException if an input/output error occurred
  105        * @exception ServletException if a servlet error occurred
  106        */
  107       public final void invoke(Request request, Response response)
  108           throws IOException, ServletException {
  109   
  110           // Select the Context to be used for this Request
  111           Context context = request.getContext();
  112           if (context == null) {
  113               response.sendError
  114                   (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  115                    sm.getString("standardHost.noContext"));
  116               return;
  117           }
  118   
  119           // Bind the context CL to the current thread
  120           if( context.getLoader() != null ) {
  121               // Not started - it should check for availability first
  122               // This should eventually move to Engine, it's generic.
  123               Thread.currentThread().setContextClassLoader
  124                       (context.getLoader().getClassLoader());
  125           }
  126   
  127           // Ask this Context to process this request
  128           context.getPipeline().getFirst().invoke(request, response);
  129   
  130           // Access a session (if present) to update last accessed time, based on a
  131           // strict interpretation of the specification
  132           if (Globals.STRICT_SERVLET_COMPLIANCE) {
  133               request.getSession(false);
  134           }
  135   
  136           // Error page processing
  137           response.setSuspended(false);
  138   
  139           Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
  140   
  141           if (t != null) {
  142               throwable(request, response, t);
  143           } else {
  144               status(request, response);
  145           }
  146   
  147           // Restore the context classloader
  148           Thread.currentThread().setContextClassLoader
  149               (StandardHostValve.class.getClassLoader());
  150   
  151       }
  152   
  153   
  154       /**
  155        * Process Comet event.
  156        *
  157        * @param request Request to be processed
  158        * @param response Response to be produced
  159        * @param valveContext Valve context used to forward to the next Valve
  160        *
  161        * @exception IOException if an input/output error occurred
  162        * @exception ServletException if a servlet error occurred
  163        */
  164       public final void event(Request request, Response response, CometEvent event)
  165           throws IOException, ServletException {
  166   
  167           // Select the Context to be used for this Request
  168           Context context = request.getContext();
  169   
  170           // Bind the context CL to the current thread
  171           if( context.getLoader() != null ) {
  172               // Not started - it should check for availability first
  173               // This should eventually move to Engine, it's generic.
  174               Thread.currentThread().setContextClassLoader
  175                       (context.getLoader().getClassLoader());
  176           }
  177   
  178           // Ask this Context to process this request
  179           context.getPipeline().getFirst().event(request, response, event);
  180   
  181           // Access a session (if present) to update last accessed time, based on a
  182           // strict interpretation of the specification
  183           if (Globals.STRICT_SERVLET_COMPLIANCE) {
  184               request.getSession(false);
  185           }
  186   
  187           // Error page processing
  188           response.setSuspended(false);
  189   
  190           Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
  191   
  192           if (t != null) {
  193               throwable(request, response, t);
  194           } else {
  195               status(request, response);
  196           }
  197   
  198           // Restore the context classloader
  199           Thread.currentThread().setContextClassLoader
  200               (StandardHostValve.class.getClassLoader());
  201   
  202       }
  203   
  204   
  205       // ------------------------------------------------------ Protected Methods
  206   
  207   
  208       /**
  209        * Handle the specified Throwable encountered while processing
  210        * the specified Request to produce the specified Response.  Any
  211        * exceptions that occur during generation of the exception report are
  212        * logged and swallowed.
  213        *
  214        * @param request The request being processed
  215        * @param response The response being generated
  216        * @param throwable The exception that occurred (which possibly wraps
  217        *  a root cause exception
  218        */
  219       protected void throwable(Request request, Response response,
  220                                Throwable throwable) {
  221           Context context = request.getContext();
  222           if (context == null)
  223               return;
  224   
  225           Throwable realError = throwable;
  226   
  227           if (realError instanceof ServletException) {
  228               realError = ((ServletException) realError).getRootCause();
  229               if (realError == null) {
  230                   realError = throwable;
  231               }
  232           }
  233   
  234           // If this is an aborted request from a client just log it and return
  235           if (realError instanceof ClientAbortException ) {
  236               if (log.isDebugEnabled()) {
  237                   log.debug
  238                       (sm.getString("standardHost.clientAbort",
  239                           realError.getCause().getMessage()));
  240               }
  241               return;
  242           }
  243   
  244           ErrorPage errorPage = findErrorPage(context, throwable);
  245           if ((errorPage == null) && (realError != throwable)) {
  246               errorPage = findErrorPage(context, realError);
  247           }
  248   
  249           if (errorPage != null) {
  250               response.setAppCommitted(false);
  251               request.setAttribute
  252                   (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
  253                    errorPage.getLocation());
  254               request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
  255                                 new Integer(ApplicationFilterFactory.ERROR));
  256               request.setAttribute
  257                   (Globals.STATUS_CODE_ATTR,
  258                    new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
  259               request.setAttribute(Globals.ERROR_MESSAGE_ATTR,
  260                                 throwable.getMessage());
  261               request.setAttribute(Globals.EXCEPTION_ATTR,
  262                                 realError);
  263               Wrapper wrapper = request.getWrapper();
  264               if (wrapper != null)
  265                   request.setAttribute(Globals.SERVLET_NAME_ATTR,
  266                                     wrapper.getName());
  267               request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,
  268                                    request.getRequestURI());
  269               request.setAttribute(Globals.EXCEPTION_TYPE_ATTR,
  270                                 realError.getClass());
  271               if (custom(request, response, errorPage)) {
  272                   try {
  273                       response.flushBuffer();
  274                   } catch (IOException e) {
  275                       container.getLogger().warn("Exception Processing " + errorPage, e);
  276                   }
  277               }
  278           } else {
  279               // A custom error-page has not been defined for the exception
  280               // that was thrown during request processing. Check if an
  281               // error-page for error code 500 was specified and if so,
  282               // send that page back as the response.
  283               response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
  284               // The response is an error
  285               response.setError();
  286   
  287               status(request, response);
  288           }
  289   
  290   
  291       }
  292   
  293   
  294       /**
  295        * Handle the HTTP status code (and corresponding message) generated
  296        * while processing the specified Request to produce the specified
  297        * Response.  Any exceptions that occur during generation of the error
  298        * report are logged and swallowed.
  299        *
  300        * @param request The request being processed
  301        * @param response The response being generated
  302        */
  303       protected void status(Request request, Response response) {
  304   
  305           int statusCode = response.getStatus();
  306   
  307           // Handle a custom error page for this status code
  308           Context context = request.getContext();
  309           if (context == null)
  310               return;
  311   
  312           /* Only look for error pages when isError() is set.
  313            * isError() is set when response.sendError() is invoked. This
  314            * allows custom error pages without relying on default from
  315            * web.xml.
  316            */
  317           if (!response.isError())
  318               return;
  319   
  320           ErrorPage errorPage = context.findErrorPage(statusCode);
  321           if (errorPage != null) {
  322               response.setAppCommitted(false);
  323               request.setAttribute(Globals.STATUS_CODE_ATTR,
  324                                 new Integer(statusCode));
  325   
  326               String message = RequestUtil.filter(response.getMessage());
  327               if (message == null)
  328                   message = "";
  329               request.setAttribute(Globals.ERROR_MESSAGE_ATTR, message);
  330               request.setAttribute
  331                   (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
  332                    errorPage.getLocation());
  333               request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
  334                                 new Integer(ApplicationFilterFactory.ERROR));
  335   
  336   
  337               Wrapper wrapper = request.getWrapper();
  338               if (wrapper != null)
  339                   request.setAttribute(Globals.SERVLET_NAME_ATTR,
  340                                     wrapper.getName());
  341               request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,
  342                                    request.getRequestURI());
  343               if (custom(request, response, errorPage)) {
  344                   try {
  345                       response.flushBuffer();
  346                   } catch (ClientAbortException e) {
  347                       // Ignore
  348                   } catch (IOException e) {
  349                       container.getLogger().warn("Exception Processing " + errorPage, e);
  350                   }
  351               }
  352           }
  353   
  354       }
  355   
  356   
  357       /**
  358        * Find and return the ErrorPage instance for the specified exception's
  359        * class, or an ErrorPage instance for the closest superclass for which
  360        * there is such a definition.  If no associated ErrorPage instance is
  361        * found, return <code>null</code>.
  362        *
  363        * @param context The Context in which to search
  364        * @param exception The exception for which to find an ErrorPage
  365        */
  366       protected static ErrorPage findErrorPage
  367           (Context context, Throwable exception) {
  368   
  369           if (exception == null)
  370               return (null);
  371           Class clazz = exception.getClass();
  372           String name = clazz.getName();
  373           while (!Object.class.equals(clazz)) {
  374               ErrorPage errorPage = context.findErrorPage(name);
  375               if (errorPage != null)
  376                   return (errorPage);
  377               clazz = clazz.getSuperclass();
  378               if (clazz == null)
  379                   break;
  380               name = clazz.getName();
  381           }
  382           return (null);
  383   
  384       }
  385   
  386   
  387       /**
  388        * Handle an HTTP status code or Java exception by forwarding control
  389        * to the location included in the specified errorPage object.  It is
  390        * assumed that the caller has already recorded any request attributes
  391        * that are to be forwarded to this page.  Return <code>true</code> if
  392        * we successfully utilized the specified error page location, or
  393        * <code>false</code> if the default error report should be rendered.
  394        *
  395        * @param request The request being processed
  396        * @param response The response being generated
  397        * @param errorPage The errorPage directive we are obeying
  398        */
  399       protected boolean custom(Request request, Response response,
  400                                ErrorPage errorPage) {
  401   
  402           if (container.getLogger().isDebugEnabled())
  403               container.getLogger().debug("Processing " + errorPage);
  404   
  405           request.setPathInfo(errorPage.getLocation());
  406   
  407           try {
  408   
  409               // Reset the response if possible (else IllegalStateException)
  410               //hres.reset();
  411               // Reset the response (keeping the real error code and message)
  412               Integer statusCodeObj =
  413                   (Integer) request.getAttribute(Globals.STATUS_CODE_ATTR);
  414               int statusCode = statusCodeObj.intValue();
  415               String message =
  416                   (String) request.getAttribute(Globals.ERROR_MESSAGE_ATTR);
  417               response.reset(statusCode, message);
  418   
  419               // Forward control to the specified location
  420               ServletContext servletContext =
  421                   request.getContext().getServletContext();
  422               RequestDispatcher rd =
  423                   servletContext.getRequestDispatcher(errorPage.getLocation());
  424               rd.forward(request.getRequest(), response.getResponse());
  425   
  426               // If we forward, the response is suspended again
  427               response.setSuspended(false);
  428   
  429               // Indicate that we have successfully processed this custom page
  430               return (true);
  431   
  432           } catch (Throwable t) {
  433   
  434               // Report our failure to process this custom page
  435               container.getLogger().error("Exception Processing " + errorPage, t);
  436               return (false);
  437   
  438           }
  439   
  440       }
  441   
  442   
  443   }

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