001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet;
018
019import java.io.IOException;
020import java.security.Principal;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.List;
024import java.util.concurrent.Callable;
025import java.util.stream.Collectors;
026
027import javax.servlet.DispatcherType;
028import javax.servlet.ServletContext;
029import javax.servlet.ServletException;
030import javax.servlet.http.HttpServletRequest;
031import javax.servlet.http.HttpServletResponse;
032import javax.servlet.http.HttpServletResponseWrapper;
033
034import org.springframework.beans.BeanUtils;
035import org.springframework.context.ApplicationContext;
036import org.springframework.context.ApplicationContextAware;
037import org.springframework.context.ApplicationContextException;
038import org.springframework.context.ApplicationContextInitializer;
039import org.springframework.context.ApplicationListener;
040import org.springframework.context.ConfigurableApplicationContext;
041import org.springframework.context.event.ContextRefreshedEvent;
042import org.springframework.context.event.SourceFilteringListener;
043import org.springframework.context.i18n.LocaleContext;
044import org.springframework.context.i18n.LocaleContextHolder;
045import org.springframework.context.i18n.SimpleLocaleContext;
046import org.springframework.core.GenericTypeResolver;
047import org.springframework.core.annotation.AnnotationAwareOrderComparator;
048import org.springframework.core.env.ConfigurableEnvironment;
049import org.springframework.http.HttpMethod;
050import org.springframework.http.HttpStatus;
051import org.springframework.lang.Nullable;
052import org.springframework.util.ClassUtils;
053import org.springframework.util.ObjectUtils;
054import org.springframework.util.StringUtils;
055import org.springframework.web.context.ConfigurableWebApplicationContext;
056import org.springframework.web.context.ConfigurableWebEnvironment;
057import org.springframework.web.context.ContextLoader;
058import org.springframework.web.context.WebApplicationContext;
059import org.springframework.web.context.request.NativeWebRequest;
060import org.springframework.web.context.request.RequestAttributes;
061import org.springframework.web.context.request.RequestContextHolder;
062import org.springframework.web.context.request.ServletRequestAttributes;
063import org.springframework.web.context.request.async.CallableProcessingInterceptor;
064import org.springframework.web.context.request.async.WebAsyncManager;
065import org.springframework.web.context.request.async.WebAsyncUtils;
066import org.springframework.web.context.support.ServletRequestHandledEvent;
067import org.springframework.web.context.support.WebApplicationContextUtils;
068import org.springframework.web.context.support.XmlWebApplicationContext;
069import org.springframework.web.cors.CorsUtils;
070import org.springframework.web.util.NestedServletException;
071import org.springframework.web.util.WebUtils;
072
073/**
074 * Base servlet for Spring's web framework. Provides integration with
075 * a Spring application context, in a JavaBean-based overall solution.
076 *
077 * <p>This class offers the following functionality:
078 * <ul>
079 * <li>Manages a {@link org.springframework.web.context.WebApplicationContext
080 * WebApplicationContext} instance per servlet. The servlet's configuration is determined
081 * by beans in the servlet's namespace.
082 * <li>Publishes events on request processing, whether or not a request is
083 * successfully handled.
084 * </ul>
085 *
086 * <p>Subclasses must implement {@link #doService} to handle requests. Because this extends
087 * {@link HttpServletBean} rather than HttpServlet directly, bean properties are
088 * automatically mapped onto it. Subclasses can override {@link #initFrameworkServlet()}
089 * for custom initialization.
090 *
091 * <p>Detects a "contextClass" parameter at the servlet init-param level,
092 * falling back to the default context class,
093 * {@link org.springframework.web.context.support.XmlWebApplicationContext
094 * XmlWebApplicationContext}, if not found. Note that, with the default
095 * {@code FrameworkServlet}, a custom context class needs to implement the
096 * {@link org.springframework.web.context.ConfigurableWebApplicationContext
097 * ConfigurableWebApplicationContext} SPI.
098 *
099 * <p>Accepts an optional "contextInitializerClasses" servlet init-param that
100 * specifies one or more {@link org.springframework.context.ApplicationContextInitializer
101 * ApplicationContextInitializer} classes. The managed web application context will be
102 * delegated to these initializers, allowing for additional programmatic configuration,
103 * e.g. adding property sources or activating profiles against the {@linkplain
104 * org.springframework.context.ConfigurableApplicationContext#getEnvironment() context's
105 * environment}. See also {@link org.springframework.web.context.ContextLoader} which
106 * supports a "contextInitializerClasses" context-param with identical semantics for
107 * the "root" web application context.
108 *
109 * <p>Passes a "contextConfigLocation" servlet init-param to the context instance,
110 * parsing it into potentially multiple file paths which can be separated by any
111 * number of commas and spaces, like "test-servlet.xml, myServlet.xml".
112 * If not explicitly specified, the context implementation is supposed to build a
113 * default location from the namespace of the servlet.
114 *
115 * <p>Note: In case of multiple config locations, later bean definitions will
116 * override ones defined in earlier loaded files, at least when using Spring's
117 * default ApplicationContext implementation. This can be leveraged to
118 * deliberately override certain bean definitions via an extra XML file.
119 *
120 * <p>The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a
121 * servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location
122 * with XmlWebApplicationContext). The namespace can also be set explicitly via
123 * the "namespace" servlet init-param.
124 *
125 * <p>As of Spring 3.1, {@code FrameworkServlet} may now be injected with a web
126 * application context, rather than creating its own internally. This is useful in Servlet
127 * 3.0+ environments, which support programmatic registration of servlet instances. See
128 * {@link #FrameworkServlet(WebApplicationContext)} Javadoc for details.
129 *
130 * @author Rod Johnson
131 * @author Juergen Hoeller
132 * @author Sam Brannen
133 * @author Chris Beams
134 * @author Rossen Stoyanchev
135 * @author Phillip Webb
136 * @see #doService
137 * @see #setContextClass
138 * @see #setContextConfigLocation
139 * @see #setContextInitializerClasses
140 * @see #setNamespace
141 */
142@SuppressWarnings("serial")
143public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
144
145        /**
146         * Suffix for WebApplicationContext namespaces. If a servlet of this class is
147         * given the name "test" in a context, the namespace used by the servlet will
148         * resolve to "test-servlet".
149         */
150        public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
151
152        /**
153         * Default context class for FrameworkServlet.
154         * @see org.springframework.web.context.support.XmlWebApplicationContext
155         */
156        public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
157
158        /**
159         * Prefix for the ServletContext attribute for the WebApplicationContext.
160         * The completion is the servlet name.
161         */
162        public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
163
164        /**
165         * Any number of these characters are considered delimiters between
166         * multiple values in a single init-param String value.
167         */
168        private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
169
170
171        /** ServletContext attribute to find the WebApplicationContext in. */
172        @Nullable
173        private String contextAttribute;
174
175        /** WebApplicationContext implementation class to create. */
176        private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
177
178        /** WebApplicationContext id to assign. */
179        @Nullable
180        private String contextId;
181
182        /** Namespace for this servlet. */
183        @Nullable
184        private String namespace;
185
186        /** Explicit context config location. */
187        @Nullable
188        private String contextConfigLocation;
189
190        /** Actual ApplicationContextInitializer instances to apply to the context. */
191        private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
192                        new ArrayList<>();
193
194        /** Comma-delimited ApplicationContextInitializer class names set through init param. */
195        @Nullable
196        private String contextInitializerClasses;
197
198        /** Should we publish the context as a ServletContext attribute?. */
199        private boolean publishContext = true;
200
201        /** Should we publish a ServletRequestHandledEvent at the end of each request?. */
202        private boolean publishEvents = true;
203
204        /** Expose LocaleContext and RequestAttributes as inheritable for child threads?. */
205        private boolean threadContextInheritable = false;
206
207        /** Should we dispatch an HTTP OPTIONS request to {@link #doService}?. */
208        private boolean dispatchOptionsRequest = false;
209
210        /** Should we dispatch an HTTP TRACE request to {@link #doService}?. */
211        private boolean dispatchTraceRequest = false;
212
213        /** Whether to log potentially sensitive info (request params at DEBUG + headers at TRACE). */
214        private boolean enableLoggingRequestDetails = false;
215
216        /** WebApplicationContext for this servlet. */
217        @Nullable
218        private WebApplicationContext webApplicationContext;
219
220        /** If the WebApplicationContext was injected via {@link #setApplicationContext}. */
221        private boolean webApplicationContextInjected = false;
222
223        /** Flag used to detect whether onRefresh has already been called. */
224        private volatile boolean refreshEventReceived = false;
225
226        /** Monitor for synchronized onRefresh execution. */
227        private final Object onRefreshMonitor = new Object();
228
229
230        /**
231         * Create a new {@code FrameworkServlet} that will create its own internal web
232         * application context based on defaults and values provided through servlet
233         * init-params. Typically used in Servlet 2.5 or earlier environments, where the only
234         * option for servlet registration is through {@code web.xml} which requires the use
235         * of a no-arg constructor.
236         * <p>Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation')
237         * will dictate which XML files will be loaded by the
238         * {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext}
239         * <p>Calling {@link #setContextClass} (init-param 'contextClass') overrides the
240         * default {@code XmlWebApplicationContext} and allows for specifying an alternative class,
241         * such as {@code AnnotationConfigWebApplicationContext}.
242         * <p>Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses')
243         * indicates which {@link ApplicationContextInitializer} classes should be used to
244         * further configure the internal application context prior to refresh().
245         * @see #FrameworkServlet(WebApplicationContext)
246         */
247        public FrameworkServlet() {
248        }
249
250        /**
251         * Create a new {@code FrameworkServlet} with the given web application context. This
252         * constructor is useful in Servlet 3.0+ environments where instance-based registration
253         * of servlets is possible through the {@link ServletContext#addServlet} API.
254         * <p>Using this constructor indicates that the following properties / init-params
255         * will be ignored:
256         * <ul>
257         * <li>{@link #setContextClass(Class)} / 'contextClass'</li>
258         * <li>{@link #setContextConfigLocation(String)} / 'contextConfigLocation'</li>
259         * <li>{@link #setContextAttribute(String)} / 'contextAttribute'</li>
260         * <li>{@link #setNamespace(String)} / 'namespace'</li>
261         * </ul>
262         * <p>The given web application context may or may not yet be {@linkplain
263         * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
264         * of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong>
265         * already been refreshed (the recommended approach), then the following will occur:
266         * <ul>
267         * <li>If the given context does not already have a {@linkplain
268         * ConfigurableApplicationContext#setParent parent}, the root application context
269         * will be set as the parent.</li>
270         * <li>If the given context has not already been assigned an {@linkplain
271         * ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
272         * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
273         * the application context</li>
274         * <li>{@link #postProcessWebApplicationContext} will be called</li>
275         * <li>Any {@link ApplicationContextInitializer ApplicationContextInitializers} specified through the
276         * "contextInitializerClasses" init-param or through the {@link
277         * #setContextInitializers} property will be applied.</li>
278         * <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li>
279         * </ul>
280         * If the context has already been refreshed or does not implement
281         * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
282         * assumption that the user has performed these actions (or not) per his or her
283         * specific needs.
284         * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
285         * @param webApplicationContext the context to use
286         * @see #initWebApplicationContext
287         * @see #configureAndRefreshWebApplicationContext
288         * @see org.springframework.web.WebApplicationInitializer
289         */
290        public FrameworkServlet(WebApplicationContext webApplicationContext) {
291                this.webApplicationContext = webApplicationContext;
292        }
293
294
295        /**
296         * Set the name of the ServletContext attribute which should be used to retrieve the
297         * {@link WebApplicationContext} that this servlet is supposed to use.
298         */
299        public void setContextAttribute(@Nullable String contextAttribute) {
300                this.contextAttribute = contextAttribute;
301        }
302
303        /**
304         * Return the name of the ServletContext attribute which should be used to retrieve the
305         * {@link WebApplicationContext} that this servlet is supposed to use.
306         */
307        @Nullable
308        public String getContextAttribute() {
309                return this.contextAttribute;
310        }
311
312        /**
313         * Set a custom context class. This class must be of type
314         * {@link org.springframework.web.context.WebApplicationContext}.
315         * <p>When using the default FrameworkServlet implementation,
316         * the context class must also implement the
317         * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
318         * interface.
319         * @see #createWebApplicationContext
320         */
321        public void setContextClass(Class<?> contextClass) {
322                this.contextClass = contextClass;
323        }
324
325        /**
326         * Return the custom context class.
327         */
328        public Class<?> getContextClass() {
329                return this.contextClass;
330        }
331
332        /**
333         * Specify a custom WebApplicationContext id,
334         * to be used as serialization id for the underlying BeanFactory.
335         */
336        public void setContextId(@Nullable String contextId) {
337                this.contextId = contextId;
338        }
339
340        /**
341         * Return the custom WebApplicationContext id, if any.
342         */
343        @Nullable
344        public String getContextId() {
345                return this.contextId;
346        }
347
348        /**
349         * Set a custom namespace for this servlet,
350         * to be used for building a default context config location.
351         */
352        public void setNamespace(String namespace) {
353                this.namespace = namespace;
354        }
355
356        /**
357         * Return the namespace for this servlet, falling back to default scheme if
358         * no custom namespace was set: e.g. "test-servlet" for a servlet named "test".
359         */
360        public String getNamespace() {
361                return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
362        }
363
364        /**
365         * Set the context config location explicitly, instead of relying on the default
366         * location built from the namespace. This location string can consist of
367         * multiple locations separated by any number of commas and spaces.
368         */
369        public void setContextConfigLocation(@Nullable String contextConfigLocation) {
370                this.contextConfigLocation = contextConfigLocation;
371        }
372
373        /**
374         * Return the explicit context config location, if any.
375         */
376        @Nullable
377        public String getContextConfigLocation() {
378                return this.contextConfigLocation;
379        }
380
381        /**
382         * Specify which {@link ApplicationContextInitializer} instances should be used
383         * to initialize the application context used by this {@code FrameworkServlet}.
384         * @see #configureAndRefreshWebApplicationContext
385         * @see #applyInitializers
386         */
387        @SuppressWarnings("unchecked")
388        public void setContextInitializers(@Nullable ApplicationContextInitializer<?>... initializers) {
389                if (initializers != null) {
390                        for (ApplicationContextInitializer<?> initializer : initializers) {
391                                this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
392                        }
393                }
394        }
395
396        /**
397         * Specify the set of fully-qualified {@link ApplicationContextInitializer} class
398         * names, per the optional "contextInitializerClasses" servlet init-param.
399         * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
400         * @see #applyInitializers(ConfigurableApplicationContext)
401         */
402        public void setContextInitializerClasses(String contextInitializerClasses) {
403                this.contextInitializerClasses = contextInitializerClasses;
404        }
405
406        /**
407         * Set whether to publish this servlet's context as a ServletContext attribute,
408         * available to all objects in the web container. Default is "true".
409         * <p>This is especially handy during testing, although it is debatable whether
410         * it's good practice to let other application objects access the context this way.
411         */
412        public void setPublishContext(boolean publishContext) {
413                this.publishContext = publishContext;
414        }
415
416        /**
417         * Set whether this servlet should publish a ServletRequestHandledEvent at the end
418         * of each request. Default is "true"; can be turned off for a slight performance
419         * improvement, provided that no ApplicationListeners rely on such events.
420         * @see org.springframework.web.context.support.ServletRequestHandledEvent
421         */
422        public void setPublishEvents(boolean publishEvents) {
423                this.publishEvents = publishEvents;
424        }
425
426        /**
427         * Set whether to expose the LocaleContext and RequestAttributes as inheritable
428         * for child threads (using an {@link java.lang.InheritableThreadLocal}).
429         * <p>Default is "false", to avoid side effects on spawned background threads.
430         * Switch this to "true" to enable inheritance for custom child threads which
431         * are spawned during request processing and only used for this request
432         * (that is, ending after their initial task, without reuse of the thread).
433         * <p><b>WARNING:</b> Do not use inheritance for child threads if you are
434         * accessing a thread pool which is configured to potentially add new threads
435         * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
436         * since this will expose the inherited context to such a pooled thread.
437         */
438        public void setThreadContextInheritable(boolean threadContextInheritable) {
439                this.threadContextInheritable = threadContextInheritable;
440        }
441
442        /**
443         * Set whether this servlet should dispatch an HTTP OPTIONS request to
444         * the {@link #doService} method.
445         * <p>Default in the {@code FrameworkServlet} is "false", applying
446         * {@link javax.servlet.http.HttpServlet}'s default behavior (i.e.enumerating
447         * all standard HTTP request methods as a response to the OPTIONS request).
448         * Note however that as of 4.3 the {@code DispatcherServlet} sets this
449         * property to "true" by default due to its built-in support for OPTIONS.
450         * <p>Turn this flag on if you prefer OPTIONS requests to go through the
451         * regular dispatching chain, just like other HTTP requests. This usually
452         * means that your controllers will receive those requests; make sure
453         * that those endpoints are actually able to handle an OPTIONS request.
454         * <p>Note that HttpServlet's default OPTIONS processing will be applied
455         * in any case if your controllers happen to not set the 'Allow' header
456         * (as required for an OPTIONS response).
457         */
458        public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) {
459                this.dispatchOptionsRequest = dispatchOptionsRequest;
460        }
461
462        /**
463         * Set whether this servlet should dispatch an HTTP TRACE request to
464         * the {@link #doService} method.
465         * <p>Default is "false", applying {@link javax.servlet.http.HttpServlet}'s
466         * default behavior (i.e. reflecting the message received back to the client).
467         * <p>Turn this flag on if you prefer TRACE requests to go through the
468         * regular dispatching chain, just like other HTTP requests. This usually
469         * means that your controllers will receive those requests; make sure
470         * that those endpoints are actually able to handle a TRACE request.
471         * <p>Note that HttpServlet's default TRACE processing will be applied
472         * in any case if your controllers happen to not generate a response
473         * of content type 'message/http' (as required for a TRACE response).
474         */
475        public void setDispatchTraceRequest(boolean dispatchTraceRequest) {
476                this.dispatchTraceRequest = dispatchTraceRequest;
477        }
478
479        /**
480         * Whether to log request params at DEBUG level, and headers at TRACE level.
481         * Both may contain sensitive information.
482         * <p>By default set to {@code false} so that request details are not shown.
483         * @param enable whether to enable or not
484         * @since 5.1
485         */
486        public void setEnableLoggingRequestDetails(boolean enable) {
487                this.enableLoggingRequestDetails = enable;
488        }
489
490        /**
491         * Whether logging of potentially sensitive, request details at DEBUG and
492         * TRACE level is allowed.
493         * @since 5.1
494         */
495        public boolean isEnableLoggingRequestDetails() {
496                return this.enableLoggingRequestDetails;
497        }
498
499        /**
500         * Called by Spring via {@link ApplicationContextAware} to inject the current
501         * application context. This method allows FrameworkServlets to be registered as
502         * Spring beans inside an existing {@link WebApplicationContext} rather than
503         * {@link #findWebApplicationContext() finding} a
504         * {@link org.springframework.web.context.ContextLoaderListener bootstrapped} context.
505         * <p>Primarily added to support use in embedded servlet containers.
506         * @since 4.0
507         */
508        @Override
509        public void setApplicationContext(ApplicationContext applicationContext) {
510                if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
511                        this.webApplicationContext = (WebApplicationContext) applicationContext;
512                        this.webApplicationContextInjected = true;
513                }
514        }
515
516
517        /**
518         * Overridden method of {@link HttpServletBean}, invoked after any bean properties
519         * have been set. Creates this servlet's WebApplicationContext.
520         */
521        @Override
522        protected final void initServletBean() throws ServletException {
523                getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
524                if (logger.isInfoEnabled()) {
525                        logger.info("Initializing Servlet '" + getServletName() + "'");
526                }
527                long startTime = System.currentTimeMillis();
528
529                try {
530                        this.webApplicationContext = initWebApplicationContext();
531                        initFrameworkServlet();
532                }
533                catch (ServletException | RuntimeException ex) {
534                        logger.error("Context initialization failed", ex);
535                        throw ex;
536                }
537
538                if (logger.isDebugEnabled()) {
539                        String value = this.enableLoggingRequestDetails ?
540                                        "shown which may lead to unsafe logging of potentially sensitive data" :
541                                        "masked to prevent unsafe logging of potentially sensitive data";
542                        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
543                                        "': request parameters and headers will be " + value);
544                }
545
546                if (logger.isInfoEnabled()) {
547                        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
548                }
549        }
550
551        /**
552         * Initialize and publish the WebApplicationContext for this servlet.
553         * <p>Delegates to {@link #createWebApplicationContext} for actual creation
554         * of the context. Can be overridden in subclasses.
555         * @return the WebApplicationContext instance
556         * @see #FrameworkServlet(WebApplicationContext)
557         * @see #setContextClass
558         * @see #setContextConfigLocation
559         */
560        protected WebApplicationContext initWebApplicationContext() {
561                WebApplicationContext rootContext =
562                                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
563                WebApplicationContext wac = null;
564
565                if (this.webApplicationContext != null) {
566                        // A context instance was injected at construction time -> use it
567                        wac = this.webApplicationContext;
568                        if (wac instanceof ConfigurableWebApplicationContext) {
569                                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
570                                if (!cwac.isActive()) {
571                                        // The context has not yet been refreshed -> provide services such as
572                                        // setting the parent context, setting the application context id, etc
573                                        if (cwac.getParent() == null) {
574                                                // The context instance was injected without an explicit parent -> set
575                                                // the root application context (if any; may be null) as the parent
576                                                cwac.setParent(rootContext);
577                                        }
578                                        configureAndRefreshWebApplicationContext(cwac);
579                                }
580                        }
581                }
582                if (wac == null) {
583                        // No context instance was injected at construction time -> see if one
584                        // has been registered in the servlet context. If one exists, it is assumed
585                        // that the parent context (if any) has already been set and that the
586                        // user has performed any initialization such as setting the context id
587                        wac = findWebApplicationContext();
588                }
589                if (wac == null) {
590                        // No context instance is defined for this servlet -> create a local one
591                        wac = createWebApplicationContext(rootContext);
592                }
593
594                if (!this.refreshEventReceived) {
595                        // Either the context is not a ConfigurableApplicationContext with refresh
596                        // support or the context injected at construction time had already been
597                        // refreshed -> trigger initial onRefresh manually here.
598                        synchronized (this.onRefreshMonitor) {
599                                onRefresh(wac);
600                        }
601                }
602
603                if (this.publishContext) {
604                        // Publish the context as a servlet context attribute.
605                        String attrName = getServletContextAttributeName();
606                        getServletContext().setAttribute(attrName, wac);
607                }
608
609                return wac;
610        }
611
612        /**
613         * Retrieve a {@code WebApplicationContext} from the {@code ServletContext}
614         * attribute with the {@link #setContextAttribute configured name}. The
615         * {@code WebApplicationContext} must have already been loaded and stored in the
616         * {@code ServletContext} before this servlet gets initialized (or invoked).
617         * <p>Subclasses may override this method to provide a different
618         * {@code WebApplicationContext} retrieval strategy.
619         * @return the WebApplicationContext for this servlet, or {@code null} if not found
620         * @see #getContextAttribute()
621         */
622        @Nullable
623        protected WebApplicationContext findWebApplicationContext() {
624                String attrName = getContextAttribute();
625                if (attrName == null) {
626                        return null;
627                }
628                WebApplicationContext wac =
629                                WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
630                if (wac == null) {
631                        throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
632                }
633                return wac;
634        }
635
636        /**
637         * Instantiate the WebApplicationContext for this servlet, either a default
638         * {@link org.springframework.web.context.support.XmlWebApplicationContext}
639         * or a {@link #setContextClass custom context class}, if set.
640         * <p>This implementation expects custom contexts to implement the
641         * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
642         * interface. Can be overridden in subclasses.
643         * <p>Do not forget to register this servlet instance as application listener on the
644         * created context (for triggering its {@link #onRefresh callback}, and to call
645         * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
646         * before returning the context instance.
647         * @param parent the parent ApplicationContext to use, or {@code null} if none
648         * @return the WebApplicationContext for this servlet
649         * @see org.springframework.web.context.support.XmlWebApplicationContext
650         */
651        protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
652                Class<?> contextClass = getContextClass();
653                if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
654                        throw new ApplicationContextException(
655                                        "Fatal initialization error in servlet with name '" + getServletName() +
656                                        "': custom WebApplicationContext class [" + contextClass.getName() +
657                                        "] is not of type ConfigurableWebApplicationContext");
658                }
659                ConfigurableWebApplicationContext wac =
660                                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
661
662                wac.setEnvironment(getEnvironment());
663                wac.setParent(parent);
664                String configLocation = getContextConfigLocation();
665                if (configLocation != null) {
666                        wac.setConfigLocation(configLocation);
667                }
668                configureAndRefreshWebApplicationContext(wac);
669
670                return wac;
671        }
672
673        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
674                if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
675                        // The application context id is still set to its original default value
676                        // -> assign a more useful id based on available information
677                        if (this.contextId != null) {
678                                wac.setId(this.contextId);
679                        }
680                        else {
681                                // Generate default id...
682                                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
683                                                ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
684                        }
685                }
686
687                wac.setServletContext(getServletContext());
688                wac.setServletConfig(getServletConfig());
689                wac.setNamespace(getNamespace());
690                wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
691
692                // The wac environment's #initPropertySources will be called in any case when the context
693                // is refreshed; do it eagerly here to ensure servlet property sources are in place for
694                // use in any post-processing or initialization that occurs below prior to #refresh
695                ConfigurableEnvironment env = wac.getEnvironment();
696                if (env instanceof ConfigurableWebEnvironment) {
697                        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
698                }
699
700                postProcessWebApplicationContext(wac);
701                applyInitializers(wac);
702                wac.refresh();
703        }
704
705        /**
706         * Instantiate the WebApplicationContext for this servlet, either a default
707         * {@link org.springframework.web.context.support.XmlWebApplicationContext}
708         * or a {@link #setContextClass custom context class}, if set.
709         * Delegates to #createWebApplicationContext(ApplicationContext).
710         * @param parent the parent WebApplicationContext to use, or {@code null} if none
711         * @return the WebApplicationContext for this servlet
712         * @see org.springframework.web.context.support.XmlWebApplicationContext
713         * @see #createWebApplicationContext(ApplicationContext)
714         */
715        protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
716                return createWebApplicationContext((ApplicationContext) parent);
717        }
718
719        /**
720         * Post-process the given WebApplicationContext before it is refreshed
721         * and activated as context for this servlet.
722         * <p>The default implementation is empty. {@code refresh()} will
723         * be called automatically after this method returns.
724         * <p>Note that this method is designed to allow subclasses to modify the application
725         * context, while {@link #initWebApplicationContext} is designed to allow
726         * end-users to modify the context through the use of
727         * {@link ApplicationContextInitializer ApplicationContextInitializers}.
728         * @param wac the configured WebApplicationContext (not refreshed yet)
729         * @see #createWebApplicationContext
730         * @see #initWebApplicationContext
731         * @see ConfigurableWebApplicationContext#refresh()
732         */
733        protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
734        }
735
736        /**
737         * Delegate the WebApplicationContext before it is refreshed to any
738         * {@link ApplicationContextInitializer} instances specified by the
739         * "contextInitializerClasses" servlet init-param.
740         * <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow
741         * subclasses (as opposed to end-users) to modify the application context, and is
742         * called immediately before this method.
743         * @param wac the configured WebApplicationContext (not refreshed yet)
744         * @see #createWebApplicationContext
745         * @see #postProcessWebApplicationContext
746         * @see ConfigurableApplicationContext#refresh()
747         */
748        protected void applyInitializers(ConfigurableApplicationContext wac) {
749                String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
750                if (globalClassNames != null) {
751                        for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
752                                this.contextInitializers.add(loadInitializer(className, wac));
753                        }
754                }
755
756                if (this.contextInitializerClasses != null) {
757                        for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
758                                this.contextInitializers.add(loadInitializer(className, wac));
759                        }
760                }
761
762                AnnotationAwareOrderComparator.sort(this.contextInitializers);
763                for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
764                        initializer.initialize(wac);
765                }
766        }
767
768        @SuppressWarnings("unchecked")
769        private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer(
770                        String className, ConfigurableApplicationContext wac) {
771                try {
772                        Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
773                        Class<?> initializerContextClass =
774                                        GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
775                        if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
776                                throw new ApplicationContextException(String.format(
777                                                "Could not apply context initializer [%s] since its generic parameter [%s] " +
778                                                "is not assignable from the type of application context used by this " +
779                                                "framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(),
780                                                wac.getClass().getName()));
781                        }
782                        return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
783                }
784                catch (ClassNotFoundException ex) {
785                        throw new ApplicationContextException(String.format("Could not load class [%s] specified " +
786                                        "via 'contextInitializerClasses' init-param", className), ex);
787                }
788        }
789
790        /**
791         * Return the ServletContext attribute name for this servlet's WebApplicationContext.
792         * <p>The default implementation returns
793         * {@code SERVLET_CONTEXT_PREFIX + servlet name}.
794         * @see #SERVLET_CONTEXT_PREFIX
795         * @see #getServletName
796         */
797        public String getServletContextAttributeName() {
798                return SERVLET_CONTEXT_PREFIX + getServletName();
799        }
800
801        /**
802         * Return this servlet's WebApplicationContext.
803         */
804        @Nullable
805        public final WebApplicationContext getWebApplicationContext() {
806                return this.webApplicationContext;
807        }
808
809
810        /**
811         * This method will be invoked after any bean properties have been set and
812         * the WebApplicationContext has been loaded. The default implementation is empty;
813         * subclasses may override this method to perform any initialization they require.
814         * @throws ServletException in case of an initialization exception
815         */
816        protected void initFrameworkServlet() throws ServletException {
817        }
818
819        /**
820         * Refresh this servlet's application context, as well as the
821         * dependent state of the servlet.
822         * @see #getWebApplicationContext()
823         * @see org.springframework.context.ConfigurableApplicationContext#refresh()
824         */
825        public void refresh() {
826                WebApplicationContext wac = getWebApplicationContext();
827                if (!(wac instanceof ConfigurableApplicationContext)) {
828                        throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
829                }
830                ((ConfigurableApplicationContext) wac).refresh();
831        }
832
833        /**
834         * Callback that receives refresh events from this servlet's WebApplicationContext.
835         * <p>The default implementation calls {@link #onRefresh},
836         * triggering a refresh of this servlet's context-dependent state.
837         * @param event the incoming ApplicationContext event
838         */
839        public void onApplicationEvent(ContextRefreshedEvent event) {
840                this.refreshEventReceived = true;
841                synchronized (this.onRefreshMonitor) {
842                        onRefresh(event.getApplicationContext());
843                }
844        }
845
846        /**
847         * Template method which can be overridden to add servlet-specific refresh work.
848         * Called after successful context refresh.
849         * <p>This implementation is empty.
850         * @param context the current WebApplicationContext
851         * @see #refresh()
852         */
853        protected void onRefresh(ApplicationContext context) {
854                // For subclasses: do nothing by default.
855        }
856
857        /**
858         * Close the WebApplicationContext of this servlet.
859         * @see org.springframework.context.ConfigurableApplicationContext#close()
860         */
861        @Override
862        public void destroy() {
863                getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
864                // Only call close() on WebApplicationContext if locally managed...
865                if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
866                        ((ConfigurableApplicationContext) this.webApplicationContext).close();
867                }
868        }
869
870
871        /**
872         * Override the parent class implementation in order to intercept PATCH requests.
873         */
874        @Override
875        protected void service(HttpServletRequest request, HttpServletResponse response)
876                        throws ServletException, IOException {
877
878                HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
879                if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
880                        processRequest(request, response);
881                }
882                else {
883                        super.service(request, response);
884                }
885        }
886
887        /**
888         * Delegate GET requests to processRequest/doService.
889         * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
890         * with a {@code NoBodyResponse} that just captures the content length.
891         * @see #doService
892         * @see #doHead
893         */
894        @Override
895        protected final void doGet(HttpServletRequest request, HttpServletResponse response)
896                        throws ServletException, IOException {
897
898                processRequest(request, response);
899        }
900
901        /**
902         * Delegate POST requests to {@link #processRequest}.
903         * @see #doService
904         */
905        @Override
906        protected final void doPost(HttpServletRequest request, HttpServletResponse response)
907                        throws ServletException, IOException {
908
909                processRequest(request, response);
910        }
911
912        /**
913         * Delegate PUT requests to {@link #processRequest}.
914         * @see #doService
915         */
916        @Override
917        protected final void doPut(HttpServletRequest request, HttpServletResponse response)
918                        throws ServletException, IOException {
919
920                processRequest(request, response);
921        }
922
923        /**
924         * Delegate DELETE requests to {@link #processRequest}.
925         * @see #doService
926         */
927        @Override
928        protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
929                        throws ServletException, IOException {
930
931                processRequest(request, response);
932        }
933
934        /**
935         * Delegate OPTIONS requests to {@link #processRequest}, if desired.
936         * <p>Applies HttpServlet's standard OPTIONS processing otherwise,
937         * and also if there is still no 'Allow' header set after dispatching.
938         * @see #doService
939         */
940        @Override
941        protected void doOptions(HttpServletRequest request, HttpServletResponse response)
942                        throws ServletException, IOException {
943
944                if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
945                        processRequest(request, response);
946                        if (response.containsHeader("Allow")) {
947                                // Proper OPTIONS response coming from a handler - we're done.
948                                return;
949                        }
950                }
951
952                // Use response wrapper in order to always add PATCH to the allowed methods
953                super.doOptions(request, new HttpServletResponseWrapper(response) {
954                        @Override
955                        public void setHeader(String name, String value) {
956                                if ("Allow".equals(name)) {
957                                        value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
958                                }
959                                super.setHeader(name, value);
960                        }
961                });
962        }
963
964        /**
965         * Delegate TRACE requests to {@link #processRequest}, if desired.
966         * <p>Applies HttpServlet's standard TRACE processing otherwise.
967         * @see #doService
968         */
969        @Override
970        protected void doTrace(HttpServletRequest request, HttpServletResponse response)
971                        throws ServletException, IOException {
972
973                if (this.dispatchTraceRequest) {
974                        processRequest(request, response);
975                        if ("message/http".equals(response.getContentType())) {
976                                // Proper TRACE response coming from a handler - we're done.
977                                return;
978                        }
979                }
980                super.doTrace(request, response);
981        }
982
983        /**
984         * Process this request, publishing an event regardless of the outcome.
985         * <p>The actual event handling is performed by the abstract
986         * {@link #doService} template method.
987         */
988        protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
989                        throws ServletException, IOException {
990
991                long startTime = System.currentTimeMillis();
992                Throwable failureCause = null;
993
994                LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
995                LocaleContext localeContext = buildLocaleContext(request);
996
997                RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
998                ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
999
1000                WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
1001                asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
1002
1003                initContextHolders(request, localeContext, requestAttributes);
1004
1005                try {
1006                        doService(request, response);
1007                }
1008                catch (ServletException | IOException ex) {
1009                        failureCause = ex;
1010                        throw ex;
1011                }
1012                catch (Throwable ex) {
1013                        failureCause = ex;
1014                        throw new NestedServletException("Request processing failed", ex);
1015                }
1016
1017                finally {
1018                        resetContextHolders(request, previousLocaleContext, previousAttributes);
1019                        if (requestAttributes != null) {
1020                                requestAttributes.requestCompleted();
1021                        }
1022                        logResult(request, response, failureCause, asyncManager);
1023                        publishRequestHandledEvent(request, response, startTime, failureCause);
1024                }
1025        }
1026
1027        /**
1028         * Build a LocaleContext for the given request, exposing the request's
1029         * primary locale as current locale.
1030         * @param request current HTTP request
1031         * @return the corresponding LocaleContext, or {@code null} if none to bind
1032         * @see LocaleContextHolder#setLocaleContext
1033         */
1034        @Nullable
1035        protected LocaleContext buildLocaleContext(HttpServletRequest request) {
1036                return new SimpleLocaleContext(request.getLocale());
1037        }
1038
1039        /**
1040         * Build ServletRequestAttributes for the given request (potentially also
1041         * holding a reference to the response), taking pre-bound attributes
1042         * (and their type) into consideration.
1043         * @param request current HTTP request
1044         * @param response current HTTP response
1045         * @param previousAttributes pre-bound RequestAttributes instance, if any
1046         * @return the ServletRequestAttributes to bind, or {@code null} to preserve
1047         * the previously bound instance (or not binding any, if none bound before)
1048         * @see RequestContextHolder#setRequestAttributes
1049         */
1050        @Nullable
1051        protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
1052                        @Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {
1053
1054                if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
1055                        return new ServletRequestAttributes(request, response);
1056                }
1057                else {
1058                        return null;  // preserve the pre-bound RequestAttributes instance
1059                }
1060        }
1061
1062        private void initContextHolders(HttpServletRequest request,
1063                        @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
1064
1065                if (localeContext != null) {
1066                        LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
1067                }
1068                if (requestAttributes != null) {
1069                        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
1070                }
1071        }
1072
1073        private void resetContextHolders(HttpServletRequest request,
1074                        @Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) {
1075
1076                LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
1077                RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
1078        }
1079
1080        private void logResult(HttpServletRequest request, HttpServletResponse response,
1081                        @Nullable Throwable failureCause, WebAsyncManager asyncManager) {
1082
1083                if (!logger.isDebugEnabled()) {
1084                        return;
1085                }
1086
1087                String dispatchType = request.getDispatcherType().name();
1088                boolean initialDispatch = request.getDispatcherType().equals(DispatcherType.REQUEST);
1089
1090                if (failureCause != null) {
1091                        if (!initialDispatch) {
1092                                // FORWARD/ERROR/ASYNC: minimal message (there should be enough context already)
1093                                if (logger.isDebugEnabled()) {
1094                                        logger.debug("Unresolved failure from \"" + dispatchType + "\" dispatch: " + failureCause);
1095                                }
1096                        }
1097                        else if (logger.isTraceEnabled()) {
1098                                logger.trace("Failed to complete request", failureCause);
1099                        }
1100                        else {
1101                                logger.debug("Failed to complete request: " + failureCause);
1102                        }
1103                        return;
1104                }
1105
1106                if (asyncManager.isConcurrentHandlingStarted()) {
1107                        logger.debug("Exiting but response remains open for further handling");
1108                        return;
1109                }
1110
1111                int status = response.getStatus();
1112                String headers = ""; // nothing below trace
1113
1114                if (logger.isTraceEnabled()) {
1115                        Collection<String> names = response.getHeaderNames();
1116                        if (this.enableLoggingRequestDetails) {
1117                                headers = names.stream().map(name -> name + ":" + response.getHeaders(name))
1118                                                .collect(Collectors.joining(", "));
1119                        }
1120                        else {
1121                                headers = names.isEmpty() ? "" : "masked";
1122                        }
1123                        headers = ", headers={" + headers + "}";
1124                }
1125
1126                if (!initialDispatch) {
1127                        logger.debug("Exiting from \"" + dispatchType + "\" dispatch, status " + status + headers);
1128                }
1129                else {
1130                        HttpStatus httpStatus = HttpStatus.resolve(status);
1131                        logger.debug("Completed " + (httpStatus != null ? httpStatus : status) + headers);
1132                }
1133        }
1134
1135        private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
1136                        long startTime, @Nullable Throwable failureCause) {
1137
1138                if (this.publishEvents && this.webApplicationContext != null) {
1139                        // Whether or not we succeeded, publish an event.
1140                        long processingTime = System.currentTimeMillis() - startTime;
1141                        this.webApplicationContext.publishEvent(
1142                                        new ServletRequestHandledEvent(this,
1143                                                        request.getRequestURI(), request.getRemoteAddr(),
1144                                                        request.getMethod(), getServletConfig().getServletName(),
1145                                                        WebUtils.getSessionId(request), getUsernameForRequest(request),
1146                                                        processingTime, failureCause, response.getStatus()));
1147                }
1148        }
1149
1150        /**
1151         * Determine the username for the given request.
1152         * <p>The default implementation takes the name of the UserPrincipal, if any.
1153         * Can be overridden in subclasses.
1154         * @param request current HTTP request
1155         * @return the username, or {@code null} if none found
1156         * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
1157         */
1158        @Nullable
1159        protected String getUsernameForRequest(HttpServletRequest request) {
1160                Principal userPrincipal = request.getUserPrincipal();
1161                return (userPrincipal != null ? userPrincipal.getName() : null);
1162        }
1163
1164
1165        /**
1166         * Subclasses must implement this method to do the work of request handling,
1167         * receiving a centralized callback for GET, POST, PUT and DELETE.
1168         * <p>The contract is essentially the same as that for the commonly overridden
1169         * {@code doGet} or {@code doPost} methods of HttpServlet.
1170         * <p>This class intercepts calls to ensure that exception handling and
1171         * event publication takes place.
1172         * @param request current HTTP request
1173         * @param response current HTTP response
1174         * @throws Exception in case of any kind of processing failure
1175         * @see javax.servlet.http.HttpServlet#doGet
1176         * @see javax.servlet.http.HttpServlet#doPost
1177         */
1178        protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
1179                        throws Exception;
1180
1181
1182        /**
1183         * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
1184         * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
1185         */
1186        private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
1187
1188                @Override
1189                public void onApplicationEvent(ContextRefreshedEvent event) {
1190                        FrameworkServlet.this.onApplicationEvent(event);
1191                }
1192        }
1193
1194
1195        /**
1196         * CallableProcessingInterceptor implementation that initializes and resets
1197         * FrameworkServlet's context holders, i.e. LocaleContextHolder and RequestContextHolder.
1198         */
1199        private class RequestBindingInterceptor implements CallableProcessingInterceptor {
1200
1201                @Override
1202                public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
1203                        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
1204                        if (request != null) {
1205                                HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
1206                                initContextHolders(request, buildLocaleContext(request),
1207                                                buildRequestAttributes(request, response, null));
1208                        }
1209                }
1210                @Override
1211                public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) {
1212                        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
1213                        if (request != null) {
1214                                resetContextHolders(request, null, null);
1215                        }
1216                }
1217        }
1218
1219}