KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > DispatcherServlet


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.servlet;
18
19 import java.io.IOException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.Enumeration JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Locale JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Properties JavaDoc;
30 import java.util.Set JavaDoc;
31
32 import javax.servlet.ServletException JavaDoc;
33 import javax.servlet.http.HttpServletRequest JavaDoc;
34 import javax.servlet.http.HttpServletResponse JavaDoc;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38
39 import org.springframework.beans.BeansException;
40 import org.springframework.beans.factory.BeanFactoryUtils;
41 import org.springframework.beans.factory.BeanInitializationException;
42 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
43 import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
44 import org.springframework.context.i18n.LocaleContext;
45 import org.springframework.context.i18n.LocaleContextHolder;
46 import org.springframework.core.OrderComparator;
47 import org.springframework.core.io.ClassPathResource;
48 import org.springframework.core.io.support.PropertiesLoaderUtils;
49 import org.springframework.ui.context.ThemeSource;
50 import org.springframework.util.ClassUtils;
51 import org.springframework.util.StringUtils;
52 import org.springframework.web.context.request.RequestAttributes;
53 import org.springframework.web.context.request.RequestContextHolder;
54 import org.springframework.web.context.request.ServletRequestAttributes;
55 import org.springframework.web.multipart.MultipartException;
56 import org.springframework.web.multipart.MultipartHttpServletRequest;
57 import org.springframework.web.multipart.MultipartResolver;
58 import org.springframework.web.util.NestedServletException;
59 import org.springframework.web.util.UrlPathHelper;
60 import org.springframework.web.util.WebUtils;
61
62 /**
63  * Central dispatcher for HTTP request handlers/controllers,
64  * e.g. for web UI controllers or HTTP-based remote service exporters.
65  * Dispatches to registered handlers for processing a web request,
66  * providing convenient mapping and exception handling facilities.
67  *
68  * <p>This servlet is very flexible: It can be used with just about any workflow,
69  * with the installation of the appropriate adapter classes. It offers the
70  * following functionality that distinguishes it from other request-driven
71  * web MVC frameworks:
72  *
73  * <ul>
74  * <li>It is based around a JavaBeans configuration mechanism.
75  *
76  * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided
77  * as part of an application - to control the routing of requests to handler objects.
78  * Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}.
79  * HandlerMapping objects can be defined as beans in the servlet's application context,
80  * implementing the HandlerMapping interface, overriding the default HandlerMapping
81  * if present. HandlerMappings can be given any bean name (they are tested by type).
82  *
83  * <li>It can use any {@link HandlerAdapter}; this allows to use any handler interface.
84  * Default adapters are {@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter},
85  * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} and
86  * {@link org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter},
87  * for Spring's {@link org.springframework.web.HttpRequestHandler},
88  * {@link org.springframework.web.servlet.mvc.Controller} and
89  * {@link org.springframework.web.servlet.mvc.throwaway.ThrowawayController} interfaces,
90  * respectively. HandlerAdapter objects can be added as beans in the application context,
91  * overriding the default HandlerAdapters. Like HandlerMappings, HandlerAdapters
92  * can be given any bean name (they are tested by type).
93  *
94  * <li>The dispatcher's exception resolution strategy can be specified via a
95  * {@link HandlerExceptionResolver}, for example mapping certain exceptions to
96  * error pages. Default is none. Additional HandlerExceptionResolvers can be added
97  * through the application context. HandlerExceptionResolver can be given any
98  * bean name (they are tested by type).
99  *
100  * <li>Its view resolution strategy can be specified via a {@link ViewResolver}
101  * implementation, resolving symbolic view names into View objects. Default is
102  * {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
103  * ViewResolver objects can be added as beans in the application context,
104  * overriding the default ViewResolver. ViewResolvers can be given any bean name
105  * (they are tested by type).
106  *
107  * <li>If a {@link View} or view name is not supplied by the user, then the configured
108  * {@link RequestToViewNameTranslator} will translate the current request into a
109  * view name. The corresponding bean name is "viewNameTranslator"; the default is
110  * {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
111  *
112  * <li>The dispatcher's strategy for resolving multipart requests is determined by
113  * a {@link org.springframework.web.multipart.MultipartResolver} implementation.
114  * Implementations for Jakarta Commons FileUpload and Jason Hunter's COS are
115  * included; the typical choise is
116  * {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
117  * The MultipartResolver bean name is "multipartResolver"; default is none.
118  *
119  * <li>Its locale resolution strategy is determined by a {@link LocaleResolver}.
120  * Out-of-the-box implementations work via HTTP accept header, cookie, or session.
121  * The LocaleResolver bean name is "localeResolver"; default is
122  * {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
123  *
124  * <li>Its theme resolution strategy is determined by a {@link ThemeResolver}.
125  * Implementations for a fixed theme and for cookie and session storage are included.
126  * The ThemeResolver bean name is "themeResolver"; default is
127  * {@link org.springframework.web.servlet.theme.FixedThemeResolver}.
128  * </ul>
129  *
130  * <p><b>A web application can define any number of DispatcherServlets.</b>
131  * Each servlet will operate in its own namespace, loading its own application
132  * context with mappings, handlers, etc. Only the root application context
133  * as loaded by {@link org.springframework.web.context.ContextLoaderListener},
134  * if any, will be shared.
135  *
136  * @author Rod Johnson
137  * @author Juergen Hoeller
138  * @author Rob Harrop
139  * @see org.springframework.web.HttpRequestHandler
140  * @see org.springframework.web.servlet.mvc.Controller
141  * @see org.springframework.web.context.ContextLoaderListener
142  */

