001/*
002 * Copyright 2002-2017 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.context;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.Properties;
024import java.util.concurrent.ConcurrentHashMap;
025import javax.servlet.ServletContext;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030import org.springframework.beans.BeanUtils;
031import org.springframework.beans.factory.access.BeanFactoryLocator;
032import org.springframework.beans.factory.access.BeanFactoryReference;
033import org.springframework.context.ApplicationContext;
034import org.springframework.context.ApplicationContextException;
035import org.springframework.context.ApplicationContextInitializer;
036import org.springframework.context.ConfigurableApplicationContext;
037import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
038import org.springframework.core.GenericTypeResolver;
039import org.springframework.core.annotation.AnnotationAwareOrderComparator;
040import org.springframework.core.env.ConfigurableEnvironment;
041import org.springframework.core.io.ClassPathResource;
042import org.springframework.core.io.support.PropertiesLoaderUtils;
043import org.springframework.util.ClassUtils;
044import org.springframework.util.ObjectUtils;
045import org.springframework.util.StringUtils;
046
047/**
048 * Performs the actual initialization work for the root application context.
049 * Called by {@link ContextLoaderListener}.
050 *
051 * <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the
052 * {@code web.xml} context-param level to specify the context class type, falling
053 * back to {@link org.springframework.web.context.support.XmlWebApplicationContext}
054 * if not found. With the default ContextLoader implementation, any context class
055 * specified needs to implement the {@link ConfigurableWebApplicationContext} interface.
056 *
057 * <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param
058 * and passes its value to the context instance, parsing it into potentially multiple
059 * file paths which can be separated by any number of commas and spaces, e.g.
060 * "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml".
061 * Ant-style path patterns are supported as well, e.g.
062 * "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
063 * If not explicitly specified, the context implementation is supposed to use a
064 * default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
065 *
066 * <p>Note: In case of multiple config locations, later bean definitions will
067 * override ones defined in previously loaded files, at least when using one of
068 * Spring's default ApplicationContext implementations. This can be leveraged
069 * to deliberately override certain bean definitions via an extra XML file.
070 *
071 * <p>Above and beyond loading the root application context, this class can optionally
072 * load or obtain and hook up a shared parent context to the root application context.
073 * See the {@link #loadParentContext(ServletContext)} method for more information.
074 *
075 * <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
076 * application context via the {@link #ContextLoader(WebApplicationContext)}
077 * constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
078 * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
079 *
080 * @author Juergen Hoeller
081 * @author Colin Sampaleanu
082 * @author Sam Brannen
083 * @since 17.02.2003
084 * @see ContextLoaderListener
085 * @see ConfigurableWebApplicationContext
086 * @see org.springframework.web.context.support.XmlWebApplicationContext
087 */
088public class ContextLoader {
089
090        /**
091         * Config param for the root WebApplicationContext id,
092         * to be used as serialization id for the underlying BeanFactory: {@value}
093         */
094        public static final String CONTEXT_ID_PARAM = "contextId";
095
096        /**
097         * Name of servlet context parameter (i.e., {@value}) that can specify the
098         * config location for the root context, falling back to the implementation's
099         * default otherwise.
100         * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
101         */
102        public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
103
104        /**
105         * Config param for the root WebApplicationContext implementation class to use: {@value}
106         * @see #determineContextClass(ServletContext)
107         */
108        public static final String CONTEXT_CLASS_PARAM = "contextClass";
109
110        /**
111         * Config param for {@link ApplicationContextInitializer} classes to use
112         * for initializing the root web application context: {@value}
113         * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
114         */
115        public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
116
117        /**
118         * Config param for global {@link ApplicationContextInitializer} classes to use
119         * for initializing all web application contexts in the current application: {@value}
120         * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
121         */
122        public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
123
124        /**
125         * Optional servlet context parameter (i.e., "{@code locatorFactorySelector}")
126         * used only when obtaining a parent context using the default implementation
127         * of {@link #loadParentContext(ServletContext servletContext)}.
128         * Specifies the 'selector' used in the
129         * {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)}
130         * method call, which is used to obtain the BeanFactoryLocator instance from
131         * which the parent context is obtained.
132         * <p>The default is {@code classpath*:beanRefContext.xml},
133         * matching the default applied for the
134         * {@link ContextSingletonBeanFactoryLocator#getInstance()} method.
135         * Supplying the "parentContextKey" parameter is sufficient in this case.
136         */
137        public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
138
139        /**
140         * Optional servlet context parameter (i.e., "{@code parentContextKey}")
141         * used only when obtaining a parent context using the default implementation
142         * of {@link #loadParentContext(ServletContext servletContext)}.
143         * Specifies the 'factoryKey' used in the
144         * {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call,
145         * obtaining the parent application context from the BeanFactoryLocator instance.
146         * <p>Supplying this "parentContextKey" parameter is sufficient when relying
147         * on the default {@code classpath*:beanRefContext.xml} selector for
148         * candidate factory references.
149         */
150        public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
151
152        /**
153         * Any number of these characters are considered delimiters between
154         * multiple values in a single init-param String value.
155         */
156        private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
157
158        /**
159         * Name of the class path resource (relative to the ContextLoader class)
160         * that defines ContextLoader's default strategy names.
161         */
162        private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
163
164
165        private static final Properties defaultStrategies;
166
167        static {
168                // Load default strategy implementations from properties file.
169                // This is currently strictly internal and not meant to be customized
170                // by application developers.
171                try {
172                        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
173                        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
174                }
175                catch (IOException ex) {
176                        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
177                }
178        }
179
180
181        /**
182         * Map from (thread context) ClassLoader to corresponding 'current' WebApplicationContext.
183         */
184        private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =
185                        new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);
186
187        /**
188         * The 'current' WebApplicationContext, if the ContextLoader class is
189         * deployed in the web app ClassLoader itself.
190         */
191        private static volatile WebApplicationContext currentContext;
192
193
194        /**
195         * The root WebApplicationContext instance that this loader manages.
196         */
197        private WebApplicationContext context;
198
199        /**
200         * Holds BeanFactoryReference when loading parent factory via
201         * ContextSingletonBeanFactoryLocator.
202         */
203        private BeanFactoryReference parentContextRef;
204
205        /** Actual ApplicationContextInitializer instances to apply to the context */
206        private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
207                        new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
208
209
210        /**
211         * Create a new {@code ContextLoader} that will create a web application context
212         * based on the "contextClass" and "contextConfigLocation" servlet context-params.
213         * See class-level documentation for details on default values for each.
214         * <p>This constructor is typically used when declaring the {@code
215         * ContextLoaderListener} subclass as a {@code <listener>} within {@code web.xml}, as
216         * a no-arg constructor is required.
217         * <p>The created application context will be registered into the ServletContext under
218         * the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
219         * and subclasses are free to call the {@link #closeWebApplicationContext} method on
220         * container shutdown to close the application context.
221         * @see #ContextLoader(WebApplicationContext)
222         * @see #initWebApplicationContext(ServletContext)
223         * @see #closeWebApplicationContext(ServletContext)
224         */
225        public ContextLoader() {
226        }
227
228        /**
229         * Create a new {@code ContextLoader} with the given application context. This
230         * constructor is useful in Servlet 3.0+ environments where instance-based
231         * registration of listeners is possible through the {@link ServletContext#addListener}
232         * API.
233         * <p>The context may or may not yet be {@linkplain
234         * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
235         * of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong>
236         * already been refreshed (the recommended approach), then the following will occur:
237         * <ul>
238         * <li>If the given context has not already been assigned an {@linkplain
239         * ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
240         * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
241         * the application context</li>
242         * <li>{@link #customizeContext} will be called</li>
243         * <li>Any {@link ApplicationContextInitializer}s specified through the
244         * "contextInitializerClasses" init-param will be applied.</li>
245         * <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li>
246         * </ul>
247         * If the context has already been refreshed or does not implement
248         * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
249         * assumption that the user has performed these actions (or not) per his or her
250         * specific needs.
251         * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
252         * <p>In any case, the given application context will be registered into the
253         * ServletContext under the attribute name {@link
254         * WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and subclasses are
255         * free to call the {@link #closeWebApplicationContext} method on container shutdown
256         * to close the application context.
257         * @param context the application context to manage
258         * @see #initWebApplicationContext(ServletContext)
259         * @see #closeWebApplicationContext(ServletContext)
260         */
261        public ContextLoader(WebApplicationContext context) {
262                this.context = context;
263        }
264
265
266        /**
267         * Specify which {@link ApplicationContextInitializer} instances should be used
268         * to initialize the application context used by this {@code ContextLoader}.
269         * @since 4.2
270         * @see #configureAndRefreshWebApplicationContext
271         * @see #customizeContext
272         */
273        @SuppressWarnings("unchecked")
274        public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {
275                if (initializers != null) {
276                        for (ApplicationContextInitializer<?> initializer : initializers) {
277                                this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
278                        }
279                }
280        }
281
282
283        /**
284         * Initialize Spring's web application context for the given servlet context,
285         * using the application context provided at construction time, or creating a new one
286         * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
287         * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
288         * @param servletContext current servlet context
289         * @return the new WebApplicationContext
290         * @see #ContextLoader(WebApplicationContext)
291         * @see #CONTEXT_CLASS_PARAM
292         * @see #CONFIG_LOCATION_PARAM
293         */
294        public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
295                if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
296                        throw new IllegalStateException(
297                                        "Cannot initialize context because there is already a root application context present - " +
298                                        "check whether you have multiple ContextLoader* definitions in your web.xml!");
299                }
300
301                Log logger = LogFactory.getLog(ContextLoader.class);
302                servletContext.log("Initializing Spring root WebApplicationContext");
303                if (logger.isInfoEnabled()) {
304                        logger.info("Root WebApplicationContext: initialization started");
305                }
306                long startTime = System.currentTimeMillis();
307
308                try {
309                        // Store context in local instance variable, to guarantee that
310                        // it is available on ServletContext shutdown.
311                        if (this.context == null) {
312                                this.context = createWebApplicationContext(servletContext);
313                        }
314                        if (this.context instanceof ConfigurableWebApplicationContext) {
315                                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
316                                if (!cwac.isActive()) {
317                                        // The context has not yet been refreshed -> provide services such as
318                                        // setting the parent context, setting the application context id, etc
319                                        if (cwac.getParent() == null) {
320                                                // The context instance was injected without an explicit parent ->
321                                                // determine parent for root web application context, if any.
322                                                ApplicationContext parent = loadParentContext(servletContext);
323                                                cwac.setParent(parent);
324                                        }
325                                        configureAndRefreshWebApplicationContext(cwac, servletContext);
326                                }
327                        }
328                        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
329
330                        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
331                        if (ccl == ContextLoader.class.getClassLoader()) {
332                                currentContext = this.context;
333                        }
334                        else if (ccl != null) {
335                                currentContextPerThread.put(ccl, this.context);
336                        }
337
338                        if (logger.isDebugEnabled()) {
339                                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
340                                                WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
341                        }
342                        if (logger.isInfoEnabled()) {
343                                long elapsedTime = System.currentTimeMillis() - startTime;
344                                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
345                        }
346
347                        return this.context;
348                }
349                catch (RuntimeException ex) {
350                        logger.error("Context initialization failed", ex);
351                        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
352                        throw ex;
353                }
354                catch (Error err) {
355                        logger.error("Context initialization failed", err);
356                        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
357                        throw err;
358                }
359        }
360
361        /**
362         * Instantiate the root WebApplicationContext for this loader, either the
363         * default context class or a custom context class if specified.
364         * <p>This implementation expects custom contexts to implement the
365         * {@link ConfigurableWebApplicationContext} interface.
366         * Can be overridden in subclasses.
367         * <p>In addition, {@link #customizeContext} gets called prior to refreshing the
368         * context, allowing subclasses to perform custom modifications to the context.
369         * @param sc current servlet context
370         * @return the root WebApplicationContext
371         * @see ConfigurableWebApplicationContext
372         */
373        protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
374                Class<?> contextClass = determineContextClass(sc);
375                if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
376                        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
377                                        "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
378                }
379                return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
380        }
381
382        /**
383         * Return the WebApplicationContext implementation class to use, either the
384         * default XmlWebApplicationContext or a custom context class if specified.
385         * @param servletContext current servlet context
386         * @return the WebApplicationContext implementation class to use
387         * @see #CONTEXT_CLASS_PARAM
388         * @see org.springframework.web.context.support.XmlWebApplicationContext
389         */
390        protected Class<?> determineContextClass(ServletContext servletContext) {
391                String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
392                if (contextClassName != null) {
393                        try {
394                                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
395                        }
396                        catch (ClassNotFoundException ex) {
397                                throw new ApplicationContextException(
398                                                "Failed to load custom context class [" + contextClassName + "]", ex);
399                        }
400                }
401                else {
402                        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
403                        try {
404                                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
405                        }
406                        catch (ClassNotFoundException ex) {
407                                throw new ApplicationContextException(
408                                                "Failed to load default context class [" + contextClassName + "]", ex);
409                        }
410                }
411        }
412
413        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
414                if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
415                        // The application context id is still set to its original default value
416                        // -> assign a more useful id based on available information
417                        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
418                        if (idParam != null) {
419                                wac.setId(idParam);
420                        }
421                        else {
422                                // Generate default id...
423                                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
424                                                ObjectUtils.getDisplayString(sc.getContextPath()));
425                        }
426                }
427
428                wac.setServletContext(sc);
429                String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
430                if (configLocationParam != null) {
431                        wac.setConfigLocation(configLocationParam);
432                }
433
434                // The wac environment's #initPropertySources will be called in any case when the context
435                // is refreshed; do it eagerly here to ensure servlet property sources are in place for
436                // use in any post-processing or initialization that occurs below prior to #refresh
437                ConfigurableEnvironment env = wac.getEnvironment();
438                if (env instanceof ConfigurableWebEnvironment) {
439                        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
440                }
441
442                customizeContext(sc, wac);
443                wac.refresh();
444        }
445
446        /**
447         * Customize the {@link ConfigurableWebApplicationContext} created by this
448         * ContextLoader after config locations have been supplied to the context
449         * but before the context is <em>refreshed</em>.
450         * <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
451         * determines} what (if any) context initializer classes have been specified through
452         * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
453         * {@linkplain ApplicationContextInitializer#initialize invokes each} with the
454         * given web application context.
455         * <p>Any {@code ApplicationContextInitializers} implementing
456         * {@link org.springframework.core.Ordered Ordered} or marked with @{@link
457         * org.springframework.core.annotation.Order Order} will be sorted appropriately.
458         * @param sc the current servlet context
459         * @param wac the newly created application context
460         * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
461         * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
462         */
463        protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
464                List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
465                                determineContextInitializerClasses(sc);
466
467                for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
468                        Class<?> initializerContextClass =
469                                        GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
470                        if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
471                                throw new ApplicationContextException(String.format(
472                                                "Could not apply context initializer [%s] since its generic parameter [%s] " +
473                                                "is not assignable from the type of application context used by this " +
474                                                "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
475                                                wac.getClass().getName()));
476                        }
477                        this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
478                }
479
480                AnnotationAwareOrderComparator.sort(this.contextInitializers);
481                for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
482                        initializer.initialize(wac);
483                }
484        }
485
486        /**
487         * Return the {@link ApplicationContextInitializer} implementation classes to use
488         * if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}.
489         * @param servletContext current servlet context
490         * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
491         */
492        protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
493                        determineContextInitializerClasses(ServletContext servletContext) {
494
495                List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
496                                new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
497
498                String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
499                if (globalClassNames != null) {
500                        for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
501                                classes.add(loadInitializerClass(className));
502                        }
503                }
504
505                String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
506                if (localClassNames != null) {
507                        for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
508                                classes.add(loadInitializerClass(className));
509                        }
510                }
511
512                return classes;
513        }
514
515        @SuppressWarnings("unchecked")
516        private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
517                try {
518                        Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
519                        if (!ApplicationContextInitializer.class.isAssignableFrom(clazz)) {
520                                throw new ApplicationContextException(
521                                                "Initializer class does not implement ApplicationContextInitializer interface: " + clazz);
522                        }
523                        return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
524                }
525                catch (ClassNotFoundException ex) {
526                        throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
527                }
528        }
529
530        /**
531         * Template method with default implementation (which may be overridden by a
532         * subclass), to load or obtain an ApplicationContext instance which will be
533         * used as the parent context of the root WebApplicationContext. If the
534         * return value from the method is null, no parent context is set.
535         * <p>The main reason to load a parent context here is to allow multiple root
536         * web application contexts to all be children of a shared EAR context, or
537         * alternately to also share the same parent context that is visible to
538         * EJBs. For pure web applications, there is usually no need to worry about
539         * having a parent context to the root web application context.
540         * <p>The default implementation uses
541         * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
542         * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
543         * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
544         * which will be shared by all other users of ContextsingletonBeanFactoryLocator
545         * which also use the same configuration parameters.
546         * @param servletContext current servlet context
547         * @return the parent application context, or {@code null} if none
548         * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
549         */
550        protected ApplicationContext loadParentContext(ServletContext servletContext) {
551                ApplicationContext parentContext = null;
552                String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
553                String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
554
555                if (parentContextKey != null) {
556                        // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
557                        BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
558                        Log logger = LogFactory.getLog(ContextLoader.class);
559                        if (logger.isDebugEnabled()) {
560                                logger.debug("Getting parent context definition: using parent context key of '" +
561                                                parentContextKey + "' with BeanFactoryLocator");
562                        }
563                        this.parentContextRef = locator.useBeanFactory(parentContextKey);
564                        parentContext = (ApplicationContext) this.parentContextRef.getFactory();
565                }
566
567                return parentContext;
568        }
569
570        /**
571         * Close Spring's web application context for the given servlet context. If
572         * the default {@link #loadParentContext(ServletContext)} implementation,
573         * which uses ContextSingletonBeanFactoryLocator, has loaded any shared
574         * parent context, release one reference to that shared parent context.
575         * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have
576         * to override this method as well.
577         * @param servletContext the ServletContext that the WebApplicationContext runs in
578         */
579        public void closeWebApplicationContext(ServletContext servletContext) {
580                servletContext.log("Closing Spring root WebApplicationContext");
581                try {
582                        if (this.context instanceof ConfigurableWebApplicationContext) {
583                                ((ConfigurableWebApplicationContext) this.context).close();
584                        }
585                }
586                finally {
587                        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
588                        if (ccl == ContextLoader.class.getClassLoader()) {
589                                currentContext = null;
590                        }
591                        else if (ccl != null) {
592                                currentContextPerThread.remove(ccl);
593                        }
594                        servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
595                        if (this.parentContextRef != null) {
596                                this.parentContextRef.release();
597                        }
598                }
599        }
600
601
602        /**
603         * Obtain the Spring root web application context for the current thread
604         * (i.e. for the current thread's context ClassLoader, which needs to be
605         * the web application's ClassLoader).
606         * @return the current root web application context, or {@code null}
607         * if none found
608         * @see org.springframework.web.context.support.SpringBeanAutowiringSupport
609         */
610        public static WebApplicationContext getCurrentWebApplicationContext() {
611                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
612                if (ccl != null) {
613                        WebApplicationContext ccpt = currentContextPerThread.get(ccl);
614                        if (ccpt != null) {
615                                return ccpt;
616                        }
617                }
618                return currentContext;
619        }
620
621}