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

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