143 public class DispatcherServlet extends FrameworkServlet {
144
145     /**
146      * Well-known name for the MultipartResolver object in the bean factory for this namespace.
147      */

148     public static final String JavaDoc MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
149
150     /**
151      * Well-known name for the LocaleResolver object in the bean factory for this namespace.
152      */

153     public static final String JavaDoc LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
154
155     /**
156      * Well-known name for the ThemeResolver object in the bean factory for this namespace.
157      */

158     public static final String JavaDoc THEME_RESOLVER_BEAN_NAME = "themeResolver";
159
160     /**
161      * Well-known name for the HandlerMapping object in the bean factory for this namespace.
162      * Only used when "detectAllHandlerMappings" is turned off.
163      * @see #setDetectAllHandlerMappings
164      */

165     public static final String JavaDoc HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
166
167     /**
168      * Well-known name for the HandlerAdapter object in the bean factory for this namespace.
169      * Only used when "detectAllHandlerAdapters" is turned off.
170      * @see #setDetectAllHandlerAdapters
171      */

172     public static final String JavaDoc HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
173
174     /**
175      * Well-known name for the HandlerExceptionResolver object in the bean factory for this
176      * namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off.
177      * @see #setDetectAllHandlerExceptionResolvers
178      */

179     public static final String JavaDoc HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
180
181     /**
182      * Well-known name for the RequestToViewNameTranslator object in the bean factory for
183      * this namespace.
184      */

185     public static final String JavaDoc REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
186
187     /**
188      * Well-known name for the ViewResolver object in the bean factory for this namespace.
189      * Only used when "detectAllViewResolvers" is turned off.
190      * @see #setDetectAllViewResolvers
191      */

192     public static final String JavaDoc VIEW_RESOLVER_BEAN_NAME = "viewResolver";
193
194     /**
195      * Request attribute to hold the currently chosen HandlerExecutionChain.
196      * Only used for internal optimizations.
197      */

198     public static final String JavaDoc HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherServlet.class.getName() + ".HANDLER";
199
200     /**
201      * Request attribute to hold the current web application context.
202      * Otherwise only the global web app context is obtainable by tags etc.
203      * @see org.springframework.web.servlet.support.RequestContextUtils#getWebApplicationContext
204      */

205     public static final String JavaDoc WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
206
207     /**
208      * Request attribute to hold the current LocaleResolver, retrievable by views.
209      * @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver
210      */

211     public static final String JavaDoc LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
212
213     /**
214      * Request attribute to hold the current ThemeResolver, retrievable by views.
215      * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver
216      */

217     public static final String JavaDoc THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
218
219     /**
220      * Request attribute to hold the current ThemeSource, retrievable by views.
221      * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
222      */

223     public static final String JavaDoc THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
224
225
226     /**
227      * Log category to use when no mapped handler is found for a request.
228      */

229     public static final String JavaDoc PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
230
231     /**
232      * Name of the class path resource (relative to the DispatcherServlet class)
233      * that defines DispatcherServlet's default strategy names.
234      */

235     private static final String JavaDoc DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
236
237
238     /**
239      * Additional logger to use when no mapped handler is found for a request.
240      */

241     protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
242
243     private static final Properties JavaDoc defaultStrategies;
244
245     static {
246         // Load default strategy implementations from properties file.
247
// This is currently strictly internal and not meant to be customized
248
// by application developers.
249
try {
250             ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
251             defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
252         }
253         catch (IOException JavaDoc ex) {
254             throw new IllegalStateException JavaDoc("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
255         }
256     }
257
258
259     /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
260     private boolean detectAllHandlerMappings = true;
261
262     /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
263     private boolean detectAllHandlerAdapters = true;
264
265     /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
266     private boolean detectAllHandlerExceptionResolvers = true;
267
268     /** Detect all ViewResolvers or just expect "viewResolver" bean? */
269     private boolean detectAllViewResolvers = true;
270
271     /** Perform cleanup of request attributes after include request? */
272     private boolean cleanupAfterInclude = true;
273
274     /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
275     private boolean threadContextInheritable = false;
276
277
278     /** MultipartResolver used by this servlet */
279     private MultipartResolver multipartResolver;
280
281     /** LocaleResolver used by this servlet */
282     private LocaleResolver localeResolver;
283
284     /** ThemeResolver used by this servlet */
285     private ThemeResolver themeResolver;
286
287     /** List of HandlerMappings used by this servlet */
288     private List JavaDoc handlerMappings;
289
290     /** List of HandlerAdapters used by this servlet */
291     private List JavaDoc handlerAdapters;
292
293     /** List of HandlerExceptionResolvers used by this servlet */
294     private List JavaDoc handlerExceptionResolvers;
295
296     /** RequestToViewNameTranslator used by this servlet */
297     private RequestToViewNameTranslator viewNameTranslator;
298
299     /** List of ViewResolvers used by this servlet */
300     private List JavaDoc viewResolvers;
301
302
303     /**
304      * Set whether to detect all HandlerMapping beans in this servlet's context.
305      * Else, just a single bean with name "handlerMapping" will be expected.
306      * <p>Default is "true". Turn this off if you want this servlet to use a
307      * single HandlerMapping, despite multiple HandlerMapping beans being
308      * defined in the context.
309      */

310     public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
311         this.detectAllHandlerMappings = detectAllHandlerMappings;
312     }
313
314     /**
315      * Set whether to detect all HandlerAdapter beans in this servlet's context.
316      * Else, just a single bean with name "handlerAdapter" will be expected.
317      * <p>Default is "true". Turn this off if you want this servlet to use a
318      * single HandlerAdapter, despite multiple HandlerAdapter beans being
319      * defined in the context.
320      */

321     public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
322         this.detectAllHandlerAdapters = detectAllHandlerAdapters;
323     }
324
325     /**
326      * Set whether to detect all HandlerExceptionResolver beans in this servlet's context.
327      * Else, just a single bean with name "handlerExceptionResolver" will be expected.
328      * <p>Default is "true". Turn this off if you want this servlet to use a
329      * single HandlerExceptionResolver, despite multiple HandlerExceptionResolver
330      * beans being defined in the context.
331      */

