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