Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » web » servlet » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    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.springframework.web.servlet;
   18   
   19   import java.io.IOException;
   20   import java.util.ArrayList;
   21   import java.util.Collections;
   22   import java.util.Enumeration;
   23   import java.util.HashMap;
   24   import java.util.HashSet;
   25   import java.util.Iterator;
   26   import java.util.List;
   27   import java.util.Locale;
   28   import java.util.Map;
   29   import java.util.Properties;
   30   import java.util.Set;
   31   import javax.servlet.ServletException;
   32   import javax.servlet.http.HttpServletRequest;
   33   import javax.servlet.http.HttpServletResponse;
   34   
   35   import org.apache.commons.logging.Log;
   36   import org.apache.commons.logging.LogFactory;
   37   import org.springframework.beans.BeansException;
   38   import org.springframework.beans.factory.BeanFactoryUtils;
   39   import org.springframework.beans.factory.BeanInitializationException;
   40   import org.springframework.beans.factory.NoSuchBeanDefinitionException;
   41   import org.springframework.context.ApplicationContext;
   42   import org.springframework.context.i18n.LocaleContext;
   43   import org.springframework.context.i18n.LocaleContextHolder;
   44   import org.springframework.core.JdkVersion;
   45   import org.springframework.core.OrderComparator;
   46   import org.springframework.core.io.ClassPathResource;
   47   import org.springframework.core.io.support.PropertiesLoaderUtils;
   48   import org.springframework.ui.context.ThemeSource;
   49   import org.springframework.util.ClassUtils;
   50   import org.springframework.util.StringUtils;
   51   import org.springframework.web.HttpRequestMethodNotSupportedException;
   52   import org.springframework.web.context.request.RequestAttributes;
   53   import org.springframework.web.context.request.RequestContextHolder;
   54   import org.springframework.web.context.request.ServletRequestAttributes;
   55   import org.springframework.web.multipart.MultipartException;
   56   import org.springframework.web.multipart.MultipartHttpServletRequest;
   57   import org.springframework.web.multipart.MultipartResolver;
   58   import org.springframework.web.util.NestedServletException;
   59   import org.springframework.web.util.UrlPathHelper;
   60   import org.springframework.web.util.WebUtils;
   61   
   62   /**
   63    * Central dispatcher for HTTP request handlers/controllers,
   64    * e.g. for web UI controllers or HTTP-based remote service exporters.
   65    * Dispatches to registered handlers for processing a web request,
   66    * providing convenient mapping and exception handling facilities.
   67    *
   68    * <p>This servlet is very flexible: It can be used with just about any workflow,
   69    * with the installation of the appropriate adapter classes. It offers the
   70    * following functionality that distinguishes it from other request-driven
   71    * web MVC frameworks:
   72    *
   73    * <ul>
   74    * <li>It is based around a JavaBeans configuration mechanism.
   75    *
   76    * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided
   77    * as part of an application - to control the routing of requests to handler objects.
   78    * Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}, as well
   79    * as a {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}
   80    * when running on Java 5+. HandlerMapping objects can be defined as beans in the servlet's
   81    * application context, implementing the HandlerMapping interface, overriding the default
   82    * HandlerMapping if present. HandlerMappings can be given any bean name (they are tested by type).
   83    *
   84    * <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface.
   85    * Default adapters are {@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter},
   86    * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} and
   87    * {@link org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter},
   88    * for Spring's {@link org.springframework.web.HttpRequestHandler},
   89    * {@link org.springframework.web.servlet.mvc.Controller} and
   90    * {@link org.springframework.web.servlet.mvc.throwaway.ThrowawayController} interfaces,
   91    * respectively. When running in a Java 5+ environment, a default
   92    * {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
   93    * will be registered as well. HandlerAdapter objects can be added as beans in the
   94    * application context, overriding the default HandlerAdapters. Like HandlerMappings,
   95    * HandlerAdapters can be given any bean name (they are tested by type).
   96    *
   97    * <li>The dispatcher's exception resolution strategy can be specified via a
   98    * {@link HandlerExceptionResolver}, for example mapping certain exceptions to
   99    * error pages. Default is none. Additional HandlerExceptionResolvers can be added
  100    * through the application context. HandlerExceptionResolver can be given any
  101    * bean name (they are tested by type).
  102    *
  103    * <li>Its view resolution strategy can be specified via a {@link ViewResolver}
  104    * implementation, resolving symbolic view names into View objects. Default is
  105    * {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
  106    * ViewResolver objects can be added as beans in the application context,
  107    * overriding the default ViewResolver. ViewResolvers can be given any bean name
  108    * (they are tested by type).
  109    *
  110    * <li>If a {@link View} or view name is not supplied by the user, then the configured
  111    * {@link RequestToViewNameTranslator} will translate the current request into a
  112    * view name. The corresponding bean name is "viewNameTranslator"; the default is
  113    * {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
  114    *
  115    * <li>The dispatcher's strategy for resolving multipart requests is determined by
  116    * a {@link org.springframework.web.multipart.MultipartResolver} implementation.
  117    * Implementations for Jakarta Commons FileUpload and Jason Hunter's COS are
  118    * included; the typical choise is
  119    * {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
  120    * The MultipartResolver bean name is "multipartResolver"; default is none.
  121    *
  122    * <li>Its locale resolution strategy is determined by a {@link LocaleResolver}.
  123    * Out-of-the-box implementations work via HTTP accept header, cookie, or session.
  124    * The LocaleResolver bean name is "localeResolver"; default is
  125    * {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
  126    *
  127    * <li>Its theme resolution strategy is determined by a {@link ThemeResolver}.
  128    * Implementations for a fixed theme and for cookie and session storage are included.
  129    * The ThemeResolver bean name is "themeResolver"; default is
  130    * {@link org.springframework.web.servlet.theme.FixedThemeResolver}.
  131    * </ul>
  132    *
  133    * <p><b>NOTE: The <code>@RequestMapping</code> annotation will only be processed
  134    * if a corresponding <code>HandlerMapping</code> (for type level annotations)
  135    * and/or <code>HandlerAdapter</code> (for method level annotations)
  136    * is present in the dispatcher.</b> This is the case by default.
  137    * However, if you are defining custom <code>HandlerMappings</code> or
  138    * <code>HandlerAdapters</code>, then you need to make sure that a
  139    * corresponding custom <code>DefaultAnnotationHandlerMapping</code>
  140    * and/or <code>AnnotationMethodHandlerAdapter</code> is defined as well
  141    * - provided that you intend to use <code>@RequestMapping</code>.
  142    *
  143    * <p><b>A web application can define any number of DispatcherServlets.</b>
  144    * Each servlet will operate in its own namespace, loading its own application
  145    * context with mappings, handlers, etc. Only the root application context
  146    * as loaded by {@link org.springframework.web.context.ContextLoaderListener},
  147    * if any, will be shared.
  148    *
  149    * @author Rod Johnson
  150    * @author Juergen Hoeller
  151    * @author Rob Harrop
  152    * @see org.springframework.web.HttpRequestHandler
  153    * @see org.springframework.web.servlet.mvc.Controller
  154    * @see org.springframework.web.context.ContextLoaderListener
  155    */
  156   public class DispatcherServlet extends FrameworkServlet {
  157   
  158   	/**
  159   	 * Well-known name for the MultipartResolver object in the bean factory for this namespace.
  160   	 */
  161   	public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
  162   
  163   	/**
  164   	 * Well-known name for the LocaleResolver object in the bean factory for this namespace.
  165   	 */
  166   	public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
  167   
  168   	/**
  169   	 * Well-known name for the ThemeResolver object in the bean factory for this namespace.
  170   	 */
  171   	public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
  172   
  173   	/**
  174   	 * Well-known name for the HandlerMapping object in the bean factory for this namespace.
  175   	 * Only used when "detectAllHandlerMappings" is turned off.
  176   	 * @see #setDetectAllHandlerMappings
  177   	 */
  178   	public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
  179   
  180   	/**
  181   	 * Well-known name for the HandlerAdapter object in the bean factory for this namespace.
  182   	 * Only used when "detectAllHandlerAdapters" is turned off.
  183   	 * @see #setDetectAllHandlerAdapters
  184   	 */
  185   	public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
  186   
  187   	/**
  188   	 * Well-known name for the HandlerExceptionResolver object in the bean factory for this
  189   	 * namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off.
  190   	 * @see #setDetectAllHandlerExceptionResolvers
  191   	 */
  192   	public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
  193   
  194   	/**
  195   	 * Well-known name for the RequestToViewNameTranslator object in the bean factory for
  196   	 * this namespace.
  197   	 */
  198   	public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
  199   
  200   	/**
  201   	 * Well-known name for the ViewResolver object in the bean factory for this namespace.
  202   	 * Only used when "detectAllViewResolvers" is turned off.
  203   	 * @see #setDetectAllViewResolvers
  204   	 */
  205   	public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
  206   
  207   	/**
  208   	 * Request attribute to hold the currently chosen HandlerExecutionChain.
  209   	 * Only used for internal optimizations.
  210   	 */
  211   	public static final String HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherServlet.class.getName() + ".HANDLER";
  212   
  213   	/**
  214   	 * Request attribute to hold the current web application context.
  215   	 * Otherwise only the global web app context is obtainable by tags etc.
  216   	 * @see org.springframework.web.servlet.support.RequestContextUtils#getWebApplicationContext
  217   	 */
  218   	public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
  219   
  220   	/**
  221   	 * Request attribute to hold the current LocaleResolver, retrievable by views.
  222   	 * @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver
  223   	 */
  224   	public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
  225   
  226   	/**
  227   	 * Request attribute to hold the current ThemeResolver, retrievable by views.
  228   	 * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver
  229   	 */
  230   	public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
  231   
  232   	/**
  233   	 * Request attribute to hold the current ThemeSource, retrievable by views.
  234   	 * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
  235   	 */
  236   	public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
  237   
  238   
  239   	/**
  240   	 * Log category to use when no mapped handler is found for a request.
  241   	 */
  242   	public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
  243   
  244   	/**
  245   	 * Name of the class path resource (relative to the DispatcherServlet class)
  246   	 * that defines DispatcherServlet's default strategy names.
  247   	 */
  248   	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
  249   
  250   
  251   	/**
  252   	 * Additional logger to use when no mapped handler is found for a request.
  253   	 */
  254   	protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
  255   
  256   	private static final Properties defaultStrategies;
  257   
  258   	static {
  259   		// Load default strategy implementations from properties file.
  260   		// This is currently strictly internal and not meant to be customized
  261   		// by application developers.
  262   		try {
  263   			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
  264   			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
  265   		}
  266   		catch (IOException ex) {
  267   			throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
  268   		}
  269   	}
  270   
  271   
  272   	/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
  273   	private boolean detectAllHandlerMappings = true;
  274   
  275   	/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
  276   	private boolean detectAllHandlerAdapters = true;
  277   
  278   	/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
  279   	private boolean detectAllHandlerExceptionResolvers = true;
  280   
  281   	/** Detect all ViewResolvers or just expect "viewResolver" bean? */
  282   	private boolean detectAllViewResolvers = true;
  283   
  284   	/** Perform cleanup of request attributes after include request? */
  285   	private boolean cleanupAfterInclude = true;
  286   
  287   	/** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
  288   	private boolean threadContextInheritable = false;
  289   
  290   
  291   	/** MultipartResolver used by this servlet */
  292   	private MultipartResolver multipartResolver;
  293   
  294   	/** LocaleResolver used by this servlet */
  295   	private LocaleResolver localeResolver;
  296   
  297   	/** ThemeResolver used by this servlet */
  298   	private ThemeResolver themeResolver;
  299   
  300   	/** List of HandlerMappings used by this servlet */
  301   	private List handlerMappings;
  302   
  303   	/** List of HandlerAdapters used by this servlet */
  304   	private List handlerAdapters;
  305   
  306   	/** List of HandlerExceptionResolvers used by this servlet */
  307   	private List handlerExceptionResolvers;
  308   
  309   	/** RequestToViewNameTranslator used by this servlet */
  310   	private RequestToViewNameTranslator viewNameTranslator;
  311   
  312   	/** List of ViewResolvers used by this servlet */
  313   	private List viewResolvers;
  314   
  315   
  316   	/**
  317   	 * Set whether to detect all HandlerMapping beans in this servlet's context.
  318   	 * Else, just a single bean with name "handlerMapping" will be expected.
  319   	 * <p>Default is "true". Turn this off if you want this servlet to use a
  320   	 * single HandlerMapping, despite multiple HandlerMapping beans being
  321   	 * defined in the context.
  322   	 */
  323   	public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
  324   		this.detectAllHandlerMappings = detectAllHandlerMappings;
  325   	}
  326   
  327   	/**
  328   	 * Set whether to detect all HandlerAdapter beans in this servlet's context.
  329   	 * Else, just a single bean with name "handlerAdapter" will be expected.
  330   	 * <p>Default is "true". Turn this off if you want this servlet to use a
  331   	 * single HandlerAdapter, despite multiple HandlerAdapter beans being
  332   	 * defined in the context.
  333   	 */
  334   	public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
  335   		this.detectAllHandlerAdapters = detectAllHandlerAdapters;
  336   	}
  337   
  338   	/**
  339   	 * Set whether to detect all HandlerExceptionResolver beans in this servlet's context.
  340   	 * Else, just a single bean with name "handlerExceptionResolver" will be expected.
  341   	 * <p>Default is "true". Turn this off if you want this servlet to use a
  342   	 * single HandlerExceptionResolver, despite multiple HandlerExceptionResolver
  343   	 * beans being defined in the context.
  344   	 */
  345   	public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
  346   		this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
  347   	}
  348   
  349   	/**
  350   	 * Set whether to detect all ViewResolver beans in this servlet's context.
  351   	 * Else, just a single bean with name "viewResolver" will be expected.
  352   	 * <p>Default is "true". Turn this off if you want this servlet to use a
  353   	 * single ViewResolver, despite multiple ViewResolver beans being
  354   	 * defined in the context.
  355   	 */
  356   	public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
  357   		this.detectAllViewResolvers = detectAllViewResolvers;
  358   	}
  359   
  360   	/**
  361   	 * Set whether to perform cleanup of request attributes after an include request,
  362   	 * that is, whether to reset the original state of all request attributes after
  363   	 * the DispatcherServlet has processed within an include request. Else, just the
  364   	 * DispatcherServlet's own request attributes will be reset, but not model
  365   	 * attributes for JSPs or special attributes set by views (for example, JSTL's).
  366   	 * <p>Default is "true", which is strongly recommended. Views should not rely on
  367   	 * request attributes having been set by (dynamic) includes. This allows JSP views
  368   	 * rendered by an included controller to use any model attributes, even with the
  369   	 * same names as in the main JSP, without causing side effects. Only turn this
  370   	 * off for special needs, for example to deliberately allow main JSPs to access
  371   	 * attributes from JSP views rendered by an included controller.
  372   	 */
  373   	public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
  374   		this.cleanupAfterInclude = cleanupAfterInclude;
  375   	}
  376   
  377   	/**
  378   	 * Set whether to expose the LocaleContext and RequestAttributes as inheritable
  379   	 * for child threads (using an {@link java.lang.InheritableThreadLocal}).
  380   	 * <p>Default is "false", to avoid side effects on spawned background threads.
  381   	 * Switch this to "true" to enable inheritance for custom child threads which
  382   	 * are spawned during request processing and only used for this request
  383   	 * (that is, ending after their initial task, without reuse of the thread).
  384   	 * <p><b>WARNING:</b> Do not use inheritance for child threads if you are
  385   	 * accessing a thread pool which is configured to potentially add new threads
  386   	 * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
  387   	 * since this will expose the inherited context to such a pooled thread.
  388   	 */
  389   	public void setThreadContextInheritable(boolean threadContextInheritable) {
  390   		this.threadContextInheritable = threadContextInheritable;
  391   	}
  392   
  393   
  394   	/**
  395   	 * This implementation calls {@link #initStrategies}.
  396   	 */
  397   	protected void onRefresh(ApplicationContext context) throws BeansException {
  398   		initStrategies(context);
  399   	}
  400   
  401   	/**
  402   	 * Initialize the strategy objects that this servlet uses.
  403   	 * <p>May be overridden in subclasses in order to initialize
  404   	 * further strategy objects.
  405   	 */
  406   	protected void initStrategies(ApplicationContext context) {
  407   		initMultipartResolver(context);
  408   		initLocaleResolver(context);
  409   		initThemeResolver(context);
  410   		initHandlerMappings(context);
  411   		initHandlerAdapters(context);
  412   		initHandlerExceptionResolvers(context);
  413   		initRequestToViewNameTranslator(context);
  414   		initViewResolvers(context);
  415   	}
  416   
  417   	/**
  418   	 * Initialize the MultipartResolver used by this class.
  419   	 * <p>If no bean is defined with the given name in the BeanFactory
  420   	 * for this namespace, no multipart handling is provided.
  421   	 */
  422   	private void initMultipartResolver(ApplicationContext context) {
  423   		try {
  424   			this.multipartResolver = (MultipartResolver)
  425   					context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
  426   			if (logger.isDebugEnabled()) {
  427   				logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
  428   			}
  429   		}
  430   		catch (NoSuchBeanDefinitionException ex) {
  431   			// Default is no multipart resolver.
  432   			this.multipartResolver = null;
  433   			if (logger.isDebugEnabled()) {
  434   				logger.debug("Unable to locate MultipartResolver with name '"	+ MULTIPART_RESOLVER_BEAN_NAME +
  435   						"': no multipart request handling provided");
  436   			}
  437   		}
  438   	}
  439   
  440   	/**
  441   	 * Initialize the LocaleResolver used by this class.
  442   	 * <p>If no bean is defined with the given name in the BeanFactory
  443   	 * for this namespace, we default to AcceptHeaderLocaleResolver.
  444   	 */
  445   	private void initLocaleResolver(ApplicationContext context) {
  446   		try {
  447   			this.localeResolver = (LocaleResolver)
  448   					context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
  449   			if (logger.isDebugEnabled()) {
  450   				logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
  451   			}
  452   		}
  453   		catch (NoSuchBeanDefinitionException ex) {
  454   			// We need to use the default.
  455   			this.localeResolver = (LocaleResolver) getDefaultStrategy(context, LocaleResolver.class);
  456   			if (logger.isDebugEnabled()) {
  457   				logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
  458   						"': using default [" + this.localeResolver + "]");
  459   			}
  460   		}
  461   	}
  462   
  463   	/**
  464   	 * Initialize the ThemeResolver used by this class.
  465   	 * <p>If no bean is defined with the given name in the BeanFactory
  466   	 * for this namespace, we default to a FixedThemeResolver.
  467   	 */
  468   	private void initThemeResolver(ApplicationContext context) {
  469   		try {
  470   			this.themeResolver = (ThemeResolver)
  471   					context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
  472   			if (logger.isDebugEnabled()) {
  473   				logger.debug("Using ThemeResolver [" + this.themeResolver + "]");
  474   			}
  475   		}
  476   		catch (NoSuchBeanDefinitionException ex) {
  477   			// We need to use the default.
  478   			this.themeResolver = (ThemeResolver) getDefaultStrategy(context, ThemeResolver.class);
  479   			if (logger.isDebugEnabled()) {
  480   				logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME +
  481   						"': using default [" + this.themeResolver + "]");
  482   			}
  483   		}
  484   	}
  485   
  486   	/**
  487   	 * Initialize the HandlerMappings used by this class.
  488   	 * <p>If no HandlerMapping beans are defined in the BeanFactory
  489   	 * for this namespace, we default to BeanNameUrlHandlerMapping.
  490   	 */
  491   	private void initHandlerMappings(ApplicationContext context) {
  492   		this.handlerMappings = null;
  493   
  494   		if (this.detectAllHandlerMappings) {
  495   			// Find all HandlerMappings in the ApplicationContext,
  496   			// including ancestor contexts.
  497   			Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
  498   					context, HandlerMapping.class, true, false);
  499   			if (!matchingBeans.isEmpty()) {
  500   				this.handlerMappings = new ArrayList(matchingBeans.values());
  501   				// We keep HandlerMappings in sorted order.
  502   				Collections.sort(this.handlerMappings, new OrderComparator());
  503   			}
  504   		}
  505   		else {
  506   			try {
  507   				Object hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
  508   				this.handlerMappings = Collections.singletonList(hm);
  509   			}
  510   			catch (NoSuchBeanDefinitionException ex) {
  511   				// Ignore, we'll add a default HandlerMapping later.
  512   			}
  513   		}
  514   
  515   		// Ensure we have at least one HandlerMapping, by registering
  516   		// a default HandlerMapping if no other mappings are found.
  517   		if (this.handlerMappings == null) {
  518   			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
  519   			if (logger.isDebugEnabled()) {
  520   				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
  521   			}
  522   		}
  523   	}
  524   
  525   	/**
  526   	 * Initialize the HandlerAdapters used by this class.
  527   	 * <p>If no HandlerAdapter beans are defined in the BeanFactory
  528   	 * for this namespace, we default to SimpleControllerHandlerAdapter.
  529   	 */
  530   	private void initHandlerAdapters(ApplicationContext context) {
  531   		this.handlerAdapters = null;
  532   
  533   		if (this.detectAllHandlerAdapters) {
  534   			// Find all HandlerAdapters in the ApplicationContext,
  535   			// including ancestor contexts.
  536   			Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
  537   					context, HandlerAdapter.class, true, false);
  538   			if (!matchingBeans.isEmpty()) {
  539   				this.handlerAdapters = new ArrayList(matchingBeans.values());
  540   				// We keep HandlerAdapters in sorted order.
  541   				Collections.sort(this.handlerAdapters, new OrderComparator());
  542   			}
  543   		}
  544   		else {
  545   			try {
  546   				Object ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
  547   				this.handlerAdapters = Collections.singletonList(ha);
  548   			}
  549   			catch (NoSuchBeanDefinitionException ex) {
  550   				// Ignore, we'll add a default HandlerAdapter later.
  551   			}
  552   		}
  553   
  554   		// Ensure we have at least some HandlerAdapters, by registering
  555   		// default HandlerAdapters if no other adapters are found.
  556   		if (this.handlerAdapters == null) {
  557   			this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
  558   			if (logger.isDebugEnabled()) {
  559   				logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
  560   			}
  561   		}
  562   	}
  563   
  564   	/**
  565   	 * Initialize the HandlerExceptionResolver used by this class.
  566   	 * <p>If no bean is defined with the given name in the BeanFactory
  567   	 * for this namespace, we default to no exception resolver.
  568   	 */
  569   	private void initHandlerExceptionResolvers(ApplicationContext context) {
  570   		this.handlerExceptionResolvers = null;
  571   
  572   		if (this.detectAllHandlerExceptionResolvers) {
  573   			// Find all HandlerExceptionResolvers in the ApplicationContext,
  574   			// including ancestor contexts.
  575   			Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
  576   					context, HandlerExceptionResolver.class, true, false);
  577   			if (!matchingBeans.isEmpty()) {
  578   				this.handlerExceptionResolvers = new ArrayList(matchingBeans.values());
  579   				// We keep HandlerExceptionResolvers in sorted order.
  580   				Collections.sort(this.handlerExceptionResolvers, new OrderComparator());
  581   			}
  582   		}
  583   		else {
  584   			try {
  585   				Object her = context.getBean(
  586   						HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
  587   				this.handlerExceptionResolvers = Collections.singletonList(her);
  588   			}
  589   			catch (NoSuchBeanDefinitionException ex) {
  590   				// Ignore, no HandlerExceptionResolver is fine too.
  591   			}
  592   		}
  593   
  594   		// Just for consistency, check for default HandlerExceptionResolvers...
  595   		// There aren't any in usual scenarios.
  596   		if (this.handlerExceptionResolvers == null) {
  597   			this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
  598   			if (logger.isDebugEnabled()) {
  599   				logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
  600   			}
  601   		}
  602   	}
  603   
  604   	/**
  605   	 * Initialize the RequestToViewNameTranslator used by this servlet instance. If no
  606   	 * implementation is configured then we default to DefaultRequestToViewNameTranslator.
  607   	 */
  608   	private void initRequestToViewNameTranslator(ApplicationContext context) {
  609   		try {
  610   			this.viewNameTranslator = (RequestToViewNameTranslator) context.getBean(
  611   					REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
  612   			if (logger.isDebugEnabled()) {
  613   				logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");
  614   			}
  615   		}
  616   		catch (NoSuchBeanDefinitionException ex) {
  617   			// We need to use the default.
  618   			this.viewNameTranslator =
  619   					(RequestToViewNameTranslator) getDefaultStrategy(context, RequestToViewNameTranslator.class);
  620   			if (logger.isDebugEnabled()) {
  621   				logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
  622   						REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
  623   						"': using default [" + this.viewNameTranslator + "]");
  624   			}
  625   		}
  626   	}
  627   
  628   	/**
  629   	 * Initialize the ViewResolvers used by this class.
  630   	 * <p>If no ViewResolver beans are defined in the BeanFactory
  631   	 * for this namespace, we default to InternalResourceViewResolver.
  632   	 */
  633   	private void initViewResolvers(ApplicationContext context) {
  634   		this.viewResolvers = null;
  635   
  636   		if (this.detectAllViewResolvers) {
  637   			// Find all ViewResolvers in the ApplicationContext,
  638   			// including ancestor contexts.
  639   			Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
  640   					context, ViewResolver.class, true, false);
  641   			if (!matchingBeans.isEmpty()) {
  642   				this.viewResolvers = new ArrayList(matchingBeans.values());
  643   				// We keep ViewResolvers in sorted order.
  644   				Collections.sort(this.viewResolvers, new OrderComparator());
  645   			}
  646   		}
  647   		else {
  648   			try {
  649   				Object vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
  650   				this.viewResolvers = Collections.singletonList(vr);
  651   			}
  652   			catch (NoSuchBeanDefinitionException ex) {
  653   				// Ignore, we'll add a default ViewResolver later.
  654   			}
  655   		}
  656   
  657   		// Ensure we have at least one ViewResolver, by registering
  658   		// a default ViewResolver if no other resolvers are found.
  659   		if (this.viewResolvers == null) {
  660   			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
  661   			if (logger.isDebugEnabled()) {
  662   				logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
  663   			}
  664   		}
  665   	}
  666   
  667   	/**
  668   	 * Return this servlet's ThemeSource, if any; else return <code>null</code>.
  669   	 * <p>Default is to return the WebApplicationContext as ThemeSource,
  670   	 * provided that it implements the ThemeSource interface.
  671   	 * @return the ThemeSource, if any
  672   	 * @see #getWebApplicationContext()
  673   	 */
  674   	public final ThemeSource getThemeSource() {
  675   		if (getWebApplicationContext() instanceof ThemeSource) {
  676   			return (ThemeSource) getWebApplicationContext();
  677   		}
  678   		else {
  679   			return null;
  680   		}
  681   	}
  682   
  683   	/**
  684   	 * Obtain this servlet's MultipartResolver, if any.
  685   	 * @return the MultipartResolver used by this servlet, or <code>null</code>
  686   	 * if none (indicating that no multipart support is available)
  687   	 */
  688   	public final MultipartResolver getMultipartResolver() {
  689   		return this.multipartResolver;
  690   	}
  691   
  692   
  693   	/**
  694   	 * Return the default strategy object for the given strategy interface.
  695   	 * <p>The default implementation delegates to {@link #getDefaultStrategies},
  696   	 * expecting a single object in the list.
  697   	 * @param context the current WebApplicationContext
  698   	 * @param strategyInterface the strategy interface
  699   	 * @return the corresponding strategy object
  700   	 * @throws BeansException if initialization failed
  701   	 * @see #getDefaultStrategies
  702   	 */
  703   	protected Object getDefaultStrategy(ApplicationContext context, Class strategyInterface) throws BeansException {
  704   		List strategies = getDefaultStrategies(context, strategyInterface);
  705   		if (strategies.size() != 1) {
  706   			throw new BeanInitializationException(
  707   					"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
  708   		}
  709   		return strategies.get(0);
  710   	}
  711   
  712   	/**
  713   	 * Create a List of default strategy objects for the given strategy interface.
  714   	 * <p>The default implementation uses the "DispatcherServlet.properties" file
  715   	 * (in the same package as the DispatcherServlet class) to determine the class names.
  716   	 * It instantiates the strategy objects through the context's BeanFactory.
  717   	 * @param context the current WebApplicationContext
  718   	 * @param strategyInterface the strategy interface
  719   	 * @return the List of corresponding strategy objects
  720   	 * @throws BeansException if initialization failed
  721   	 */
  722   	protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) throws BeansException {
  723   		String key = strategyInterface.getName();
  724   		List strategies = null;
  725   		String value = defaultStrategies.getProperty(key);
  726   		if (value != null) {
  727   			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
  728   			strategies = new ArrayList(classNames.length);
  729   			for (int i = 0; i < classNames.length; i++) {
  730   				String className = classNames[i];
  731   				if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_15 && className.indexOf("Annotation") != -1) {
  732   					// Skip Java 5 specific strategies when running on JDK 1.4...
  733   					continue;
  734   				}
  735   				try {
  736   					Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
  737   					Object strategy = createDefaultStrategy(context, clazz);
  738   					strategies.add(strategy);
  739   				}
  740   				catch (ClassNotFoundException ex) {
  741   					throw new BeanInitializationException(
  742   							"Could not find DispatcherServlet's default strategy class [" + className +
  743   							"] for interface [" + key + "]", ex);
  744   				}
  745   				catch (LinkageError err) {
  746   					throw new BeanInitializationException(
  747   							"Error loading DispatcherServlet's default strategy class [" + className +
  748   							"] for interface [" + key + "]: problem with class file or dependent class", err);
  749   				}
  750   			}
  751   		}
  752   		else {
  753   			strategies = Collections.EMPTY_LIST;
  754   		}
  755   		return strategies;
  756   	}
  757   
  758   	/**
  759   	 * Create a default strategy.
  760   	 * <p>The default implementation uses
  761   	 * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
  762   	 * @param context the current WebApplicationContext
  763   	 * @param clazz the strategy implementation class to instantiate
  764   	 * @throws BeansException if initialization failed
  765   	 * @return the fully configured strategy instance
  766   	 * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
  767   	 * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
  768   	 */
  769   	protected Object createDefaultStrategy(ApplicationContext context, Class clazz) throws BeansException {
  770   		return context.getAutowireCapableBeanFactory().createBean(clazz);
  771   	}
  772   
  773   
  774   	/**
  775   	 * Exposes the DispatcherServlet-specific request attributes and
  776   	 * delegates to {@link #doDispatch} for the actual dispatching.
  777   	 */
  778   	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  779   		if (logger.isDebugEnabled()) {
  780   			String requestUri = new UrlPathHelper().getRequestUri(request);
  781   			logger.debug("DispatcherServlet with name '" + getServletName() +
  782   					"' processing request for [" + requestUri + "]");
  783   		}
  784   
  785   		// Keep a snapshot of the request attributes in case of an include,
  786   		// to be able to restore the original attributes after the include.
  787   		Map attributesSnapshot = null;
  788   		if (WebUtils.isIncludeRequest(request)) {
  789   			logger.debug("Taking snapshot of request attributes before include");
  790   			attributesSnapshot = new HashMap();
  791   			Enumeration attrNames = request.getAttributeNames();
  792   			while (attrNames.hasMoreElements()) {
  793   				String attrName = (String) attrNames.nextElement();
  794   				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
  795   					attributesSnapshot.put(attrName, request.getAttribute(attrName));
  796   				}
  797   			}
  798   		}
  799   
  800   		// Make framework objects available to handlers and view objects.
  801   		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  802   		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  803   		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  804   		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  805   
  806   		try {
  807   			doDispatch(request, response);
  808   		}
  809   		finally {
  810   			// Restore the original attribute snapshot, in case of an include.
  811   			if (attributesSnapshot != null) {
  812   				restoreAttributesAfterInclude(request, attributesSnapshot);
  813   			}
  814   		}
  815   	}
  816   
  817   	/**
  818   	 * Process the actual dispatching to the handler.
  819   	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
  820   	 * The HandlerAdapter will be obtained by querying the servlet's installed
  821   	 * HandlerAdapters to find the first that supports the handler class.
  822   	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or
  823   	 * handlers themselves to decide which methods are acceptable.
  824   	 * @param request current HTTP request
  825   	 * @param response current HTTP response
  826   	 * @throws Exception in case of any kind of processing failure
  827   	 */
  828   	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  829   		HttpServletRequest processedRequest = request;
  830   		HandlerExecutionChain mappedHandler = null;
  831   		int interceptorIndex = -1;
  832   
  833   		// Expose current LocaleResolver and request as LocaleContext.
  834   		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  835   		LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
  836   
  837   		// Expose current RequestAttributes to current thread.
  838   		RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
  839   		ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
  840   		RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
  841   
  842   		if (logger.isTraceEnabled()) {
  843   			logger.trace("Bound request context to thread: " + request);
  844   		}
  845   		
  846   		try {
  847   			ModelAndView mv = null;
  848   			boolean errorView = false;
  849   
  850   			try {
  851   				processedRequest = checkMultipart(request);
  852   
  853   				// Determine handler for the current request.
  854   				mappedHandler = getHandler(processedRequest, false);
  855   				if (mappedHandler == null || mappedHandler.getHandler() == null) {
  856   					noHandlerFound(processedRequest, response);
  857   					return;
  858   				}
  859   
  860   				// Apply preHandle methods of registered interceptors.
  861   				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
  862   				if (interceptors != null) {
  863   					for (int i = 0; i < interceptors.length; i++) {
  864   						HandlerInterceptor interceptor = interceptors[i];
  865   						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
  866   							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  867   							return;
  868   						}
  869   						interceptorIndex = i;
  870   					}
  871   				}
  872   
  873   				// Actually invoke the handler.
  874   				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  875   				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  876   
  877   				// Do we need view name translation?
  878   				if (mv != null && !mv.hasView()) {
  879   					mv.setViewName(getDefaultViewName(request));
  880   				}
  881   
  882   				// Apply postHandle methods of registered interceptors.
  883   				if (interceptors != null) {
  884   					for (int i = interceptors.length - 1; i >= 0; i--) {
  885   						HandlerInterceptor interceptor = interceptors[i];
  886   						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
  887   					}
  888   				}
  889   			}
  890   			catch (ModelAndViewDefiningException ex) {
  891   				logger.debug("ModelAndViewDefiningException encountered", ex);
  892   				mv = ex.getModelAndView();
  893   			}
  894   			catch (Exception ex) {
  895   				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  896   				mv = processHandlerException(processedRequest, response, handler, ex);
  897   				errorView = (mv != null);
  898   			}
  899   
  900   			// Did the handler return a view to render?
  901   			if (mv != null && !mv.wasCleared()) {
  902   				render(mv, processedRequest, response);
  903   				if (errorView) {
  904   					WebUtils.clearErrorRequestAttributes(request);
  905   				}
  906   			}
  907   			else {
  908   				if (logger.isDebugEnabled()) {
  909   					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
  910   							getServletName() + "': assuming HandlerAdapter completed request handling");
  911   				}
  912   			}
  913   
  914   			// Trigger after-completion for successful outcome.
  915   			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  916   		}
  917   
  918   		catch (Exception ex) {
  919   			// Trigger after-completion for thrown exception.
  920   			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  921   			throw ex;
  922   		}
  923   		catch (Error err) {
  924   			ServletException ex = new NestedServletException("Handler processing failed", err);
  925   			// Trigger after-completion for thrown exception.
  926   			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  927   			throw ex;
  928   		}
  929   
  930   		finally {
  931   			// Clean up any resources used by a multipart request.
  932   			if (processedRequest != request) {
  933   				cleanupMultipart(processedRequest);
  934   			}
  935   
  936   			// Reset thread-bound context.
  937   			RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
  938   			LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
  939   
  940   			// Clear request attributes.
  941   			requestAttributes.requestCompleted();
  942   			if (logger.isTraceEnabled()) {
  943   				logger.trace("Cleared thread-bound request context: " + request);
  944   			}
  945   		}
  946   	}
  947   
  948   	/**
  949   	 * Override HttpServlet's <code>getLastModified</code> method to evaluate
  950   	 * the Last-Modified value of the mapped handler.
  951   	 */
  952   	protected long getLastModified(HttpServletRequest request) {
  953   		if (logger.isDebugEnabled()) {
  954   			String requestUri = new UrlPathHelper().getRequestUri(request);
  955   			logger.debug("DispatcherServlet with name '" + getServletName() +
  956   					"' determining Last-Modified value for [" + requestUri + "]");
  957   		}
  958   		try {
  959   			HandlerExecutionChain mappedHandler = getHandler(request, true);
  960   			if (mappedHandler == null || mappedHandler.getHandler() == null) {
  961   				// Ignore -> will reappear on doService.
  962   				logger.debug("No handler found in getLastModified");
  963   				return -1;
  964   			}
  965   
  966   			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  967   			long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  968   			if (logger.isDebugEnabled()) {
  969   				String requestUri = new UrlPathHelper().getRequestUri(request);
  970   				logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
  971   			}
  972   			return lastModified;
  973   		}
  974   		catch (Exception ex) {
  975   			// Ignore -> will reappear on doService.
  976   			logger.debug("Exception thrown in getLastModified", ex);
  977   			return -1;
  978   		}
  979   	}
  980   
  981   
  982   	/**
  983   	 * Build a LocaleContext for the given request, exposing the request's
  984   	 * primary locale as current locale.
  985   	 * <p>The default implementation uses the dispatcher's LocaleResolver
  986   	 * to obtain the current locale, which might change during a request.
  987   	 * @param request current HTTP request
  988   	 * @return the corresponding LocaleContext
  989   	 */
  990   	protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
  991   		return new LocaleContext() {
  992   			public Locale getLocale() {
  993   				return localeResolver.resolveLocale(request);
  994   			}
  995   			public String toString() {
  996   				return getLocale().toString();
  997   			}
  998   		};
  999   	}
 1000   
 1001   	/**
 1002   	 * Convert the request into a multipart request, and make multipart resolver available.
 1003   	 * If no multipart resolver is set, simply use the existing request.
 1004   	 * @param request current HTTP request
 1005   	 * @return the processed request (multipart wrapper if necessary)
 1006   	 * @see MultipartResolver#resolveMultipart
 1007   	 */
 1008   	protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
 1009   		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
 1010   			if (request instanceof MultipartHttpServletRequest) {
 1011   				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
 1012   						"this typically results from an additional MultipartFilter in web.xml");
 1013   			}
 1014   			else {
 1015   				return this.multipartResolver.resolveMultipart(request);
 1016   			}
 1017   		}
 1018   		// If not returned before: return original request.
 1019   		return request;
 1020   	}
 1021   
 1022   	/**
 1023   	 * Clean up any resources used by the given multipart request (if any).
 1024   	 * @param request current HTTP request
 1025   	 * @see MultipartResolver#cleanupMultipart
 1026   	 */
 1027   	protected void cleanupMultipart(HttpServletRequest request) {
 1028   		if (request instanceof MultipartHttpServletRequest) {
 1029   			this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
 1030   		}
 1031   	}
 1032   
 1033   	/**
 1034   	 * Return the HandlerExecutionChain for this request.
 1035   	 * Try all handler mappings in order.
 1036   	 * @param request current HTTP request
 1037   	 * @param cache whether to cache the HandlerExecutionChain in a request attribute
 1038   	 * @return the HandlerExceutionChain, or <code>null</code> if no handler could be found
 1039   	 */
 1040   	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
 1041   		HandlerExecutionChain handler =
 1042   				(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
 1043   		if (handler != null) {
 1044   			if (!cache) {
 1045   				request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
 1046   			}
 1047   			return handler;
 1048   		}
 1049   
 1050   		Iterator it = this.handlerMappings.iterator();
 1051   		while (it.hasNext()) {
 1052   			HandlerMapping hm = (HandlerMapping) it.next();
 1053   			if (logger.isTraceEnabled()) {
 1054   				logger.trace("Testing handler map [" + hm  + "] in DispatcherServlet with name '" +
 1055   						getServletName() + "'");
 1056   			}
 1057   			handler = hm.getHandler(request);
 1058   			if (handler != null) {
 1059   				if (cache) {
 1060   					request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
 1061   				}
 1062   				return handler;
 1063   			}
 1064   		}
 1065   		return null;
 1066   	}
 1067   
 1068   	/**
 1069   	 * No handler found -> set appropriate HTTP response status.
 1070   	 * @param request current HTTP request
 1071   	 * @param response current HTTP response
 1072   	 * @throws Exception if preparing the response failed
 1073   	 */
 1074   	protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
 1075   		if (pageNotFoundLogger.isWarnEnabled()) {
 1076   			String requestUri = new UrlPathHelper().getRequestUri(request);
 1077   			pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" +
 1078   					requestUri + "] in DispatcherServlet with name '" + getServletName() + "'");
 1079   		}
 1080   		response.sendError(HttpServletResponse.SC_NOT_FOUND);
 1081   	}
 1082   
 1083   	/**
 1084   	 * Return the HandlerAdapter for this handler object.
 1085   	 * @param handler the handler object to find an adapter for
 1086   	 * @throws ServletException if no HandlerAdapter can be found for the handler.
 1087   	 * This is a fatal error.
 1088   	 */
 1089   	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 1090   		Iterator it = this.handlerAdapters.iterator();
 1091   		while (it.hasNext()) {
 1092   			HandlerAdapter ha = (HandlerAdapter) it.next();
 1093   			if (logger.isTraceEnabled()) {
 1094   				logger.trace("Testing handler adapter [" + ha + "]");
 1095   			}
 1096   			if (ha.supports(handler)) {
 1097   				return ha;
 1098   			}
 1099   		}
 1100   		throw new ServletException("No adapter for handler [" + handler +
 1101   				"]: Does your handler implement a supported interface like Controller?");
 1102   	}
 1103   
 1104   	/**
 1105   	 * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
 1106   	 * @param request current HTTP request
 1107   	 * @param response current HTTP response
 1108   	 * @param handler the executed handler, or <code>null</code> if none chosen at the time of
 1109   	 * the exception (for example, if multipart resolution failed)
 1110   	 * @param ex the exception that got thrown during handler execution
 1111   	 * @return a corresponding ModelAndView to forward to
 1112   	 * @throws Exception if no error ModelAndView found
 1113   	 */
 1114   	protected ModelAndView processHandlerException(
 1115   			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
 1116   			throws Exception {
 1117   
 1118   		// Check registerer HandlerExceptionResolvers...
 1119   		ModelAndView exMv = null;
 1120   		for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) {
 1121   			HandlerExceptionResolver resolver = (HandlerExceptionResolver) it.next();
 1122   			exMv = resolver.resolveException(request, response, handler, ex);
 1123   		}
 1124   		if (exMv != null) {
 1125   			if (logger.isDebugEnabled()) {
 1126   				logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
 1127   			}
 1128   			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
 1129   			return exMv;
 1130   		}
 1131   
 1132   		// Send default responses for well-known exceptions, if possible.
 1133   		if (ex instanceof HttpRequestMethodNotSupportedException && !response.isCommitted()) {
 1134   			String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
 1135   			if (supportedMethods != null) {
 1136   				response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
 1137   			}
 1138   			response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
 1139   			return null;
 1140   		}
 1141   
 1142   		throw ex;
 1143   	}
 1144   
 1145   	/**
 1146   	 * Render the given ModelAndView. This is the last stage in handling a request.
 1147   	 * It may involve resolving the view by name.
 1148   	 * @param mv the ModelAndView to render
 1149   	 * @param request current HTTP servlet request
 1150   	 * @param response current HTTP servlet response
 1151   	 * @throws Exception if there's a problem rendering the view
 1152   	 */
 1153   	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
 1154   			throws Exception {
 1155   
 1156   		// Determine locale for request and apply it to the response.
 1157   		Locale locale = this.localeResolver.resolveLocale(request);
 1158   		response.setLocale(locale);
 1159   
 1160   		View view = null;
 1161   
 1162   		if (mv.isReference()) {
 1163   			// We need to resolve the view name.
 1164   			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
 1165   			if (view == null) {
 1166   				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
 1167   						"' in servlet with name '" + getServletName() + "'");
 1168   			}
 1169   		}
 1170   		else {
 1171   			// No need to lookup: the ModelAndView object contains the actual View object.
 1172   			view = mv.getView();
 1173   			if (view == null) {
 1174   				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
 1175   						"View object in servlet with name '" + getServletName() + "'");
 1176   			}
 1177   		}
 1178   
 1179   		// Delegate to the View object for rendering.
 1180   		if (logger.isDebugEnabled()) {
 1181   			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
 1182   		}
 1183   		view.render(mv.getModelInternal(), request, response);
 1184   	}
 1185   
 1186   	/**
 1187   	 * Translate the supplied request into a default view name.
 1188   	 * @param request current HTTP servlet request
 1189   	 * @return the view name (or <code>null</code> if no default found)
 1190   	 * @throws Exception if view name translation failed
 1191   	 */
 1192   	protected String getDefaultViewName(HttpServletRequest request) throws Exception {
 1193   		return this.viewNameTranslator.getViewName(request);
 1194   	}
 1195   
 1196   	/**
 1197   	 * Resolve the given view name into a View object (to be rendered).
 1198   	 * <p>Default implementations asks all ViewResolvers of this dispatcher.
 1199   	 * Can be overridden for custom resolution strategies, potentially based
 1200   	 * on specific model attributes or request parameters.
 1201   	 * @param viewName the name of the view to resolve
 1202   	 * @param model the model to be passed to the view
 1203   	 * @param locale the current locale
 1204   	 * @param request current HTTP servlet request
 1205   	 * @return the View object, or <code>null</code> if none found
 1206   	 * @throws Exception if the view cannot be resolved
 1207   	 * (typically in case of problems creating an actual View object)
 1208   	 * @see ViewResolver#resolveViewName
 1209   	 */
 1210   	protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
 1211   			throws Exception {
 1212   
 1213   		for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
 1214   			ViewResolver viewResolver = (ViewResolver) it.next();
 1215   			View view = viewResolver.resolveViewName(viewName, locale);
 1216   			if (view != null) {
 1217   				return view;
 1218   			}
 1219   		}
 1220   		return null;
 1221   	}
 1222   
 1223   	/**
 1224   	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
 1225   	 * Will just invoke afterCompletion for all interceptors whose preHandle
 1226   	 * invocation has successfully completed and returned true.
 1227   	 * @param mappedHandler the mapped HandlerExecutionChain
 1228   	 * @param interceptorIndex index of last interceptor that successfully completed
 1229   	 * @param ex Exception thrown on handler execution, or <code>null</code> if none
 1230   	 * @see HandlerInterceptor#afterCompletion
 1231   	 */
 1232   	private void triggerAfterCompletion(
 1233   			HandlerExecutionChain mappedHandler, int interceptorIndex,
 1234   			HttpServletRequest request, HttpServletResponse response, Exception ex)
 1235   			throws Exception {
 1236   
 1237   		// Apply afterCompletion methods of registered interceptors.
 1238   		if (mappedHandler != null) {
 1239   			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
 1240   			if (interceptors != null) {
 1241   				for (int i = interceptorIndex; i >= 0; i--) {
 1242   					HandlerInterceptor interceptor = interceptors[i];
 1243   					try {
 1244   						interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
 1245   					}
 1246   					catch (Throwable ex2) {
 1247   						logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
 1248   					}
 1249   				}
 1250   			}
 1251   		}
 1252   	}
 1253   
 1254   	/**
 1255   	 * Restore the request attributes after an include.
 1256   	 * @param request current HTTP request
 1257   	 * @param attributesSnapshot the snapshot of the request attributes
 1258   	 * before the include
 1259   	 */
 1260   	private void restoreAttributesAfterInclude(HttpServletRequest request, Map attributesSnapshot) {
 1261   		logger.debug("Restoring snapshot of request attributes after include");
 1262   
 1263   		// Need to copy into separate Collection here, to avoid side effects
 1264   		// on the Enumeration when removing attributes.
 1265   		Set attrsToCheck = new HashSet();
 1266   		Enumeration attrNames = request.getAttributeNames();
 1267   		while (attrNames.hasMoreElements()) {
 1268   			String attrName = (String) attrNames.nextElement();
 1269   			if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
 1270   				attrsToCheck.add(attrName);
 1271   			}
 1272   		}
 1273   
 1274   		// Iterate over the attributes to check, restoring the original value
 1275   		// or removing the attribute, respectively, if appropriate.
 1276   		for (Iterator it = attrsToCheck.iterator(); it.hasNext();) {
 1277   			String attrName = (String) it.next();
 1278   			Object attrValue = attributesSnapshot.get(attrName);
 1279   			if (attrValue != null) {
 1280   				if (logger.isDebugEnabled()) {
 1281   					logger.debug("Restoring original value of attribute [" + attrName + "] after include");
 1282   				}
 1283   				request.setAttribute(attrName, attrValue);
 1284   			}
 1285   			else {
 1286   				if (logger.isDebugEnabled()) {
 1287   					logger.debug("Removing attribute [" + attrName + "] after include");
 1288   				}
 1289   				request.removeAttribute(attrName);
 1290   			}
 1291   		}
 1292   	}
 1293   
 1294   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » web » servlet » [javadoc | source]