332     public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
333         this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
334     }
335
336     /**
337      * Set whether to detect all ViewResolver beans in this servlet's context.
338      * Else, just a single bean with name "viewResolver" will be expected.
339      * <p>Default is "true". Turn this off if you want this servlet to use a
340      * single ViewResolver, despite multiple ViewResolver beans being
341      * defined in the context.
342      */

343     public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
344         this.detectAllViewResolvers = detectAllViewResolvers;
345     }
346
347     /**
348      * Set whether to perform cleanup of request attributes after an include request,
349      * that is, whether to reset the original state of all request attributes after
350      * the DispatcherServlet has processed within an include request. Else, just the
351      * DispatcherServlet's own request attributes will be reset, but not model
352      * attributes for JSPs or special attributes set by views (for example, JSTL's).
353      * <p>Default is "true", which is strongly recommended. Views should not rely on
354      * request attributes having been set by (dynamic) includes. This allows JSP views
355      * rendered by an included controller to use any model attributes, even with the
356      * same names as in the main JSP, without causing side effects. Only turn this
357      * off for special needs, for example to deliberately allow main JSPs to access
358      * attributes from JSP views rendered by an included controller.
359      */

360     public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
361         this.cleanupAfterInclude = cleanupAfterInclude;
362     }
363
364     /**
365      * Set whether to expose the LocaleContext and RequestAttributes as inheritable
366      * for child threads (using an {@link java.lang.InheritableThreadLocal}).
367      * <p>Default is "false", to avoid side effects on spawned background threads.
368      * Switch this to "true" to enable inheritance for custom child threads which
369      * are spawned during request processing and only used for this request
370      * (that is, ending after their initial task, without reuse of the thread).
371      * <p><b>WARNING:</b> Do not use inheritance for child threads if you are
372      * accessing a thread pool which is configured to potentially add new threads
373      * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
374      * since this will expose the inherited context to such a pooled thread.
375      */

376     public void setThreadContextInheritable(boolean threadContextInheritable) {
377         this.threadContextInheritable = threadContextInheritable;
378     }
379
380
381     /**
382      * Overridden method, invoked after any bean properties have been set and the
383      * WebApplicationContext and BeanFactory for this namespace is available.
384      * <p>Loads HandlerMapping and HandlerAdapter objects, and configures a
385      * ViewResolver and a LocaleResolver.
386      */

387     protected void initFrameworkServlet() throws ServletException JavaDoc, BeansException {
388         initMultipartResolver();
389         initLocaleResolver();
390         initThemeResolver();
391         initHandlerMappings();
392         initHandlerAdapters();
393         initHandlerExceptionResolvers();
394         initRequestToViewNameTranslator();
395         initViewResolvers();
396     }
397
398     /**
399      * Initialize the MultipartResolver used by this class.
400      * <p>If no bean is defined with the given name in the BeanFactory
401      * for this namespace, no multipart handling is provided.
402      */

403     private void initMultipartResolver() {
404         try {
405             this.multipartResolver = (MultipartResolver)
406                     getWebApplicationContext().getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
407             if (logger.isDebugEnabled()) {
408                 logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
409             }
410         }
411         catch (NoSuchBeanDefinitionException ex) {
412             // Default is no multipart resolver.
413
this.multipartResolver = null;
414             if (logger.isDebugEnabled()) {
415                 logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
416                         "': no multipart request handling provided");
417             }
418         }
419     }
420
421     /**
422      * Initialize the LocaleResolver used by this class.
423      * <p>If no bean is defined with the given name in the BeanFactory
424      * for this namespace, we default to AcceptHeaderLocaleResolver.
425      */

426     private void initLocaleResolver() {
427         try {
428             this.localeResolver = (LocaleResolver)
429                     getWebApplicationContext().getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
430             if (logger.isDebugEnabled()) {
431                 logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
432             }
433         }
434         catch (NoSuchBeanDefinitionException ex) {
435             // We need to use the default.
436
this.localeResolver = (LocaleResolver) getDefaultStrategy(LocaleResolver.class);
437             if (logger.isDebugEnabled()) {
438                 logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
439                         "': using default [" + this.localeResolver + "]");
440             }
441         }
442     }
443
444     /**
445      * Initialize the ThemeResolver used by this class.
446      * <p>If no bean is defined with the given name in the BeanFactory
447      * for this namespace, we default to a FixedThemeResolver.
448      */

449     private void initThemeResolver() {
450         try {
451             this.themeResolver = (ThemeResolver)
452                     getWebApplicationContext().getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
453             if (logger.isDebugEnabled()) {
454                 logger.debug("Using ThemeResolver [" + this.themeResolver + "]");
455             }
456         }
457         catch (NoSuchBeanDefinitionException ex) {
458             // We need to use the default.
459
this.themeResolver = (ThemeResolver) getDefaultStrategy(ThemeResolver.class);
460             if (logger.isDebugEnabled()) {
461                 logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME +
462                         "': using default [" + this.themeResolver + "]");
463             }
464         }
465     }
466
467     /**
468      * Initialize the HandlerMappings used by this class.
469      * <p>If no HandlerMapping beans are defined in the BeanFactory
470      * for this namespace, we default to BeanNameUrlHandlerMapping.
471      */

