diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index f245b41b4bf..231967867fb 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -22,7 +22,6 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -48,7 +47,6 @@ import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.ui.context.ThemeSource; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; @@ -57,88 +55,65 @@ import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.WebUtils; /** - * Central dispatcher for HTTP request handlers/controllers, - * e.g. for web UI controllers or HTTP-based remote service exporters. - * Dispatches to registered handlers for processing a web request, - * providing convenient mapping and exception handling facilities. + * Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service + * exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception + * handling facilities. * - *

This servlet is very flexible: It can be used with just about any workflow, - * with the installation of the appropriate adapter classes. It offers the - * following functionality that distinguishes it from other request-driven + *

This servlet is very flexible: It can be used with just about any workflow, with the installation of the + * appropriate adapter classes. It offers the following functionality that distinguishes it from other request-driven * web MVC frameworks: * - *

* - *

NOTE: The @RequestMapping annotation will only be processed - * if a corresponding HandlerMapping (for type level annotations) - * and/or HandlerAdapter (for method level annotations) - * is present in the dispatcher. This is the case by default. - * However, if you are defining custom HandlerMappings or - * HandlerAdapters, then you need to make sure that a - * corresponding custom DefaultAnnotationHandlerMapping - * and/or AnnotationMethodHandlerAdapter is defined as well - * - provided that you intend to use @RequestMapping. + *

NOTE: The @RequestMapping annotation will only be processed if a corresponding + * HandlerMapping (for type level annotations) and/or HandlerAdapter (for method level + * annotations) is present in the dispatcher. This is the case by default. However, if you are defining custom + * HandlerMappings or HandlerAdapters, then you need to make sure that a corresponding custom + * DefaultAnnotationHandlerMapping and/or AnnotationMethodHandlerAdapter is defined as well - + * provided that you intend to use @RequestMapping. * - *

A web application can define any number of DispatcherServlets. - * Each servlet will operate in its own namespace, loading its own application - * context with mappings, handlers, etc. Only the root application context - * as loaded by {@link org.springframework.web.context.ContextLoaderListener}, - * if any, will be shared. + *

A web application can define any number of DispatcherServlets. Each servlet will operate in its own + * namespace, loading its own application context with mappings, handlers, etc. Only the root application context as + * loaded by {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared. * * @author Rod Johnson * @author Juergen Hoeller @@ -149,102 +124,92 @@ import org.springframework.web.util.WebUtils; */ public class DispatcherServlet extends FrameworkServlet { - /** - * Well-known name for the MultipartResolver object in the bean factory for this namespace. - */ + /** Well-known name for the MultipartResolver object in the bean factory for this namespace. */ public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; - /** - * Well-known name for the LocaleResolver object in the bean factory for this namespace. - */ + /** Well-known name for the LocaleResolver object in the bean factory for this namespace. */ public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; - /** - * Well-known name for the ThemeResolver object in the bean factory for this namespace. - */ + /** Well-known name for the ThemeResolver object in the bean factory for this namespace. */ public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; /** - * Well-known name for the HandlerMapping object in the bean factory for this namespace. - * Only used when "detectAllHandlerMappings" is turned off. + * Well-known name for the HandlerMapping object in the bean factory for this namespace. Only used when + * "detectAllHandlerMappings" is turned off. + * * @see #setDetectAllHandlerMappings */ public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; /** - * Well-known name for the HandlerAdapter object in the bean factory for this namespace. - * Only used when "detectAllHandlerAdapters" is turned off. + * Well-known name for the HandlerAdapter object in the bean factory for this namespace. Only used when + * "detectAllHandlerAdapters" is turned off. + * * @see #setDetectAllHandlerAdapters */ public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; /** - * Well-known name for the HandlerExceptionResolver object in the bean factory for this - * namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off. + * Well-known name for the HandlerExceptionResolver object in the bean factory for this namespace. Only used when + * "detectAllHandlerExceptionResolvers" is turned off. + * * @see #setDetectAllHandlerExceptionResolvers */ public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; - /** - * Well-known name for the RequestToViewNameTranslator object in the bean factory for - * this namespace. - */ + /** Well-known name for the RequestToViewNameTranslator object in the bean factory for this namespace. */ public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; /** - * Well-known name for the ViewResolver object in the bean factory for this namespace. - * Only used when "detectAllViewResolvers" is turned off. + * Well-known name for the ViewResolver object in the bean factory for this namespace. Only used when + * "detectAllViewResolvers" is turned off. + * * @see #setDetectAllViewResolvers */ public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; - /** - * Request attribute to hold the currently chosen HandlerExecutionChain. - * Only used for internal optimizations. - */ + /** Request attribute to hold the currently chosen HandlerExecutionChain. Only used for internal optimizations. */ public static final String HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherServlet.class.getName() + ".HANDLER"; /** - * Request attribute to hold the current web application context. - * Otherwise only the global web app context is obtainable by tags etc. + * Request attribute to hold the current web application context. Otherwise only the global web app context is + * obtainable by tags etc. + * * @see org.springframework.web.servlet.support.RequestContextUtils#getWebApplicationContext */ public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; /** * Request attribute to hold the current LocaleResolver, retrievable by views. + * * @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver */ public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; /** * Request attribute to hold the current ThemeResolver, retrievable by views. + * * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver */ public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; /** * Request attribute to hold the current ThemeSource, retrievable by views. + * * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource */ public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; - - /** - * Log category to use when no mapped handler is found for a request. - */ + /** Log category to use when no mapped handler is found for a request. */ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /** - * Name of the class path resource (relative to the DispatcherServlet class) - * that defines DispatcherServlet's default strategy names. + * Name of the class path resource (relative to the DispatcherServlet class) that defines DispatcherServlet's default + * strategy names. */ private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; - - /** - * Additional logger to use when no mapped handler is found for a request. - */ + /** Additional logger to use when no mapped handler is found for a request. */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); private static final Properties defaultStrategies; @@ -262,7 +227,6 @@ public class DispatcherServlet extends FrameworkServlet { } } - /** Detect all HandlerMappings or just expect "handlerMapping" bean? */ private boolean detectAllHandlerMappings = true; @@ -278,7 +242,6 @@ public class DispatcherServlet extends FrameworkServlet { /** Perform cleanup of request attributes after include request? */ private boolean cleanupAfterInclude = true; - /** MultipartResolver used by this servlet */ private MultipartResolver multipartResolver; @@ -303,80 +266,65 @@ public class DispatcherServlet extends FrameworkServlet { /** List of ViewResolvers used by this servlet */ private List viewResolvers; - /** - * Set whether to detect all HandlerMapping beans in this servlet's context. - * Else, just a single bean with name "handlerMapping" will be expected. - *

Default is "true". Turn this off if you want this servlet to use a - * single HandlerMapping, despite multiple HandlerMapping beans being - * defined in the context. + * Set whether to detect all HandlerMapping beans in this servlet's context. Else, just a single bean with name + * "handlerMapping" will be expected.

Default is "true". Turn this off if you want this servlet to use a single + * HandlerMapping, despite multiple HandlerMapping beans being defined in the context. */ public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) { this.detectAllHandlerMappings = detectAllHandlerMappings; } /** - * Set whether to detect all HandlerAdapter beans in this servlet's context. - * Else, just a single bean with name "handlerAdapter" will be expected. - *

Default is "true". Turn this off if you want this servlet to use a - * single HandlerAdapter, despite multiple HandlerAdapter beans being - * defined in the context. + * Set whether to detect all HandlerAdapter beans in this servlet's context. Else, just a single bean with name + * "handlerAdapter" will be expected.

Default is "true". Turn this off if you want this servlet to use a single + * HandlerAdapter, despite multiple HandlerAdapter beans being defined in the context. */ public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) { this.detectAllHandlerAdapters = detectAllHandlerAdapters; } /** - * Set whether to detect all HandlerExceptionResolver beans in this servlet's context. - * Else, just a single bean with name "handlerExceptionResolver" will be expected. - *

Default is "true". Turn this off if you want this servlet to use a - * single HandlerExceptionResolver, despite multiple HandlerExceptionResolver - * beans being defined in the context. + * Set whether to detect all HandlerExceptionResolver beans in this servlet's context. Else, just a single bean with + * name "handlerExceptionResolver" will be expected.

Default is "true". Turn this off if you want this servlet to + * use a single HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans being defined in the + * context. */ public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) { this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers; } /** - * Set whether to detect all ViewResolver beans in this servlet's context. - * Else, just a single bean with name "viewResolver" will be expected. - *

Default is "true". Turn this off if you want this servlet to use a - * single ViewResolver, despite multiple ViewResolver beans being - * defined in the context. + * Set whether to detect all ViewResolver beans in this servlet's context. Else, just a single bean with name + * "viewResolver" will be expected.

Default is "true". Turn this off if you want this servlet to use a single + * ViewResolver, despite multiple ViewResolver beans being defined in the context. */ public void setDetectAllViewResolvers(boolean detectAllViewResolvers) { this.detectAllViewResolvers = detectAllViewResolvers; } /** - * Set whether to perform cleanup of request attributes after an include request, - * that is, whether to reset the original state of all request attributes after - * the DispatcherServlet has processed within an include request. Else, just the - * DispatcherServlet's own request attributes will be reset, but not model - * attributes for JSPs or special attributes set by views (for example, JSTL's). - *

Default is "true", which is strongly recommended. Views should not rely on - * request attributes having been set by (dynamic) includes. This allows JSP views - * rendered by an included controller to use any model attributes, even with the - * same names as in the main JSP, without causing side effects. Only turn this - * off for special needs, for example to deliberately allow main JSPs to access - * attributes from JSP views rendered by an included controller. + * Set whether to perform cleanup of request attributes after an include request, that is, whether to reset the + * original state of all request attributes after the DispatcherServlet has processed within an include request. Else, + * just the DispatcherServlet's own request attributes will be reset, but not model attributes for JSPs or special + * attributes set by views (for example, JSTL's).

Default is "true", which is strongly recommended. Views should not + * rely on request attributes having been set by (dynamic) includes. This allows JSP views rendered by an included + * controller to use any model attributes, even with the same names as in the main JSP, without causing side effects. + * Only turn this off for special needs, for example to deliberately allow main JSPs to access attributes from JSP + * views rendered by an included controller. */ public void setCleanupAfterInclude(boolean cleanupAfterInclude) { this.cleanupAfterInclude = cleanupAfterInclude; } - - /** - * This implementation calls {@link #initStrategies}. - */ + /** This implementation calls {@link #initStrategies}. */ @Override protected void onRefresh(ApplicationContext context) throws BeansException { initStrategies(context); } /** - * Initialize the strategy objects that this servlet uses. - *

May be overridden in subclasses in order to initialize + * Initialize the strategy objects that this servlet uses.

May be overridden in subclasses in order to initialize * further strategy objects. */ protected void initStrategies(ApplicationContext context) { @@ -391,8 +339,7 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Initialize the MultipartResolver used by this class. - *

If no bean is defined with the given name in the BeanFactory + * Initialize the MultipartResolver used by this class.

If no bean is defined with the given name in the BeanFactory * for this namespace, no multipart handling is provided. */ private void initMultipartResolver(ApplicationContext context) { @@ -406,15 +353,14 @@ public class DispatcherServlet extends FrameworkServlet { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isDebugEnabled()) { - logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + + logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + "': no multipart request handling provided"); } } } /** - * Initialize the LocaleResolver used by this class. - *

If no bean is defined with the given name in the BeanFactory + * Initialize the LocaleResolver used by this class.

If no bean is defined with the given name in the BeanFactory * for this namespace, we default to AcceptHeaderLocaleResolver. */ private void initLocaleResolver(ApplicationContext context) { @@ -435,9 +381,8 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Initialize the ThemeResolver used by this class. - *

If no bean is defined with the given name in the BeanFactory - * for this namespace, we default to a FixedThemeResolver. + * Initialize the ThemeResolver used by this class.

If no bean is defined with the given name in the BeanFactory for + * this namespace, we default to a FixedThemeResolver. */ private void initThemeResolver(ApplicationContext context) { try { @@ -450,24 +395,24 @@ public class DispatcherServlet extends FrameworkServlet { // We need to use the default. this.themeResolver = getDefaultStrategy(context, ThemeResolver.class); if (logger.isDebugEnabled()) { - logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + - "': using default [" + this.themeResolver + "]"); + logger.debug( + "Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default [" + + this.themeResolver + "]"); } } } /** - * Initialize the HandlerMappings used by this class. - *

If no HandlerMapping beans are defined in the BeanFactory - * for this namespace, we default to BeanNameUrlHandlerMapping. + * Initialize the HandlerMappings used by this class.

If no HandlerMapping beans are defined in the BeanFactory for + * this namespace, we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. - Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( - context, HandlerMapping.class, true, false); + Map matchingBeans = + BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList(matchingBeans.values()); // We keep HandlerMappings in sorted order. @@ -495,17 +440,16 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Initialize the HandlerAdapters used by this class. - *

If no HandlerAdapter beans are defined in the BeanFactory - * for this namespace, we default to SimpleControllerHandlerAdapter. + * Initialize the HandlerAdapters used by this class.

If no HandlerAdapter beans are defined in the BeanFactory for + * this namespace, we default to SimpleControllerHandlerAdapter. */ private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. - Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( - context, HandlerAdapter.class, true, false); + Map matchingBeans = + BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList(matchingBeans.values()); // We keep HandlerAdapters in sorted order. @@ -533,17 +477,16 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Initialize the HandlerExceptionResolver used by this class. - *

If no bean is defined with the given name in the BeanFactory - * for this namespace, we default to no exception resolver. + * Initialize the HandlerExceptionResolver used by this class.

If no bean is defined with the given name in the + * BeanFactory for this namespace, we default to no exception resolver. */ private void initHandlerExceptionResolvers(ApplicationContext context) { this.handlerExceptionResolvers = null; if (this.detectAllHandlerExceptionResolvers) { // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. - Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( - context, HandlerExceptionResolver.class, true, false); + Map matchingBeans = BeanFactoryUtils + .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerExceptionResolvers = new ArrayList(matchingBeans.values()); // We keep HandlerExceptionResolvers in sorted order. @@ -552,8 +495,8 @@ public class DispatcherServlet extends FrameworkServlet { } else { try { - HandlerExceptionResolver her = context.getBean( - HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); + HandlerExceptionResolver her = + context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); this.handlerExceptionResolvers = Collections.singletonList(her); } catch (NoSuchBeanDefinitionException ex) { @@ -561,8 +504,8 @@ public class DispatcherServlet extends FrameworkServlet { } } - // Just for consistency, check for default HandlerExceptionResolvers... - // There aren't any in usual scenarios. + // Ensure we have at least some HandlerExceptionResolvers, by registering + // default HandlerExceptionResolvers if no other resolvers are found. if (this.handlerExceptionResolvers == null) { this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); if (logger.isDebugEnabled()) { @@ -572,13 +515,13 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Initialize the RequestToViewNameTranslator used by this servlet instance. If no - * implementation is configured then we default to DefaultRequestToViewNameTranslator. + * Initialize the RequestToViewNameTranslator used by this servlet instance. If no implementation is configured then we + * default to DefaultRequestToViewNameTranslator. */ private void initRequestToViewNameTranslator(ApplicationContext context) { try { - this.viewNameTranslator = context.getBean( - REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class); + this.viewNameTranslator = + context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class); if (logger.isDebugEnabled()) { logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]"); } @@ -588,24 +531,23 @@ public class DispatcherServlet extends FrameworkServlet { this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class); if (logger.isDebugEnabled()) { logger.debug("Unable to locate RequestToViewNameTranslator with name '" + - REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + - "': using default [" + this.viewNameTranslator + "]"); + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this.viewNameTranslator + + "]"); } } } /** - * Initialize the ViewResolvers used by this class. - *

If no ViewResolver beans are defined in the BeanFactory - * for this namespace, we default to InternalResourceViewResolver. + * Initialize the ViewResolvers used by this class.

If no ViewResolver beans are defined in the BeanFactory for this + * namespace, we default to InternalResourceViewResolver. */ private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) { // Find all ViewResolvers in the ApplicationContext, including ancestor contexts. - Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( - context, ViewResolver.class, true, false); + Map matchingBeans = + BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList(matchingBeans.values()); // We keep ViewResolvers in sorted order. @@ -633,9 +575,9 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Return this servlet's ThemeSource, if any; else return null. - *

Default is to return the WebApplicationContext as ThemeSource, - * provided that it implements the ThemeSource interface. + * Return this servlet's ThemeSource, if any; else return null.

Default is to return the + * WebApplicationContext as ThemeSource, provided that it implements the ThemeSource interface. + * * @return the ThemeSource, if any * @see #getWebApplicationContext() */ @@ -650,18 +592,18 @@ public class DispatcherServlet extends FrameworkServlet { /** * Obtain this servlet's MultipartResolver, if any. - * @return the MultipartResolver used by this servlet, or null - * if none (indicating that no multipart support is available) + * + * @return the MultipartResolver used by this servlet, or null if none (indicating that no multipart + * support is available) */ public final MultipartResolver getMultipartResolver() { return this.multipartResolver; } - /** - * Return the default strategy object for the given strategy interface. - *

The default implementation delegates to {@link #getDefaultStrategies}, - * expecting a single object in the list. + * Return the default strategy object for the given strategy interface.

The default implementation delegates to + * {@link #getDefaultStrategies}, expecting a single object in the list. + * * @param context the current WebApplicationContext * @param strategyInterface the strategy interface * @return the corresponding strategy object @@ -677,10 +619,10 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Create a List of default strategy objects for the given strategy interface. - *

The default implementation uses the "DispatcherServlet.properties" file - * (in the same package as the DispatcherServlet class) to determine the class names. - * It instantiates the strategy objects through the context's BeanFactory. + * Create a List of default strategy objects for the given strategy interface.

The default implementation uses the + * "DispatcherServlet.properties" file (in the same package as the DispatcherServlet class) to determine the class + * names. It instantiates the strategy objects through the context's BeanFactory. + * * @param context the current WebApplicationContext * @param strategyInterface the strategy interface * @return the List of corresponding strategy objects @@ -717,13 +659,12 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Create a default strategy. - *

The default implementation uses - * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}. + * Create a default strategy.

The default implementation uses {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}. + * * @param context the current WebApplicationContext * @param clazz the strategy implementation class to instantiate - * @throws BeansException if initialization failed * @return the fully configured strategy instance + * @throws BeansException if initialization failed * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory() * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean */ @@ -731,17 +672,16 @@ public class DispatcherServlet extends FrameworkServlet { return context.getAutowireCapableBeanFactory().createBean(clazz); } - /** - * Exposes the DispatcherServlet-specific request attributes and - * delegates to {@link #doDispatch} for the actual dispatching. + * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} for the actual + * dispatching. */ @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String requestUri = new UrlPathHelper().getRequestUri(request); - logger.debug("DispatcherServlet with name '" + getServletName() + - "' processing " + request.getMethod() + " request for [" + requestUri + "]"); + logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() + + " request for [" + requestUri + "]"); } // Keep a snapshot of the request attributes in case of an include, @@ -777,12 +717,11 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Process the actual dispatching to the handler. - *

The handler will be obtained by applying the servlet's HandlerMappings in order. - * The HandlerAdapter will be obtained by querying the servlet's installed - * HandlerAdapters to find the first that supports the handler class. - *

All HTTP methods are handled by this method. It's up to HandlerAdapters or - * handlers themselves to decide which methods are acceptable. + * Process the actual dispatching to the handler.

The handler will be obtained by applying the servlet's + * HandlerMappings in order. The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters to + * find the first that supports the handler class.

All HTTP methods are handled by this method. It's up to + * HandlerAdapters or handlers themselves to decide which methods are acceptable. + * * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure @@ -855,8 +794,8 @@ public class DispatcherServlet extends FrameworkServlet { } else { if (logger.isDebugEnabled()) { - logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + - getServletName() + "': assuming HandlerAdapter completed request handling"); + logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + + "': assuming HandlerAdapter completed request handling"); } } @@ -885,15 +824,16 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Override HttpServlet's getLastModified method to evaluate - * the Last-Modified value of the mapped handler. + * Override HttpServlet's getLastModified method to evaluate the Last-Modified value of the mapped + * handler. */ @Override protected long getLastModified(HttpServletRequest request) { if (logger.isDebugEnabled()) { String requestUri = new UrlPathHelper().getRequestUri(request); - logger.debug("DispatcherServlet with name '" + getServletName() + - "' determining Last-Modified value for [" + requestUri + "]"); + logger.debug( + "DispatcherServlet with name '" + getServletName() + "' determining Last-Modified value for [" + + requestUri + "]"); } try { HandlerExecutionChain mappedHandler = getHandler(request, true); @@ -918,12 +858,11 @@ public class DispatcherServlet extends FrameworkServlet { } } - /** - * Build a LocaleContext for the given request, exposing the request's - * primary locale as current locale. - *

The default implementation uses the dispatcher's LocaleResolver - * to obtain the current locale, which might change during a request. + * Build a LocaleContext for the given request, exposing the request's primary locale as current locale.

The default + * implementation uses the dispatcher's LocaleResolver to obtain the current locale, which might change during a + * request. + * * @param request current HTTP request * @return the corresponding LocaleContext */ @@ -933,6 +872,7 @@ public class DispatcherServlet extends FrameworkServlet { public Locale getLocale() { return localeResolver.resolveLocale(request); } + @Override public String toString() { return getLocale().toString(); @@ -941,8 +881,9 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Convert the request into a multipart request, and make multipart resolver available. - * If no multipart resolver is set, simply use the existing request. + * Convert the request into a multipart request, and make multipart resolver available. If no multipart resolver is + * set, simply use the existing request. + * * @param request current HTTP request * @return the processed request (multipart wrapper if necessary) * @see MultipartResolver#resolveMultipart @@ -963,6 +904,7 @@ public class DispatcherServlet extends FrameworkServlet { /** * Clean up any resources used by the given multipart request (if any). + * * @param request current HTTP request * @see MultipartResolver#cleanupMultipart */ @@ -973,15 +915,14 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Return the HandlerExecutionChain for this request. - * Try all handler mappings in order. + * Return the HandlerExecutionChain for this request. Try all handler mappings in order. + * * @param request current HTTP request * @param cache whether to cache the HandlerExecutionChain in a request attribute * @return the HandlerExceutionChain, or null if no handler could be found */ protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception { - HandlerExecutionChain handler = - (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); + HandlerExecutionChain handler = (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); if (handler != null) { if (!cache) { request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); @@ -1007,6 +948,7 @@ public class DispatcherServlet extends FrameworkServlet { /** * No handler found -> set appropriate HTTP response status. + * * @param request current HTTP request * @param response current HTTP response * @throws Exception if preparing the response failed @@ -1014,17 +956,17 @@ public class DispatcherServlet extends FrameworkServlet { protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { String requestUri = new UrlPathHelper().getRequestUri(request); - pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + - requestUri + "] in DispatcherServlet with name '" + getServletName() + "'"); + pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri + + "] in DispatcherServlet with name '" + getServletName() + "'"); } response.sendError(HttpServletResponse.SC_NOT_FOUND); } /** * Return the HandlerAdapter for this handler object. + * * @param handler the handler object to find an adapter for - * @throws ServletException if no HandlerAdapter can be found for the handler. - * This is a fatal error. + * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error. */ protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { @@ -1041,17 +983,19 @@ public class DispatcherServlet extends FrameworkServlet { /** * Determine an error ModelAndView via the registered HandlerExceptionResolvers. + * * @param request current HTTP request * @param response current HTTP response - * @param handler the executed handler, or null if none chosen at the time of - * the exception (for example, if multipart resolution failed) + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) * @param ex the exception that got thrown during handler execution * @return a corresponding ModelAndView to forward to * @throws Exception if no error ModelAndView found */ - protected ModelAndView processHandlerException( - HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) - throws Exception { + protected ModelAndView processHandlerException(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex) throws Exception { // Check registerer HandlerExceptionResolvers... ModelAndView exMv = null; @@ -1066,35 +1010,26 @@ public class DispatcherServlet extends FrameworkServlet { return null; } if (logger.isDebugEnabled()) { - logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); + logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, + ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } - // Send default responses for well-known exceptions, if possible. - if (ex instanceof HttpRequestMethodNotSupportedException && !response.isCommitted()) { - String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods(); - if (supportedMethods != null) { - response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", ")); - } - response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage()); - return null; - } - throw ex; } /** - * Render the given ModelAndView. This is the last stage in handling a request. - * It may involve resolving the view by name. + * Render the given ModelAndView. This is the last stage in handling a request. It may involve resolving the view by + * name. + * * @param mv the ModelAndView to render * @param request current HTTP servlet request * @param response current HTTP servlet response * @throws Exception if there's a problem rendering the view */ - protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) - throws Exception { + protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = this.localeResolver.resolveLocale(request); @@ -1106,8 +1041,9 @@ public class DispatcherServlet extends FrameworkServlet { // We need to resolve the view name. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { - throw new ServletException("Could not resolve view with name '" + mv.getViewName() + - "' in servlet with name '" + getServletName() + "'"); + throw new ServletException( + "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + + getServletName() + "'"); } } else { @@ -1128,6 +1064,7 @@ public class DispatcherServlet extends FrameworkServlet { /** * Translate the supplied request into a default view name. + * * @param request current HTTP servlet request * @return the view name (or null if no default found) * @throws Exception if view name translation failed @@ -1137,22 +1074,22 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Resolve the given view name into a View object (to be rendered). - *

Default implementations asks all ViewResolvers of this dispatcher. - * Can be overridden for custom resolution strategies, potentially based - * on specific model attributes or request parameters. + * Resolve the given view name into a View object (to be rendered).

Default implementations asks all ViewResolvers + * of this dispatcher. Can be overridden for custom resolution strategies, potentially based on specific model + * attributes or request parameters. + * * @param viewName the name of the view to resolve * @param model the model to be passed to the view * @param locale the current locale * @param request current HTTP servlet request * @return the View object, or null if none found - * @throws Exception if the view cannot be resolved - * (typically in case of problems creating an actual View object) + * @throws Exception if the view cannot be resolved (typically in case of problems creating an actual View object) * @see ViewResolver#resolveViewName */ - protected View resolveViewName( - String viewName, Map model, Locale locale, HttpServletRequest request) - throws Exception { + protected View resolveViewName(String viewName, + Map model, + Locale locale, + HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); @@ -1164,18 +1101,19 @@ public class DispatcherServlet extends FrameworkServlet { } /** - * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. - * Will just invoke afterCompletion for all interceptors whose preHandle - * invocation has successfully completed and returned true. + * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. Will just invoke afterCompletion for all + * interceptors whose preHandle invocation has successfully completed and returned true. + * * @param mappedHandler the mapped HandlerExecutionChain * @param interceptorIndex index of last interceptor that successfully completed * @param ex Exception thrown on handler execution, or null if none * @see HandlerInterceptor#afterCompletion */ - private void triggerAfterCompletion( - HandlerExecutionChain mappedHandler, int interceptorIndex, - HttpServletRequest request, HttpServletResponse response, Exception ex) - throws Exception { + private void triggerAfterCompletion(HandlerExecutionChain mappedHandler, + int interceptorIndex, + HttpServletRequest request, + HttpServletResponse response, + Exception ex) throws Exception { // Apply afterCompletion methods of registered interceptors. if (mappedHandler != null) { @@ -1196,9 +1134,9 @@ public class DispatcherServlet extends FrameworkServlet { /** * Restore the request attributes after an include. + * * @param request current HTTP request - * @param attributesSnapshot the snapshot of the request attributes - * before the include + * @param attributesSnapshot the snapshot of the request attributes before the include */ private void restoreAttributesAfterInclude(HttpServletRequest request, Map attributesSnapshot) { logger.debug("Restoring snapshot of request attributes after include"); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.properties b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.properties index 95404a75500..c550bd71275 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.properties +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.properties @@ -13,6 +13,8 @@ org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.m org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter +org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.handler.DefaultHandlerExceptionResolver + org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java new file mode 100644 index 00000000000..a32f626da16 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java @@ -0,0 +1,190 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.handler; + +import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.core.Ordered; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.ModelAndView; + +/** + * Abstract base class for {@link HandlerExceptionResolver} implementations.

Provides a set of mapped handlers that + * the resolver should map to, and the {@link Ordered} implementation. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered { + + /** Logger available to subclasses */ + protected final Log logger = LogFactory.getLog(getClass()); + + private int order = Ordered.LOWEST_PRECEDENCE; + + private Set mappedHandlers; + + private Class[] mappedHandlerClasses; + + private Log warnLogger; + + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return this.order; + } + + /** + * Specify the set of handlers that this exception resolver should apply to. The exception mappings and the default + * error view will only apply to the specified handlers.

If no handlers and handler classes are set, the exception + * mappings and the default error view will apply to all handlers. This means that a specified default error view will + * be used as fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be ignored in this + * case. + */ + public void setMappedHandlers(Set mappedHandlers) { + this.mappedHandlers = mappedHandlers; + } + + /** + * Specify the set of classes that this exception resolver should apply to. The exception mappings and the default + * error view will only apply to handlers of the specified type; the specified types may be interfaces and superclasses + * of handlers as well.

If no handlers and handler classes are set, the exception mappings and the default error + * view will apply to all handlers. This means that a specified default error view will be used as fallback for all + * exceptions; any further HandlerExceptionResolvers in the chain will be ignored in this case. + */ + public void setMappedHandlerClasses(Class[] mappedHandlerClasses) { + this.mappedHandlerClasses = mappedHandlerClasses; + } + + /** + * Set the log category for warn logging. The name will be passed to the underlying logger implementation through + * Commons Logging, getting interpreted as log category according to the logger's configuration.

Default is no warn + * logging. Specify this setting to activate warn logging into a specific category. Alternatively, override the {@link + * #logException} method for custom logging. + * + * @see org.apache.commons.logging.LogFactory#getLog(String) + * @see org.apache.log4j.Logger#getLogger(String) + * @see java.util.logging.Logger#getLogger(String) + */ + public void setWarnLogCategory(String loggerName) { + this.warnLogger = LogFactory.getLog(loggerName); + } + + /** + * Checks whether this resolver is supposed to apply (i.e. the handler matches in case of "mappedHandlers" having been + * specified), then delegates to the {@link #doResolveException} template method. + */ + public ModelAndView resolveException(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex) { + + if (shouldApplyTo(request, handler)) { + // Log exception, both at debug log level and at warn level, if desired. + if (logger.isDebugEnabled()) { + logger.debug("Resolving exception from handler [" + handler + "]: " + ex); + } + logException(ex, request); + return doResolveException(request, response, handler, ex); + } + else { + return null; + } + } + + /** + * Check whether this resolver is supposed to apply to the given handler.

The default implementation checks against + * the specified mapped handlers and handler classes, if any. + * + * @param request current HTTP request + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return whether this resolved should proceed with resolving the exception for the given request and handler + * @see #setMappedHandlers + * @see #setMappedHandlerClasses + */ + protected boolean shouldApplyTo(HttpServletRequest request, Object handler) { + if (handler != null) { + if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { + return true; + } + if (this.mappedHandlerClasses != null) { + for (Class handlerClass : this.mappedHandlerClasses) { + if (handlerClass.isInstance(handler)) { + return true; + } + } + } + } + // Else only apply if there are no explicit handler mappings. + return (this.mappedHandlers == null && this.mappedHandlerClasses == null); + } + + /** + * Log the given exception at warn level, provided that warn logging has been activated through the {@link + * #setWarnLogCategory "warnLogCategory"} property.

Calls {@link #buildLogMessage} in order to determine the + * concrete message to log. Always passes the full exception to the logger. + * + * @param ex the exception that got thrown during handler execution + * @param request current HTTP request (useful for obtaining metadata) + * @see #setWarnLogCategory + * @see #buildLogMessage + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + protected void logException(Exception ex, HttpServletRequest request) { + if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) { + this.warnLogger.warn(buildLogMessage(ex, request), ex); + } + } + + /** + * Build a log message for the given exception, occured during processing the given request. + * + * @param ex the exception that got thrown during handler execution + * @param request current HTTP request (useful for obtaining metadata) + * @return the log message to use + */ + protected String buildLogMessage(Exception ex, HttpServletRequest request) { + return "Handler execution resulted in exception"; + } + + /** + * Actually resolve the given exception that got thrown during on handler execution, returning a ModelAndView that + * represents a specific error page if appropriate.

May be overridden in subclasses, in order to apply specific + * exception checks. Note that this template method will be invoked after checking whether this resolved applies + * ("mappedHandlers" etc), so an implementation may simply proceed with its actual exception handling. + * + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @param ex the exception that got thrown during handler execution + * @return a corresponding ModelAndView to forward to, or null for default processing + */ + protected abstract ModelAndView doResolveException(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex); + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/DefaultHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/DefaultHandlerExceptionResolver.java new file mode 100644 index 00000000000..03f837719aa --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/DefaultHandlerExceptionResolver.java @@ -0,0 +1,280 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.handler; + +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.TypeMismatchException; +import org.springframework.core.Ordered; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.util.StringUtils; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; + +/** + * Default implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver + * HandlerExceptionResolver} interface that resolves standard Spring exceptions.

Default implementations typically + * set the response status. + * + * @author Arjen Poutsma + * @see #handleNoSuchRequestHandlingMethod + * @see #handleHttpRequestMethodNotSupported + * @see #handleHttpMediaTypeNotSupported + * @see #handleMissingServletRequestParameter + * @see #handleTypeMismatch + * @see #handleHttpMessageNotReadable + * @see #handleHttpMessageNotWritable + * @since 3.0 + */ +public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver { + + /** + * Log category to use when no mapped handler is found for a request. + * + * @see #pageNotFoundLogger + */ + public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; + + /** + * Additional logger to use when no mapped handler is found for a request. + * + * @see #PAGE_NOT_FOUND_LOG_CATEGORY + */ + protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); + + /** Sets the {@linkplain #setOrder(int) order} to {@link #LOWEST_PRECEDENCE}. */ + public DefaultHandlerExceptionResolver() { + setOrder(Ordered.LOWEST_PRECEDENCE); + } + + @Override + protected ModelAndView doResolveException(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex) { + try { + if (ex instanceof NoSuchRequestHandlingMethodException) { + return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response, + handler); + } + else if (ex instanceof HttpRequestMethodNotSupportedException) { + return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request, + response, handler); + } + else if (ex instanceof HttpMediaTypeNotSupportedException) { + return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response, + handler); + } + else if (ex instanceof MissingServletRequestParameterException) { + return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request, + response, handler); + } + else if (ex instanceof TypeMismatchException) { + return handleTypeMismatch((TypeMismatchException) ex, request, response, handler); + } + else if (ex instanceof HttpMessageNotReadableException) { + return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler); + } + else if (ex instanceof HttpMessageNotWritableException) { + return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler); + } + } + catch (Exception handlerException) { + logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException); + } + return null; + } + + /** + * Handle the case where no request handler method was found.

The default implementation logs a warning, sends an + * HTTP 404 error, and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the + * NoSuchRequestHandlingMethodException could be rethrown as-is. + * + * @param ex the NoSuchRequestHandlingMethodException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + pageNotFoundLogger.warn(ex.getMessage()); + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return new ModelAndView(); + } + + /** + * Handle the case where no request handler method was found for the particular HTTP request method.

The default + * implementation logs a warning, sends an HTTP 405 error, sets the "Allow" header, and returns an empty {@code + * ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpRequestMethodNotSupportedException could + * be rethrown as-is. + * + * @param ex the HttpRequestMethodNotSupportedException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + pageNotFoundLogger.warn(ex.getMessage()); + String[] supportedMethods = ex.getSupportedMethods(); + if (supportedMethods != null) { + response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", ")); + } + response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage()); + return new ModelAndView(); + } + + /** + * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters} + * were found for the PUT or POSTed content.

The default implementation sends an HTTP 415 error, sets the "Allow" + * header, and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the + * HttpMediaTypeNotSupportedException could be rethrown as-is. + * + * @param ex the HttpMediaTypeNotSupportedException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + List mediaTypes = ex.getSupportedMediaTypes(); + if (mediaTypes != null) { + response.setHeader("Accept", MediaType.toString(mediaTypes)); + } + return new ModelAndView(); + } + + /** + * Handle the case when a required parameter is missing.

The default implementation sends an HTTP 400 error, and + * returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the + * MissingServletRequestParameterException could be rethrown as-is. + * + * @param ex the MissingServletRequestParameterException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return new ModelAndView(); + } + + /** + * Handle the case when a {@link org.springframework.web.bind.WebDataBinder} conversion error occurs.

The default + * implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}. Alternatively, a fallback view + * could be chosen, or the TypeMismatchException could be rethrown as-is. + * + * @param ex the TypeMismatchException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleTypeMismatch(TypeMismatchException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return new ModelAndView(); + } + + /** + * Handle the case where a {@linkplain org.springframework.http.converter.HttpMessageConverter message converter} can + * not read from a HTTP request.

The default implementation sends an HTTP 400 error, and returns an empty {@code + * ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException could be + * rethrown as-is. + * + * @param ex the HttpMessageNotReadableException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return new ModelAndView(); + } + + /** + * Handle the case where a {@linkplain org.springframework.http.converter.HttpMessageConverter message converter} can + * not write to a HTTP request.

The default implementation sends an HTTP 500 error, and returns an empty {@code + * ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException could be + * rethrown as-is. + * + * @param ex the HttpMessageNotWritableException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableException ex, + HttpServletRequest request, + HttpServletResponse response, + Object handler) throws Exception { + + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return new ModelAndView(); + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java index 8a5fade1ea0..f2f7affef7c 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java @@ -18,51 +18,29 @@ package org.springframework.web.servlet.handler; import java.util.Enumeration; import java.util.Properties; -import java.util.Set; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.core.Ordered; -import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.WebUtils; /** - * {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation - * that allows for mapping exception class names to view names, either for a - * set of given handlers or for all handlers in the DispatcherServlet. + * {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation that allows for mapping exception + * class names to view names, either for a set of given handlers or for all handlers in the DispatcherServlet. * - *

Error views are analogous to error page JSPs, but can be used with any - * kind of exception including any checked one, with fine-granular mappings for - * specific handlers. + *

Error views are analogous to error page JSPs, but can be used with any kind of exception including any checked + * one, with fine-granular mappings for specific handlers. * * @author Juergen Hoeller - * @since 22.11.2003 + * @author Arjen Poutsma * @see org.springframework.web.servlet.DispatcherServlet + * @since 22.11.2003 */ -public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered { +public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver { - /** - * The default name of the exception attribute: "exception". - */ + /** The default name of the exception attribute: "exception". */ public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception"; - - /** Logger available to subclasses */ - protected final Log logger = LogFactory.getLog(getClass()); - - private int order = Integer.MAX_VALUE; // default: same as non-Ordered - - private Set mappedHandlers; - - private Class[] mappedHandlerClasses; - - private Log warnLogger; - private Properties exceptionMappings; private String defaultErrorView; @@ -71,74 +49,18 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE; - - public void setOrder(int order) { - this.order = order; - } - - public int getOrder() { - return this.order; - } - /** - * Specify the set of handlers that this exception resolver should apply to. - * The exception mappings and the default error view will only apply - * to the specified handlers. - *

If no handlers and handler classes are set, the exception mappings - * and the default error view will apply to all handlers. This means that - * a specified default error view will be used as fallback for all exceptions; - * any further HandlerExceptionResolvers in the chain will be ignored in - * this case. - */ - public void setMappedHandlers(Set mappedHandlers) { - this.mappedHandlers = mappedHandlers; - } - - /** - * Specify the set of classes that this exception resolver should apply to. - * The exception mappings and the default error view will only apply - * to handlers of the specified type; the specified types may be interfaces - * and superclasses of handlers as well. - *

If no handlers and handler classes are set, the exception mappings - * and the default error view will apply to all handlers. This means that - * a specified default error view will be used as fallback for all exceptions; - * any further HandlerExceptionResolvers in the chain will be ignored in - * this case. - */ - public void setMappedHandlerClasses(Class[] mappedHandlerClasses) { - this.mappedHandlerClasses = mappedHandlerClasses; - } - - /** - * Set the log category for warn logging. The name will be passed to the - * underlying logger implementation through Commons Logging, getting - * interpreted as log category according to the logger's configuration. - *

Default is no warn logging. Specify this setting to activate - * warn logging into a specific category. Alternatively, override - * the {@link #logException} method for custom logging. - * @see org.apache.commons.logging.LogFactory#getLog(String) - * @see org.apache.log4j.Logger#getLogger(String) - * @see java.util.logging.Logger#getLogger(String) - */ - public void setWarnLogCategory(String loggerName) { - this.warnLogger = LogFactory.getLog(loggerName); - } - - /** - * Set the mappings between exception class names and error view names. - * The exception class name can be a substring, with no wildcard support - * at present. A value of "ServletException" would match - * javax.servlet.ServletException and subclasses, for example. - *

NB: Consider carefully how specific the pattern is, and whether - * to include package information (which isn't mandatory). For example, - * "Exception" will match nearly anything, and will probably hide other rules. - * "java.lang.Exception" would be correct if "Exception" was meant to define - * a rule for all checked exceptions. With more unusual exception names such - * as "BaseBusinessException" there's no need to use a FQN. - *

Follows the same matching algorithm as RuleBasedTransactionAttribute - * and RollbackRuleAttribute. - * @param mappings exception patterns (can also be fully qualified class names) - * as keys, and error view names as values + * Set the mappings between exception class names and error view names. The exception class name can be a substring, + * with no wildcard support at present. A value of "ServletException" would match + * javax.servlet.ServletException and subclasses, for example.

NB: Consider carefully how + * specific the pattern is, and whether to include package information (which isn't mandatory). For example, + * "Exception" will match nearly anything, and will probably hide other rules. "java.lang.Exception" would be correct + * if "Exception" was meant to define a rule for all checked exceptions. With more unusual exception names such as + * "BaseBusinessException" there's no need to use a FQN.

Follows the same matching algorithm as + * RuleBasedTransactionAttribute and RollbackRuleAttribute. + * + * @param mappings exception patterns (can also be fully qualified class names) as keys, and error view names as + * values * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute * @see org.springframework.transaction.interceptor.RollbackRuleAttribute */ @@ -147,24 +69,20 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Set the name of the default error view. - * This view will be returned if no specific mapping was found. - *

Default is none. + * Set the name of the default error view. This view will be returned if no specific mapping was found.

Default is + * none. */ public void setDefaultErrorView(String defaultErrorView) { this.defaultErrorView = defaultErrorView; } /** - * Set the default HTTP status code that this exception resolver will apply - * if it resolves an error view. - *

Note that this error code will only get applied in case of a top-level - * request. It will not be set for an include request, since the HTTP status - * cannot be modified from within an include. - *

If not specified, no status code will be applied, either leaving this to - * the controller or view, or keeping the servlet engine's default of 200 (OK). - * @param defaultStatusCode HTTP status code value, for example - * 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND) + * Set the default HTTP status code that this exception resolver will apply if it resolves an error view.

Note that + * this error code will only get applied in case of a top-level request. It will not be set for an include request, + * since the HTTP status cannot be modified from within an include.

If not specified, no status code will be + * applied, either leaving this to the controller or view, or keeping the servlet engine's default of 200 (OK). + * + * @param defaultStatusCode HTTP status code value, for example 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND) * @see javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR * @see javax.servlet.http.HttpServletResponse#SC_NOT_FOUND */ @@ -173,84 +91,33 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Set the name of the model attribute as which the exception should - * be exposed. Default is "exception". - *

This can be either set to a different attribute name or to - * null for not exposing an exception attribute at all. + * Set the name of the model attribute as which the exception should be exposed. Default is "exception".

This can be + * either set to a different attribute name or to null for not exposing an exception attribute at all. + * * @see #DEFAULT_EXCEPTION_ATTRIBUTE */ public void setExceptionAttribute(String exceptionAttribute) { this.exceptionAttribute = exceptionAttribute; } - /** - * Checks whether this resolver is supposed to apply (i.e. the handler - * matches in case of "mappedHandlers" having been specified), then - * delegates to the {@link #doResolveException} template method. - */ - public ModelAndView resolveException( - HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { - - if (shouldApplyTo(request, handler)) { - return doResolveException(request, response, handler, ex); - } - else { - return null; - } - } - - /** - * Check whether this resolver is supposed to apply to the given handler. - *

The default implementation checks against the specified mapped handlers - * and handler classes, if any. - * @param request current HTTP request - * @param handler the executed handler, or null if none chosen at the - * time of the exception (for example, if multipart resolution failed) - * @return whether this resolved should proceed with resolving the exception - * for the given request and handler - * @see #setMappedHandlers - * @see #setMappedHandlerClasses - */ - protected boolean shouldApplyTo(HttpServletRequest request, Object handler) { - if (handler != null) { - if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { - return true; - } - if (this.mappedHandlerClasses != null) { - for (Class handlerClass : this.mappedHandlerClasses) { - if (handlerClass.isInstance(handler)) { - return true; - } - } - } - } - // Else only apply if there are no explicit handler mappings. - return (this.mappedHandlers == null && this.mappedHandlerClasses == null); - } - - /** - * Actually resolve the given exception that got thrown during on handler execution, - * returning a ModelAndView that represents a specific error page if appropriate. - *

May be overridden in subclasses, in order to apply specific exception checks. - * Note that this template method will be invoked after checking whether - * this resolved applies ("mappedHandlers" etc), so an implementation may simply - * proceed with its actual exception handling. + * Actually resolve the given exception that got thrown during on handler execution, returning a ModelAndView that + * represents a specific error page if appropriate.

May be overridden in subclasses, in order to apply specific + * exception checks. Note that this template method will be invoked after checking whether this resolved applies + * ("mappedHandlers" etc), so an implementation may simply proceed with its actual exception handling. + * * @param request current HTTP request * @param response current HTTP response - * @param handler the executed handler, or null if none chosen at the - * time of the exception (for example, if multipart resolution failed) + * @param handler the executed handler, or null if none chosen at the time of the exception (for example, + * if multipart resolution failed) * @param ex the exception that got thrown during handler execution * @return a corresponding ModelAndView to forward to, or null for default processing */ - protected ModelAndView doResolveException( - HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { - - // Log exception, both at debug log level and at warn level, if desired. - if (logger.isDebugEnabled()) { - logger.debug("Resolving exception from handler [" + handler + "]: " + ex); - } - logException(ex, request); + @Override + protected ModelAndView doResolveException(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex) { // Expose ModelAndView for chosen error view. String viewName = determineViewName(ex, request); @@ -268,40 +135,10 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } } - /** - * Log the given exception at warn level, provided that warn logging has been - * activated through the {@link #setWarnLogCategory "warnLogCategory"} property. - *

Calls {@link #buildLogMessage} in order to determine the concrete message - * to log. Always passes the full exception to the logger. - * @param ex the exception that got thrown during handler execution - * @param request current HTTP request (useful for obtaining metadata) - * @see #setWarnLogCategory - * @see #buildLogMessage - * @see org.apache.commons.logging.Log#warn(Object, Throwable) - */ - protected void logException(Exception ex, HttpServletRequest request) { - if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) { - this.warnLogger.warn(buildLogMessage(ex, request), ex); - } - } - - /** - * Build a log message for the given exception, occured during processing - * the given request. - * @param ex the exception that got thrown during handler execution - * @param request current HTTP request (useful for obtaining metadata) - * @return the log message to use - */ - protected String buildLogMessage(Exception ex, HttpServletRequest request) { - return "Handler execution resulted in exception"; - } - - - /** - * Determine the view name for the given exception, searching the - * {@link #setExceptionMappings "exceptionMappings"}, using the - * {@link #setDefaultErrorView "defaultErrorView"} as fallback. + * Determine the view name for the given exception, searching the {@link #setExceptionMappings "exceptionMappings"}, + * using the {@link #setDefaultErrorView "defaultErrorView"} as fallback. + * * @param ex the exception that got thrown during handler execution * @param request current HTTP request (useful for obtaining metadata) * @return the resolved view name, or null if none found @@ -315,8 +152,8 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, // Return default error view else, if defined. if (viewName == null && this.defaultErrorView != null) { if (logger.isDebugEnabled()) { - logger.debug("Resolving to default view '" + this.defaultErrorView + - "' for exception of type [" + ex.getClass().getName() + "]"); + logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" + + ex.getClass().getName() + "]"); } viewName = this.defaultErrorView; } @@ -325,6 +162,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, /** * Find a matching view name in the given exception mappings. + * * @param exceptionMappings mappings between exception class names and error view names * @param ex the exception that got thrown during handler execution * @return the view name, or null if none found @@ -351,11 +189,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Return the depth to the superclass matching. - *

0 means ex matches exactly. Returns -1 if there's no match. - * Otherwise, returns depth. Lowest depth wins. - *

Follows the same algorithm as - * {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}. + * Return the depth to the superclass matching.

0 means ex matches exactly. Returns -1 if there's no match. + * Otherwise, returns depth. Lowest depth wins.

Follows the same algorithm as {@link + * org.springframework.transaction.interceptor.RollbackRuleAttribute}. */ protected int getDepth(String exceptionMapping, Exception ex) { return getDepth(exceptionMapping, ex.getClass(), 0); @@ -373,17 +209,15 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1); } - /** - * Determine the HTTP status code to apply for the given error view. - *

The default implementation always returns the specified - * {@link #setDefaultStatusCode "defaultStatusCode"}, as a common - * status code for all error views. Override this in a custom subclass - * to determine a specific status code for the given view. + * Determine the HTTP status code to apply for the given error view.

The default implementation always returns the + * specified {@link #setDefaultStatusCode "defaultStatusCode"}, as a common status code for all error views. Override + * this in a custom subclass to determine a specific status code for the given view. + * * @param request current HTTP request * @param viewName the name of the error view - * @return the HTTP status code to use, or null for the - * servlet container's default (200 in case of a standard error view) + * @return the HTTP status code to use, or null for the servlet container's default (200 in case of a + * standard error view) * @see #setDefaultStatusCode * @see #applyStatusCodeIfPossible */ @@ -392,8 +226,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Apply the specified HTTP status code to the given response, if possible - * (that is, if not executing within an include request). + * Apply the specified HTTP status code to the given response, if possible (that is, if not executing within an include + * request). + * * @param request current HTTP request * @param response current HTTP response * @param statusCode the status code to apply @@ -412,8 +247,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Return a ModelAndView for the given request, view name and exception. - *

The default implementation delegates to {@link #getModelAndView(String, Exception)}. + * Return a ModelAndView for the given request, view name and exception.

The default implementation delegates to + * {@link #getModelAndView(String, Exception)}. + * * @param viewName the name of the error view * @param ex the exception that got thrown during handler execution * @param request current HTTP request (useful for obtaining metadata) @@ -424,9 +260,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } /** - * Return a ModelAndView for the given view name and exception. - *

The default implementation adds the specified exception attribute. - * Can be overridden in subclasses. + * Return a ModelAndView for the given view name and exception.

The default implementation adds the specified + * exception attribute. Can be overridden in subclasses. + * * @param viewName the name of the error view * @param ex the exception that got thrown during handler execution * @return the ModelAndView instance diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 3b419798e3e..4cc68680ce3 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -52,7 +52,6 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; -import org.springframework.http.MediaType; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; @@ -68,7 +67,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.validation.support.BindingAwareModelMap; -import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -101,38 +99,36 @@ import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.WebUtils; /** - * Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} - * interface that maps handler methods based on HTTP paths, HTTP methods and - * request parameters expressed through the {@link RequestMapping} annotation. + * Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} interface that maps handler methods + * based on HTTP paths, HTTP methods and request parameters expressed through the {@link RequestMapping} annotation. * - *

Supports request parameter binding through the {@link RequestParam} annotation. - * Also supports the {@link ModelAttribute} annotation for exposing model attribute - * values to the view, as well as {@link InitBinder} for binder initialization methods - * and {@link SessionAttributes} for automatic session management of specific attributes. + *

Supports request parameter binding through the {@link RequestParam} annotation. Also supports the {@link + * ModelAttribute} annotation for exposing model attribute values to the view, as well as {@link InitBinder} for binder + * initialization methods and {@link SessionAttributes} for automatic session management of specific attributes. * - *

This adapter can be customized through various bean properties. - * A common use case is to apply shared binder initialization logic through - * a custom {@link #setWebBindingInitializer WebBindingInitializer}. + *

This adapter can be customized through various bean properties. A common use case is to apply shared binder + * initialization logic through a custom {@link #setWebBindingInitializer WebBindingInitializer}. * * @author Juergen Hoeller * @author Arjen Poutsma - * @since 2.5 * @see #setPathMatcher * @see #setMethodNameResolver * @see #setWebBindingInitializer * @see #setSessionAttributeStore + * @since 2.5 */ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter { /** * Log category to use when no mapped handler is found for a request. + * * @see #pageNotFoundLogger */ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; - /** * Additional logger to use when no mapped handler is found for a request. + * * @see #PAGE_NOT_FOUND_LOG_CATEGORY */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); @@ -167,12 +163,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen super(false); } - /** - * Set if URL lookup should always use the full path within the current servlet - * context. Else, the path within the current servlet mapping is used if applicable - * (that is, in the case of a ".../*" servlet mapping in web.xml). + * Set if URL lookup should always use the full path within the current servlet context. Else, the path within the + * current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml). *

Default is "false". + * * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath */ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { @@ -180,10 +175,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set if context path and request URI should be URL-decoded. Both are returned - * undecoded by the Servlet API, in contrast to the servlet path. - *

Uses either the request encoding or the default encoding according - * to the Servlet spec (ISO-8859-1). + * Set if context path and request URI should be URL-decoded. Both are returned undecoded by the Servlet API, in + * contrast to the servlet path.

Uses either the request encoding or the default encoding according to the Servlet + * spec (ISO-8859-1). + * * @see org.springframework.web.util.UrlPathHelper#setUrlDecode */ public void setUrlDecode(boolean urlDecode) { @@ -191,10 +186,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the UrlPathHelper to use for resolution of lookup paths. - *

Use this to override the default UrlPathHelper with a custom subclass, - * or to share common UrlPathHelper settings across multiple HandlerMappings - * and HandlerAdapters. + * Set the UrlPathHelper to use for resolution of lookup paths.

Use this to override the default UrlPathHelper with + * a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters. */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); @@ -202,8 +195,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the PathMatcher implementation to use for matching URL paths - * against registered URL patterns. Default is AntPathMatcher. + * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is + * AntPathMatcher. + * * @see org.springframework.util.AntPathMatcher */ public void setPathMatcher(PathMatcher pathMatcher) { @@ -212,9 +206,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the MethodNameResolver to use for resolving default handler methods - * (carrying an empty @RequestMapping annotation). - *

Will only kick in when the handler method cannot be resolved uniquely + * Set the MethodNameResolver to use for resolving default handler methods (carrying an empty + * @RequestMapping annotation).

Will only kick in when the handler method cannot be resolved uniquely * through the annotation metadata already. */ public void setMethodNameResolver(MethodNameResolver methodNameResolver) { @@ -222,18 +215,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Specify a WebBindingInitializer which will apply pre-configured - * configuration to every DataBinder that this controller uses. + * Specify a WebBindingInitializer which will apply pre-configured configuration to every DataBinder that this + * controller uses. */ public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { this.webBindingInitializer = webBindingInitializer; } /** - * Specify the strategy to store session attributes with. - *

Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, - * storing session attributes in the HttpSession, using the same - * attribute name as in the model. + * Specify the strategy to store session attributes with.

Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, + * storing session attributes in the HttpSession, using the same attribute name as in the model. */ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) { Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null"); @@ -241,11 +232,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Cache content produced by @SessionAttributes annotated handlers - * for the given number of seconds. Default is 0, preventing caching completely. - *

In contrast to the "cacheSeconds" property which will apply to all general - * handlers (but not to @SessionAttributes annotated handlers), this - * setting will apply to @SessionAttributes annotated handlers only. + * Cache content produced by @SessionAttributes annotated handlers for the given number of seconds. + * Default is 0, preventing caching completely.

In contrast to the "cacheSeconds" property which will apply to all + * general handlers (but not to @SessionAttributes annotated handlers), this setting will apply to + * @SessionAttributes annotated handlers only. + * * @see #setCacheSeconds * @see org.springframework.web.bind.annotation.SessionAttributes */ @@ -254,20 +245,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set if controller execution should be synchronized on the session, - * to serialize parallel invocations from the same client. - *

More specifically, the execution of each handler method will get - * synchronized if this flag is "true". The best available session mutex - * will be used for the synchronization; ideally, this will be a mutex - * exposed by HttpSessionMutexListener. - *

The session mutex is guaranteed to be the same object during - * the entire lifetime of the session, available under the key defined - * by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a - * safe reference to synchronize on for locking on the current session. - *

In many cases, the HttpSession reference itself is a safe mutex - * as well, since it will always be the same object reference for the - * same active logical session. However, this is not guaranteed across - * different servlet containers; the only 100% safe way is a session mutex. + * Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same + * client.

More specifically, the execution of each handler method will get synchronized if this flag is "true". The + * best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by + * HttpSessionMutexListener.

The session mutex is guaranteed to be the same object during the entire lifetime of the + * session, available under the key defined by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a safe + * reference to synchronize on for locking on the current session.

In many cases, the HttpSession reference itself + * is a safe mutex as well, since it will always be the same object reference for the same active logical session. + * However, this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex. + * * @see org.springframework.web.util.HttpSessionMutexListener * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) */ @@ -276,44 +262,38 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the ParameterNameDiscoverer to use for resolving method parameter - * names if needed (e.g. for default attribute names). - *

Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}. + * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed (e.g. for default attribute + * names).

Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}. */ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } /** - * Set a custom ArgumentResolvers to use for special method parameter types. - * Such a custom ArgumentResolver will kick in first, having a chance to - * resolve an argument value before the standard argument handling kicks in. + * Set a custom ArgumentResolvers to use for special method parameter types. Such a custom ArgumentResolver will kick + * in first, having a chance to resolve an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) { - this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver}; + this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver}; } /** - * Set one or more custom ArgumentResolvers to use for special method - * parameter types. Any such custom ArgumentResolver will kick in first, - * having a chance to resolve an argument value before the standard - * argument handling kicks in. + * Set one or more custom ArgumentResolvers to use for special method parameter types. Any such custom ArgumentResolver + * will kick in first, having a chance to resolve an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) { this.customArgumentResolvers = argumentResolvers; } /** - * Set the message body converters to use. These converters are used to convert - * from and to HTTP requests and responses. + * Set the message body converters to use. These converters are used to convert from and to HTTP requests and + * responses. */ public void setMessageConverters(HttpMessageConverter[] messageConverters) { Assert.notEmpty(messageConverters, "'messageConverters' must not be empty"); this.messageConverters = messageConverters; } - - public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); } @@ -345,32 +325,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen return invokeHandlerMethod(request, response, handler); } - protected ModelAndView invokeHandlerMethod( - HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { - try { - ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); - Method handlerMethod = methodResolver.resolveHandlerMethod(request); - ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); - ServletWebRequest webRequest = new ServletWebRequest(request, response); - ExtendedModelMap implicitModel = new BindingAwareModelMap(); + ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); + Method handlerMethod = methodResolver.resolveHandlerMethod(request); + ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); + ServletWebRequest webRequest = new ServletWebRequest(request, response); + ExtendedModelMap implicitModel = new BindingAwareModelMap(); - Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); - ModelAndView mav = - methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); - methodInvoker.updateModelAttributes( - handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); - return mav; - } - catch (NoSuchRequestHandlingMethodException ex) { - return handleNoSuchRequestHandlingMethod(ex, request, response); - } - catch (HttpRequestMethodNotSupportedException ex) { - return handleHttpRequestMethodNotSupportedException(ex, request, response); - } - catch (HttpMediaTypeNotSupportedException ex) { - return handleHttpMediaTypeNotSupportedException(ex, request, response); - } + Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); + ModelAndView mav = + methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); + methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); + return mav; } public long getLastModified(HttpServletRequest request, Object handler) { @@ -378,89 +346,25 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Handle the case where no request handler method was found. - *

The default implementation logs a warning and sends an HTTP 404 error. - * Alternatively, a fallback view could be chosen, or the - * NoSuchRequestHandlingMethodException could be rethrown as-is. - * @param ex the NoSuchRequestHandlingMethodException to be handled + * Template method for creating a new ServletRequestDataBinder instance.

The default implementation creates a + * standard ServletRequestDataBinder. This can be overridden for custom ServletRequestDataBinder subclasses. + * * @param request current HTTP request - * @param response current HTTP response - * @return a ModelAndView to render, or null if handled directly - * @throws Exception an Exception that should be thrown as result of the servlet request - */ - protected ModelAndView handleNoSuchRequestHandlingMethod( - NoSuchRequestHandlingMethodException ex, HttpServletRequest request, HttpServletResponse response) - throws Exception { - - pageNotFoundLogger.warn(ex.getMessage()); - response.sendError(HttpServletResponse.SC_NOT_FOUND); - return null; - } - - /** - * Handle the case where no request handler method was found for the particular HTTP request method. - *

The default implementation logs a warning, sends an HTTP 405 error and sets the "Allow" header. - * Alternatively, a fallback view could be chosen, or the HttpRequestMethodNotSupportedException - * could be rethrown as-is. - * @param ex the HttpRequestMethodNotSupportedException to be handled - * @param request current HTTP request - * @param response current HTTP response - * @return a ModelAndView to render, or null if handled directly - * @throws Exception an Exception that should be thrown as result of the servlet request - */ - protected ModelAndView handleHttpRequestMethodNotSupportedException( - HttpRequestMethodNotSupportedException ex, HttpServletRequest request, HttpServletResponse response) - throws Exception { - - pageNotFoundLogger.warn(ex.getMessage()); - response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); - response.addHeader("Allow", StringUtils.arrayToDelimitedString(ex.getSupportedMethods(), ", ")); - return null; - } - - /** - * Handle the case where no {@linkplain HttpMessageConverter message converters} was found for the PUT or POSTed - * content. - *

The default implementation logs a warning, sends an HTTP 415 error and sets the "Allow" header. - * Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException - * could be rethrown as-is. - * @param ex the HttpMediaTypeNotSupportedException to be handled - * @param request current HTTP request - * @param response current HTTP response - * @return a ModelAndView to render, or null if handled directly - * @throws Exception an Exception that should be thrown as result of the servlet request - */ - protected ModelAndView handleHttpMediaTypeNotSupportedException( - HttpMediaTypeNotSupportedException ex, HttpServletRequest request, HttpServletResponse response) - throws Exception { - - response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); - response.addHeader("Accept", MediaType.toString(ex.getSupportedMediaTypes())); - return null; - } - - /** - * Template method for creating a new ServletRequestDataBinder instance. - *

The default implementation creates a standard ServletRequestDataBinder. - * This can be overridden for custom ServletRequestDataBinder subclasses. - * @param request current HTTP request - * @param target the target object to bind onto (or null - * if the binder is just used to convert a plain parameter value) + * @param target the target object to bind onto (or null if the binder is just used to convert a plain + * parameter value) * @param objectName the objectName of the target object * @return the ServletRequestDataBinder instance to use * @throws Exception in case of invalid state or arguments * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest) * @see ServletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter) */ - protected ServletRequestDataBinder createBinder( - HttpServletRequest request, Object target, String objectName) throws Exception { + protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName) + throws Exception { return new ServletRequestDataBinder(target, objectName); } - /** - * Build a HandlerMethodResolver for the given handler type. - */ + /** Build a HandlerMethodResolver for the given handler type. */ private ServletHandlerMethodResolver getMethodResolver(Object handler) { Class handlerClass = ClassUtils.getUserClass(handler); ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass); @@ -471,10 +375,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen return resolver; } - - /** - * Servlet-specific subclass of {@link HandlerMethodResolver}. - */ + /** Servlet-specific subclass of {@link HandlerMethodResolver}. */ private class ServletHandlerMethodResolver extends HandlerMethodResolver { private ServletHandlerMethodResolver(Class handlerType) { @@ -499,7 +400,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } boolean match = false; if (mappingInfo.paths.length > 0) { - List matchedPaths = new ArrayList(mappingInfo.paths.length); + List matchedPaths = new ArrayList(mappingInfo.paths.length); for (String mappedPath : mappingInfo.paths) { if (isPathMatch(mappedPath, lookupPath)) { if (checkParameters(mappingInfo, request)) { @@ -515,7 +416,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } Collections.sort(matchedPaths, pathComparator); - mappingInfo.matchedPaths = matchedPaths.toArray(new String[matchedPaths.size()]); + mappingInfo.matchedPaths = matchedPaths.toArray(new String[matchedPaths.size()]); } else { // No paths specified: parameter match sufficient. @@ -548,17 +449,19 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } if (oldMappedMethod != null) { - throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + - lookupPath + "': {" + oldMappedMethod + ", " + handlerMethod + - "}. If you intend to handle the same path in multiple methods, then factor " + - "them out into a dedicated handler class with that path mapped at the type level!"); + throw new IllegalStateException( + "Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" + + oldMappedMethod + ", " + handlerMethod + + "}. If you intend to handle the same path in multiple methods, then factor " + + "them out into a dedicated handler class with that path mapped at the type level!"); } } } } if (!targetHandlerMethods.isEmpty()) { List matches = new ArrayList(targetHandlerMethods.keySet()); - RequestMappingInfoComparator requestMappingInfoComparator = new RequestMappingInfoComparator(pathComparator); + RequestMappingInfoComparator requestMappingInfoComparator = + new RequestMappingInfoComparator(pathComparator); Collections.sort(matches, requestMappingInfoComparator); RequestMappingInfo bestMappingMatch = matches.get(0); if (bestMappingMatch.matchedPaths.length > 0) { @@ -597,7 +500,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } @SuppressWarnings("unchecked") - private void extractHandlerMethodUriTemplates(String mappedPath, String lookupPath, HttpServletRequest request) { + private void extractHandlerMethodUriTemplates(String mappedPath, + String lookupPath, + HttpServletRequest request) { Map variables = null; boolean hasSuffix = (mappedPath.indexOf('.') != -1); if (!hasSuffix && pathMatcher.match(mappedPath + ".*", lookupPath)) { @@ -610,7 +515,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen String realPath = "/**/" + mappedPath; if (pathMatcher.match(realPath, lookupPath)) { variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath); - } else { + } + else { realPath = realPath + ".*"; if (pathMatcher.match(realPath, lookupPath)) { variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath); @@ -628,17 +534,14 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } - - /** - * Servlet-specific subclass of {@link HandlerMethodInvoker}. - */ + /** Servlet-specific subclass of {@link HandlerMethodInvoker}. */ private class ServletHandlerMethodInvoker extends HandlerMethodInvoker { private boolean responseArgumentUsed = false; private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) { - super(resolver, webBindingInitializer, sessionAttributeStore, - parameterNameDiscoverer, customArgumentResolvers, messageConverters); + super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer, + customArgumentResolvers, messageConverters); } @Override @@ -655,8 +558,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception { - return AnnotationMethodHandlerAdapter.this.createBinder( - (HttpServletRequest) webRequest.getNativeRequest(), target, objectName); + return AnnotationMethodHandlerAdapter.this + .createBinder((HttpServletRequest) webRequest.getNativeRequest(), target, objectName); } @Override @@ -699,14 +602,14 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen Map uriTemplateVariables = (Map) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) { - throw new IllegalStateException("Could not find @PathVariable [" + pathVarName + "] in @RequestMapping"); + throw new IllegalStateException( + "Could not find @PathVariable [" + pathVarName + "] in @RequestMapping"); } return uriTemplateVariables.get(pathVarName); } @Override - protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) - throws Exception { + protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse(); @@ -745,8 +648,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } @SuppressWarnings("unchecked") - public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue, - ExtendedModelMap implicitModel, ServletWebRequest webRequest) { + public ModelAndView getModelAndView(Method handlerMethod, + Class handlerType, + Object returnValue, + ExtendedModelMap implicitModel, + ServletWebRequest webRequest) { if (returnValue instanceof ModelAndView) { ModelAndView mav = (ModelAndView) returnValue; @@ -792,7 +698,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } - static class RequestMappingInfo { String[] paths = new String[0]; @@ -806,7 +711,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen String bestMatchedPath() { return matchedPaths.length > 0 ? matchedPaths[0] : null; } - + @Override public boolean equals(Object obj) { RequestMappingInfo other = (RequestMappingInfo) obj; @@ -823,16 +728,12 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen /** * Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will - * result in: - *