472     private void initHandlerMappings() {
473         if (this.detectAllHandlerMappings) {
474             // Find all HandlerMappings in the ApplicationContext,
475
// including ancestor contexts.
476
Map JavaDoc matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
477                     getWebApplicationContext(), HandlerMapping.class, true, false);
478             if (!matchingBeans.isEmpty()) {
479                 this.handlerMappings = new ArrayList JavaDoc(matchingBeans.values());
480                 // We keep HandlerMappings in sorted order.
481
Collections.sort(this.handlerMappings, new OrderComparator());
482             }
483         }
484         else {
485             try {
486                 Object JavaDoc hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
487                 this.handlerMappings = Collections.singletonList(hm);
488             }
489             catch (NoSuchBeanDefinitionException ex) {
490                 // Ignore, we'll add a default HandlerMapping later.
491
}
492         }
493
494         // Ensure we have at least one HandlerMapping, by registering
495
// a default HandlerMapping if no other mappings are found.
496
if (this.handlerMappings == null) {
497             this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
498             if (logger.isDebugEnabled()) {
499                 logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
500             }
501         }
502     }
503
504     /**
505      * Initialize the HandlerAdapters used by this class.
506      * <p>If no HandlerAdapter beans are defined in the BeanFactory
507      * for this namespace, we default to SimpleControllerHandlerAdapter.
508      */

509     private void initHandlerAdapters() {
510         if (this.detectAllHandlerAdapters) {
511             // Find all HandlerAdapters in the ApplicationContext,
512
// including ancestor contexts.
513
Map JavaDoc matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
514                     getWebApplicationContext(), HandlerAdapter.class, true, false);
515             if (!matchingBeans.isEmpty()) {
516                 this.handlerAdapters = new ArrayList JavaDoc(matchingBeans.values());
517                 // We keep HandlerAdapters in sorted order.
518
Collections.sort(this.handlerAdapters, new OrderComparator());
519             }
520         }
521         else {
522             try {
523                 Object JavaDoc ha = getWebApplicationContext().getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
524                 this.handlerAdapters = Collections.singletonList(ha);
525             }
526             catch (NoSuchBeanDefinitionException ex) {
527                 // Ignore, we'll add a default HandlerAdapter later.
528
}
529         }
530
531         // Ensure we have at least some HandlerAdapters, by registering
532
// default HandlerAdapters if no other adapters are found.
533
if (this.handlerAdapters == null) {
534             this.handlerAdapters = getDefaultStrategies(HandlerAdapter.class);
535             if (logger.isDebugEnabled()) {
536                 logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
537             }
538         }
539     }
540
541     /**
542      * Initialize the HandlerExceptionResolver used by this class.
543      * <p>If no bean is defined with the given name in the BeanFactory
544      * for this namespace, we default to no exception resolver.
545      */

546     private void initHandlerExceptionResolvers() {
547         if (this.detectAllHandlerExceptionResolvers) {
548             // Find all HandlerExceptionResolvers in the ApplicationContext,
549
// including ancestor contexts.
550
Map JavaDoc matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
551                     getWebApplicationContext(), HandlerExceptionResolver.class, true, false);
552             this.handlerExceptionResolvers = new ArrayList JavaDoc(matchingBeans.values());
553             // We keep HandlerExceptionResolvers in sorted order.
554
Collections.sort(this.handlerExceptionResolvers, new OrderComparator());
555         }
556         else {
557             try {
558                 Object JavaDoc her = getWebApplicationContext().getBean(
559                         HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
560                 this.handlerExceptionResolvers = Collections.singletonList(her);
561             }
562             catch (NoSuchBeanDefinitionException ex) {
563                 // Ignore, no HandlerExceptionResolver is fine too.
564
this.handlerExceptionResolvers = getDefaultStrategies(HandlerExceptionResolver.class);
565             }
566         }
567     }
568
569     /**
570      * Initialize the RequestToViewNameTranslator used by this servlet instance. If no
571      * implementation is configured then we default to DefaultRequestToViewNameTranslator.
572      */

573     private void initRequestToViewNameTranslator() {
574         try {
575             this.viewNameTranslator = (RequestToViewNameTranslator) getWebApplicationContext().getBean(
576                     REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
577             if (logger.isDebugEnabled()) {
578                 logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");
579             }
580         }
581         catch (NoSuchBeanDefinitionException ex) {
582             // We need to use the default.
583
this.viewNameTranslator =
584                     (RequestToViewNameTranslator) getDefaultStrategy(RequestToViewNameTranslator.class);
585             if (logger.isDebugEnabled()) {
586                 logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
587                         REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
588                         "': using default [" + this.viewNameTranslator + "]");
589             }
590         }
591     }
592
593     /**
594      * Initialize the ViewResolvers used by this class.
595      * <p>If no ViewResolver beans are defined in the BeanFactory
596      * for this namespace, we default to InternalResourceViewResolver.
597      */

598     private void initViewResolvers() {
599         if (this.detectAllViewResolvers) {
600             // Find all ViewResolvers in the ApplicationContext,
601
// including ancestor contexts.
602
Map JavaDoc matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
603                     getWebApplicationContext(), ViewResolver.class, true, false);
604             if (!matchingBeans.isEmpty()) {
605                 this.viewResolvers = new ArrayList JavaDoc(matchingBeans.values());
606                 // We keep ViewResolvers in sorted order.
607
Collections.sort(this.viewResolvers, new OrderComparator());
608             }
609         }
610         else {
611             try {
612                 Object JavaDoc vr = getWebApplicationContext().getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
613                 this.viewResolvers = Collections.singletonList(vr);
614             }
615             catch (NoSuchBeanDefinitionException ex) {
616                 // Ignore, we'll add a default ViewResolver later.
617
}
618         }
619
620         // Ensure we have at least one ViewResolver, by registering
621
// a default ViewResolver if no other resolvers are found.
622
if (this.viewResolvers == null) {
623             this.viewResolvers = getDefaultStrategies(ViewResolver.class);
624             if (logger.isDebugEnabled()) {
625                 logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
626             }
627         }
628     }
629
630     /**
631      * Return this servlet's ThemeSource, if any; else return <code>null</code>.
632      * <p>Default is to return the WebApplicationContext as ThemeSource,
633      * provided that it implements the ThemeSource interface.
634      * @see #getWebApplicationContext()
635      */

636     public ThemeSource getThemeSource() {
637         if (getWebApplicationContext() instanceof ThemeSource) {
638             return (ThemeSource) getWebApplicationContext();
639         }
640         else {
641             return null;
642         }
643     }
644
645
646     /**
647      * Return the default strategy object for the given strategy interface.
648      * <p>The default implementation delegates to {@link #getDefaultStrategies},
649      * expecting a single object in the list.
650      * @param strategyInterface the strategy interface
651      * @return the corresponding strategy object
652      * @throws BeansException if initialization failed
653      * @see #getDefaultStrategies
654      */

655     protected Object JavaDoc getDefaultStrategy(Class JavaDoc strategyInterface) throws BeansException {
656         List JavaDoc strategies = getDefaultStrategies(strategyInterface);
657         if (strategies.size() != 1) {
658             throw new BeanInitializationException(
659                     "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
660         }
661         return strategies.get(0);
662     }
663
664     /**
665      * Create a List of default strategy objects for the given strategy interface.
666      * <p>The default implementation uses the "DispatcherServlet.properties" file
667      * (in the same package as the DispatcherServlet class) to determine the class names.
668      * It instantiates the strategy objects through the context's BeanFactory.
669      * @param strategyInterface the strategy interface
670      * @return the List of corresponding strategy objects
671      * @throws BeansException if initialization failed
672      */

673     protected List JavaDoc getDefaultStrategies(Class JavaDoc strategyInterface) throws BeansException {
674         String JavaDoc key = strategyInterface.getName();
675         List JavaDoc strategies = null;
676         String JavaDoc value = defaultStrategies.getProperty(key);
677         if (value != null) {
678             String JavaDoc[] classNames = StringUtils.commaDelimitedListToStringArray(value);
679             strategies = new ArrayList JavaDoc(classNames.length);
680             for (int i = 0; i < classNames.length; i++) {
681                 String JavaDoc className = classNames[i];
682                 try {
683                     Class JavaDoc clazz = ClassUtils.forName(className, getClass().getClassLoader());
684                     Object JavaDoc strategy = createDefaultStrategy(clazz);
685                     strategies.add(strategy);
686                 }
687                 catch (ClassNotFoundException JavaDoc ex) {
688                     throw new BeanInitializationException(
689                             "Could not find DispatcherServlet's default strategy class [" + className +
690                             "] for interface [" + key + "]", ex);
691                 }
692                 catch (LinkageError JavaDoc err) {
693                     throw new BeanInitializationException(
694                             "Error loading DispatcherServlet's default strategy class [" + className +
695                             "] for interface [" + key + "]: problem with class file or dependent class", err);
696                 }
697             }
698         }
699         else {
700             strategies = Collections.EMPTY_LIST;
701         }
702         return strategies;
703     }
704
705     /**
706      * Create a default strategy.
707      * <p>The default implementation uses
708      * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
709      * @param clazz the strategy implementation class to instantiate
710      * @throws BeansException if initialization failed
711      * @return the fully configured strategy instance
712      * @see #getWebApplicationContext()
713      * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
714      * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
715      */

716     protected Object JavaDoc createDefaultStrategy(Class JavaDoc clazz) throws BeansException {
717         return getWebApplicationContext().getAutowireCapableBeanFactory().createBean(
718                 clazz, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
719     }
720
721
722     /**
723      * Exposes the DispatcherServlet-specific request attributes and
724      * delegates to {@link #doDispatch} for the actual dispatching.
725      */

726     protected void doService(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response) throws Exception JavaDoc {
727         if (logger.isDebugEnabled()) {
728             logger.debug("DispatcherServlet with name '" + getServletName() + "' received request for [" +
729                     request.getRequestURI() + "]");
730         }
731
732         // Keep a snapshot of the request attributes in case of an include,
733
// to be able to restore the original attributes after the include.
734
Map JavaDoc attributesSnapshot = null;
735         if (WebUtils.isIncludeRequest(request)) {
736             logger.debug("Taking snapshot of request attributes before include");
737             attributesSnapshot = new HashMap JavaDoc();
738             Enumeration JavaDoc attrNames = request.getAttributeNames();
739             while (attrNames.hasMoreElements()) {
740                 String JavaDoc attrName = (String JavaDoc) attrNames.nextElement();
741                 if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
742                     attributesSnapshot.put(attrName, request.getAttribute(attrName));
743                 }
744             }
745         }
746
747         // Make framework objects available to handlers and view objects.
748
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
749         request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
750         request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
751         request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
752
753         try {
754             doDispatch(request, response);
755         }
756         finally {
757             // Restore the original attribute snapshot, in case of an include.
758
if (attributesSnapshot != null) {
759                 restoreAttributesAfterInclude(request, attributesSnapshot);
760             }
761         }
762     }
763
764     /**
765      * Process the actual dispatching to the handler.
766      * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
767      * The HandlerAdapter will be obtained by querying the servlet's installed
768      * HandlerAdapters to find the first that supports the handler class.
769      * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or
770      * handlers themselves to decide which methods are acceptable.
771      * @param request current HTTP request
772      * @param response current HTTP response
773      * @throws Exception in case of any kind of processing failure
774      */

775     protected void doDispatch(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response) throws Exception JavaDoc {
776         HttpServletRequest JavaDoc processedRequest = request;
777         HandlerExecutionChain mappedHandler = null;
778         int interceptorIndex = -1;
779
780         // Expose current LocaleResolver and request as LocaleContext.
781
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
782         LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
783
784         // Expose current RequestAttributes to current thread.
785
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
786         ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
787         RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
788
789         if (logger.isDebugEnabled()) {
790             logger.debug("Bound request context to thread: " + request);
791         }
792         
793         try {
794             ModelAndView mv = null;
795             try {
796                 processedRequest = checkMultipart(request);
797
798                 // Determine handler for the current request.
799
mappedHandler = getHandler(processedRequest, false);
800                 if (mappedHandler == null || mappedHandler.getHandler() == null) {
801                     noHandlerFound(processedRequest, response);
802                     return;
803                 }
804
805                 // Apply preHandle methods of registered interceptors.
806
if (mappedHandler.getInterceptors() != null) {
807                     for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
808                         HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
809                         if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
810                             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
811                             return;
812                         }
813                         interceptorIndex = i;
814                     }
815                 }
816
817                 // Actually invoke the handler.
818
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
819                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
820
821                 // Apply postHandle methods of registered interceptors.
822
if (mappedHandler.getInterceptors() != null) {
823                     for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
824                         HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
825                         interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
826                     }
827                 }
828             }
829             catch (ModelAndViewDefiningException ex) {
830                 logger.debug("ModelAndViewDefiningException encountered", ex);
831                 mv = ex.getModelAndView();
832             }
833             catch (Exception JavaDoc ex) {
834                 Object JavaDoc handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
835                 mv = processHandlerException(processedRequest, response, handler, ex);
836             }
837
838             // Did the handler return a view to render?
839
if (mv != null && !mv.wasCleared()) {
840                 render(mv, processedRequest, response);
841             }
842             else {
843                 if (logger.isDebugEnabled()) {
844                     logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
845                             getServletName() + "': assuming HandlerAdapter completed request handling");
846                 }
847             }
848
849             // Trigger after-completion for successful outcome.
850
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
851         }
852
853         catch (Exception JavaDoc ex) {
854             // Trigger after-completion for thrown exception.
855
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
856             throw ex;
857         }
858         catch (Error JavaDoc err) {
859             ServletException JavaDoc ex = new NestedServletException("Handler processing failed", err);
860             // Trigger after-completion for thrown exception.
861
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
862             throw ex;
863         }
864
865         finally {
866             // Clean up any resources used by a multipart request.
867
if (processedRequest != request) {
868                 cleanupMultipart(processedRequest);
869             }
870
871             // Reset thread-bound context.
872
RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
873             LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
874
875             // Clear request attributes.
876
requestAttributes.requestCompleted();
877             if (logger.isDebugEnabled()) {
878                 logger.debug("Cleared thread-bound request context: " + request);
879             }
880         }
881     }
882
883     /**
884      * Override HttpServlet's <code>getLastModified</code> method to evaluate
885      * the Last-Modified value of the mapped handler.
886      */

887     protected long getLastModified(HttpServletRequest JavaDoc request) {
888         try {
889             HandlerExecutionChain mappedHandler = getHandler(request, true);
890             if (mappedHandler == null || mappedHandler.getHandler() == null) {
891                 // Ignore -> will reappear on doService.
892
logger.debug("No handler found in getLastModified");
893                 return -1;
894             }
895
896             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
897             long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
898             if (logger.isDebugEnabled()) {
899                 logger.debug("Last-Modified value for [" + request.getRequestURI() + "] is [" + lastModified + "]");
900             }
901             return lastModified;
902         }
903         catch (Exception JavaDoc ex) {
904             // Ignore -> will reappear on doService.
905
logger.debug("Exception thrown in getLastModified", ex);
906             return -1;
907         }
908     }
909
910
911     /**
912      * Build a LocaleContext for the given request, exposing the request's
913      * primary locale as current locale.
914      * <p>The default implementation uses the dispatcher's LocaleResolver
915      * to obtain the current locale, which might change during a request.
916      * @param request current HTTP request
917      * @return the corresponding LocaleContext
918      */

919     protected LocaleContext buildLocaleContext(final HttpServletRequest JavaDoc request) {
920         return new LocaleContext() {
921             public Locale JavaDoc getLocale() {
922                 return localeResolver.resolveLocale(request);
923             }
924         };
925     }
926
927     /**
928      * Convert the request into a multipart request, and make multipart resolver available.
929      * If no multipart resolver is set, simply use the existing request.
930      * @param request current HTTP request
931      * @return the processed request (multipart wrapper if necessary)
932      * @see MultipartResolver#resolveMultipart
933      */

934     protected HttpServletRequest JavaDoc checkMultipart(HttpServletRequest JavaDoc request) throws MultipartException {
935         if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
936             if (request instanceof MultipartHttpServletRequest) {
937                 logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
938                         "this typically results from an additional MultipartFilter in web.xml");
939             }
940             else {
941                 return this.multipartResolver.resolveMultipart(request);
942             }
943         }
944         // If not returned before: return original request.
945
return request;
946     }
947
948     /**
949      * Clean up any resources used by the given multipart request (if any).
950      * @param request current HTTP request
951      * @see MultipartResolver#cleanupMultipart
952      */

953     protected void cleanupMultipart(HttpServletRequest JavaDoc request) {
954         if (request instanceof MultipartHttpServletRequest) {
955             this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
956         }
957     }
958
959     /**
960      * Return the HandlerExecutionChain for this request.
961      * Try all handler mappings in order.
962      * @param request current HTTP request
963      * @param cache whether to cache the HandlerExecutionChain in a request attribute
964      * @return the HandlerExceutionChain, or <code>null</code> if no handler could be found
965      */

966     protected HandlerExecutionChain getHandler(HttpServletRequest JavaDoc request, boolean cache) throws Exception JavaDoc {
967         HandlerExecutionChain handler =
968                 (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
969         if (handler != null) {
970             if (!cache) {
971                 request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
972             }
973             return handler;
974         }
975
976         Iterator JavaDoc it = this.handlerMappings.iterator();
977         while (it.hasNext()) {
978             HandlerMapping hm = (HandlerMapping) it.next();
979             if (logger.isDebugEnabled()) {
980                 logger.debug("Testing handler map [" + hm + "] in DispatcherServlet with name '" +
981                         getServletName() + "'");
982             }
983             handler = hm.getHandler(request);
984             if (handler != null) {
985                 if (cache) {
986                     request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
987                 }
988                 return handler;
989             }
990         }
991         return null;
992     }
993
994     /**
995      * No handler found -> set appropriate HTTP response status.
996      * @param request current HTTP request
997      * @param response current HTTP response
998      * @throws IOException if thrown by the HttpServletResponse
999      */

1000    protected void noHandlerFound(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response) throws IOException JavaDoc {
1001        if (pageNotFoundLogger.isWarnEnabled()) {
1002            String JavaDoc requestUri = new UrlPathHelper().getRequestUri(request);
1003            pageNotFoundLogger.warn("No mapping for [" + requestUri +
1004                    "] in DispatcherServlet with name '" + getServletName() + "'");
1005        }
1006        response.sendError(HttpServletResponse.SC_NOT_FOUND);
1007    }
1008
1009    /**
1010     * Return the HandlerAdapter for this handler object.
1011     * @param handler the handler object to find an adapter for
1012     * @throws ServletException if no HandlerAdapter can be found for the handler.
1013     * This is a fatal error.
1014     */

1015    protected HandlerAdapter getHandlerAdapter(Object JavaDoc handler) throws ServletException JavaDoc {
1016        Iterator JavaDoc it = this.handlerAdapters.iterator();
1017        while (it.hasNext()) {
1018            HandlerAdapter ha = (HandlerAdapter) it.next();
1019            if (logger.isDebugEnabled()) {
1020                logger.debug("Testing handler adapter [" + ha + "]");
1021            }
1022            if (ha.supports(handler)) {
1023                return ha;
1024            }
1025        }
1026        throw new ServletException JavaDoc("No adapter for handler [" + handler +
1027                "]: Does your handler implement a supported interface like Controller?");
1028    }
1029
1030    /**
1031     * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
1032     * @param request current HTTP request
1033     * @param response current HTTP response
1034     * @param handler the executed handler, or <code>null</code> if none chosen at the time of
1035     * the exception (for example, if multipart resolution failed)
1036     * @param ex the exception that got thrown during handler execution
1037     * @return a corresponding ModelAndView to forward to
1038     * @throws Exception if no error ModelAndView found
1039     */

1040    protected ModelAndView processHandlerException(
1041            HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc handler, Exception JavaDoc ex)
1042            throws Exception JavaDoc {
1043
1044        ModelAndView exMv = null;
1045        for (Iterator JavaDoc it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) {
1046            HandlerExceptionResolver resolver = (HandlerExceptionResolver) it.next();
1047            exMv = resolver.resolveException(request, response, handler, ex);
1048        }
1049        if (exMv != null) {
1050            if (logger.isDebugEnabled()) {
1051                logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
1052            }
1053            return exMv;
1054        }
1055        else {
1056            throw ex;
1057        }
1058    }
1059
1060    /**
1061     * Render the given ModelAndView. This is the last stage in handling a request.
1062     * It may involve resolving the view by name.
1063     * @param mv the ModelAndView to render
1064     * @param request current HTTP servlet request
1065     * @param response current HTTP servlet response
1066     * @throws Exception if there's a problem rendering the view
1067     */

1068    protected void render(ModelAndView mv, HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
1069            throws Exception JavaDoc {
1070
1071        // Determine locale for request and apply it to the response.
1072
Locale JavaDoc locale = this.localeResolver.resolveLocale(request);
1073        response.setLocale(locale);
1074
1075        View view = null;
1076
1077        // Do we need view name translation?
1078
if (!mv.hasView()) {
1079            mv.setViewName(getDefaultViewName(request));
1080        }
1081
1082        if (mv.isReference()) {
1083            // We need to resolve the view name.
1084
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
1085            if (view == null) {
1086                throw new ServletException JavaDoc("Could not resolve view with name '" + mv.getViewName() +
1087                        "' in servlet with name '" + getServletName() + "'");
1088            }
1089        }
1090        else {
1091            // No need to lookup: the ModelAndView object contains the actual View object.
1092
view = mv.getView();
1093            if (view == null) {
1094                throw new ServletException JavaDoc("ModelAndView [" + mv + "] neither contains a view name nor a " +
1095                        "View object in servlet with name '" + getServletName() + "'");
1096            }
1097        }
1098
1099        // Delegate to the View object for rendering.
1100
if (logger.isDebugEnabled()) {
1101            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
1102        }
1103        view.render(mv.getModelInternal(), request, response);
1104    }
1105
1106    /**
1107     * Translate the supplied request into a default view name.
1108     * @param request current HTTP servlet request
1109     * @return the view name
1110     * @throws Exception if view name translation failed
1111     */

1112    protected String JavaDoc getDefaultViewName(HttpServletRequest JavaDoc request) throws Exception JavaDoc {
1113        String JavaDoc viewName = this.viewNameTranslator.getViewName(request);
1114        if (viewName == null) {
1115            throw new ServletException JavaDoc("Could not translate request [" + request +
1116                    "] into view name using [" + this.viewNameTranslator.getClass().getName() + "]");
1117        }
1118        return viewName;
1119    }
1120
1121    /**
1122     * Resolve the given view name into a View object (to be rendered).
1123     * <p>Default implementations asks all ViewResolvers of this dispatcher.
1124     * Can be overridden for custom resolution strategies, potentially based
1125     * on specific model attributes or request parameters.
1126     * @param viewName the name of the view to resolve
1127     * @param model the model to be passed to the view
1128     * @param locale the current locale
1129     * @param request current HTTP servlet request
1130     * @return the View object, or <code>null</code> if none found
1131     * @throws Exception if the view cannot be resolved
1132     * (typically in case of problems creating an actual View object)
1133     * @see ViewResolver#resolveViewName
1134     */

1135    protected View resolveViewName(String JavaDoc viewName, Map JavaDoc model, Locale JavaDoc locale, HttpServletRequest JavaDoc request)
1136            throws Exception JavaDoc {
1137
1138        for (Iterator JavaDoc it = this.viewResolvers.iterator(); it.hasNext();) {
1139            ViewResolver viewResolver = (ViewResolver) it.next();
1140            View view = viewResolver.resolveViewName(viewName, locale);
1141            if (view != null) {
1142                return view;
1143            }
1144        }
1145        return null;
1146    }
1147
1148    /**
1149     * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
1150     * Will just invoke afterCompletion for all interceptors whose preHandle
1151     * invocation has successfully completed and returned true.
1152     * @param mappedHandler the mapped HandlerExecutionChain
1153     * @param interceptorIndex index of last interceptor that successfully completed
1154     * @param ex Exception thrown on handler execution, or <code>null</code> if none
1155     * @see HandlerInterceptor#afterCompletion
1156     */

1157    private void triggerAfterCompletion(
1158            HandlerExecutionChain mappedHandler, int interceptorIndex,
1159            HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Exception JavaDoc ex)
1160            throws Exception JavaDoc {
1161
1162        // Apply afterCompletion methods of registered interceptors.
1163
if (mappedHandler != null) {
1164            if (mappedHandler.getInterceptors() != null) {
1165                for (int i = interceptorIndex; i >= 0; i--) {
1166                    HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
1167                    try {
1168                        interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
1169                    }
1170                    catch (Throwable JavaDoc ex2) {
1171                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
1172                    }
1173                }
1174            }
1175        }
1176    }
1177
1178    /**
1179     * Restore the request attributes after an include.
1180     * @param request current HTTP request
1181     * @param attributesSnapshot the snapshot of the request attributes
1182     * before the include
1183     */

1184    private void restoreAttributesAfterInclude(HttpServletRequest JavaDoc request, Map JavaDoc attributesSnapshot) {
1185        logger.debug("Restoring snapshot of request attributes after include");
1186
1187        // Need to copy into separate Collection here, to avoid side effects
1188
// on the Enumeration when removing attributes.
1189
Set JavaDoc attrsToCheck = new HashSet JavaDoc();
1190        Enumeration JavaDoc attrNames = request.getAttributeNames();
1191        while (attrNames.hasMoreElements()) {
1192            String JavaDoc attrName = (String JavaDoc) attrNames.nextElement();
1193            if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
1194                attrsToCheck.add(attrName);
1195            }
1196        }
1197
1198        // Iterate over the attributes to check, restoring the original value
1199
// or removing the attribute, respectively, if appropriate.
1200
for (Iterator JavaDoc it = attrsToCheck.iterator(); it.hasNext();) {
1201            String JavaDoc attrName = (String JavaDoc) it.next();
1202            Object JavaDoc attrValue = attributesSnapshot.get(attrName);
1203            if (attrValue != null) {
1204                if (logger.isDebugEnabled()) {
1205                    logger.debug("Restoring original value of attribute [" + attrName + "] after include");
1206                }
1207                request.setAttribute(attrName, attrValue);
1208            }
1209            else {
1210                if (logger.isDebugEnabled()) {
1211                    logger.debug("Removing attribute [" + attrName + "] after include");
1212                }
1213                request.removeAttribute(attrName);
1214            }
1215        }
1216    }
1217
1218}
1219
Popular Tags