Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » web » context » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.web.context;
   18   
   19   import java.io.IOException;
   20   import java.util.Map;
   21   import java.util.Properties;
   22   
   23   import javax.servlet.ServletContext;
   24   
   25   import org.apache.commons.logging.Log;
   26   import org.apache.commons.logging.LogFactory;
   27   
   28   import org.springframework.beans.BeanUtils;
   29   import org.springframework.beans.BeansException;
   30   import org.springframework.beans.factory.access.BeanFactoryLocator;
   31   import org.springframework.beans.factory.access.BeanFactoryReference;
   32   import org.springframework.context.ApplicationContext;
   33   import org.springframework.context.ApplicationContextException;
   34   import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
   35   import org.springframework.core.CollectionFactory;
   36   import org.springframework.core.io.ClassPathResource;
   37   import org.springframework.core.io.support.PropertiesLoaderUtils;
   38   import org.springframework.util.ClassUtils;
   39   
   40   /**
   41    * Performs the actual initialization work for the root application context.
   42    * Called by {@link ContextLoaderListener} and {@link ContextLoaderServlet}.
   43    *
   44    * <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter
   45    * at the <code>web.xml</code> context-param level to specify the context
   46    * class type, falling back to the default of
   47    * {@link org.springframework.web.context.support.XmlWebApplicationContext}
   48    * if not found. With the default ContextLoader implementation, any context class
   49    * specified needs to implement the ConfigurableWebApplicationContext interface.
   50    *
   51    * <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"}
   52    * context-param and passes its value to the context instance, parsing it into
   53    * potentially multiple file paths which can be separated by any number of
   54    * commas and spaces, e.g. "WEB-INF/applicationContext1.xml,
   55    * WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well,
   56    * e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
   57    * If not explicitly specified, the context implementation is supposed to use a
   58    * default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
   59    *
   60    * <p>Note: In case of multiple config locations, later bean definitions will
   61    * override ones defined in previously loaded files, at least when using one of
   62    * Spring's default ApplicationContext implementations. This can be leveraged
   63    * to deliberately override certain bean definitions via an extra XML file.
   64    *
   65    * <p>Above and beyond loading the root application context, this class
   66    * can optionally load or obtain and hook up a shared parent context to
   67    * the root application context. See the
   68    * {@link #loadParentContext(ServletContext)} method for more information.
   69    *
   70    * @author Juergen Hoeller
   71    * @author Colin Sampaleanu
   72    * @author Sam Brannen
   73    * @since 17.02.2003
   74    * @see ContextLoaderListener
   75    * @see ContextLoaderServlet
   76    * @see ConfigurableWebApplicationContext
   77    * @see org.springframework.web.context.support.XmlWebApplicationContext
   78    */
   79   public class ContextLoader {
   80   
   81   	/**
   82   	 * Config param for the root WebApplicationContext implementation class to
   83   	 * use: "<code>contextClass</code>"
   84   	 */
   85   	public static final String CONTEXT_CLASS_PARAM = "contextClass";
   86   
   87   	/**
   88   	 * Name of servlet context parameter (i.e., "<code>contextConfigLocation</code>")
   89   	 * that can specify the config location for the root context, falling back
   90   	 * to the implementation's default otherwise.
   91   	 * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
   92   	 */
   93   	public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
   94   
   95   	/**
   96   	 * Optional servlet context parameter (i.e., "<code>locatorFactorySelector</code>")
   97   	 * used only when obtaining a parent context using the default implementation
   98   	 * of {@link #loadParentContext(ServletContext servletContext)}.
   99   	 * Specifies the 'selector' used in the
  100   	 * {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)}
  101   	 * method call, which is used to obtain the BeanFactoryLocator instance from
  102   	 * which the parent context is obtained.
  103   	 * <p>The default is <code>classpath*:beanRefContext.xml</code>,
  104   	 * matching the default applied for the
  105   	 * {@link ContextSingletonBeanFactoryLocator#getInstance()} method.
  106   	 * Supplying the "parentContextKey" parameter is sufficient in this case.
  107   	 */
  108   	public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
  109   
  110   	/**
  111   	 * Optional servlet context parameter (i.e., "<code>parentContextKey</code>")
  112   	 * used only when obtaining a parent context using the default implementation
  113   	 * of {@link #loadParentContext(ServletContext servletContext)}.
  114   	 * Specifies the 'factoryKey' used in the
  115   	 * {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call,
  116   	 * obtaining the parent application context from the BeanFactoryLocator instance.
  117   	 * <p>Supplying this "parentContextKey" parameter is sufficient when relying
  118   	 * on the default <code>classpath*:beanRefContext.xml</code> selector for
  119   	 * candidate factory references.
  120   	 */
  121   	public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
  122   
  123   	/**
  124   	 * Name of the class path resource (relative to the ContextLoader class)
  125   	 * that defines ContextLoader's default strategy names.
  126   	 */
  127   	private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
  128   
  129   
  130   	private static final Properties defaultStrategies;
  131   
  132   	static {
  133   		// Load default strategy implementations from properties file.
  134   		// This is currently strictly internal and not meant to be customized
  135   		// by application developers.
  136   		try {
  137   			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
  138   			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
  139   		}
  140   		catch (IOException ex) {
  141   			throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
  142   		}
  143   	}
  144   
  145   
  146   	private static final Log logger = LogFactory.getLog(ContextLoader.class);
  147   
  148   	/**
  149   	 * Map from (thread context) ClassLoader to WebApplicationContext.
  150   	 * Often just holding one reference - if the ContextLoader class is
  151   	 * deployed in the web app ClassLoader itself!
  152   	 */
  153   	private static final Map currentContextPerThread = CollectionFactory.createConcurrentMapIfPossible(1);
  154   
  155   	/**
  156   	 * The root WebApplicationContext instance that this loader manages.
  157   	 */
  158   	private WebApplicationContext context;
  159   
  160   	/**
  161   	 * Holds BeanFactoryReference when loading parent factory via
  162   	 * ContextSingletonBeanFactoryLocator.
  163   	 */
  164   	private BeanFactoryReference parentContextRef;
  165   
  166   
  167   	/**
  168   	 * Initialize Spring's web application context for the given servlet context,
  169   	 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
  170   	 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
  171   	 * @param servletContext current servlet context
  172   	 * @return the new WebApplicationContext
  173   	 * @throws IllegalStateException if there is already a root application context present
  174   	 * @throws BeansException if the context failed to initialize
  175   	 * @see #CONTEXT_CLASS_PARAM
  176   	 * @see #CONFIG_LOCATION_PARAM
  177   	 */
  178   	public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
  179   			throws IllegalStateException, BeansException {
  180   
  181   		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  182   			throw new IllegalStateException(
  183   					"Cannot initialize context because there is already a root application context present - " +
  184   					"check whether you have multiple ContextLoader* definitions in your web.xml!");
  185   		}
  186   
  187   		servletContext.log("Initializing Spring root WebApplicationContext");
  188   		if (logger.isInfoEnabled()) {
  189   			logger.info("Root WebApplicationContext: initialization started");
  190   		}
  191   		long startTime = System.currentTimeMillis();
  192   
  193   		try {
  194   			// Determine parent for root web application context, if any.
  195   			ApplicationContext parent = loadParentContext(servletContext);
  196   
  197   			// Store context in local instance variable, to guarantee that
  198   			// it is available on ServletContext shutdown.
  199   			this.context = createWebApplicationContext(servletContext, parent);
  200   			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  201   			currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
  202   
  203   			if (logger.isDebugEnabled()) {
  204   				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
  205   						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  206   			}
  207   			if (logger.isInfoEnabled()) {
  208   				long elapsedTime = System.currentTimeMillis() - startTime;
  209   				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  210   			}
  211   
  212   			return this.context;
  213   		}
  214   		catch (RuntimeException ex) {
  215   			logger.error("Context initialization failed", ex);
  216   			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  217   			throw ex;
  218   		}
  219   		catch (Error err) {
  220   			logger.error("Context initialization failed", err);
  221   			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  222   			throw err;
  223   		}
  224   	}
  225   
  226   	/**
  227   	 * Instantiate the root WebApplicationContext for this loader, either the
  228   	 * default context class or a custom context class if specified.
  229   	 * <p>This implementation expects custom contexts to implement the
  230   	 * {@link ConfigurableWebApplicationContext} interface.
  231   	 * Can be overridden in subclasses.
  232   	 * <p>In addition, {@link #customizeContext} gets called prior to refreshing the
  233   	 * context, allowing subclasses to perform custom modifications to the context.
  234   	 * @param servletContext current servlet context
  235   	 * @param parent the parent ApplicationContext to use, or <code>null</code> if none
  236   	 * @return the root WebApplicationContext
  237   	 * @throws BeansException if the context couldn't be initialized
  238   	 * @see ConfigurableWebApplicationContext
  239   	 */
  240   	protected WebApplicationContext createWebApplicationContext(
  241   			ServletContext servletContext, ApplicationContext parent) throws BeansException {
  242   
  243   		Class contextClass = determineContextClass(servletContext);
  244   		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  245   			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
  246   					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  247   		}
  248   
  249   		ConfigurableWebApplicationContext wac =
  250   				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  251   		wac.setParent(parent);
  252   		wac.setServletContext(servletContext);
  253   		wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
  254   		customizeContext(servletContext, wac);
  255   		wac.refresh();
  256   
  257   		return wac;
  258   	}
  259   
  260   	/**
  261   	 * Return the WebApplicationContext implementation class to use, either the
  262   	 * default XmlWebApplicationContext or a custom context class if specified.
  263   	 * @param servletContext current servlet context
  264   	 * @return the WebApplicationContext implementation class to use
  265   	 * @throws ApplicationContextException if the context class couldn't be loaded
  266   	 * @see #CONTEXT_CLASS_PARAM
  267   	 * @see org.springframework.web.context.support.XmlWebApplicationContext
  268   	 */
  269   	protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
  270   		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
  271   		if (contextClassName != null) {
  272   			try {
  273   				return ClassUtils.forName(contextClassName);
  274   			}
  275   			catch (ClassNotFoundException ex) {
  276   				throw new ApplicationContextException(
  277   						"Failed to load custom context class [" + contextClassName + "]", ex);
  278   			}
  279   		}
  280   		else {
  281   			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
  282   			try {
  283   				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  284   			}
  285   			catch (ClassNotFoundException ex) {
  286   				throw new ApplicationContextException(
  287   						"Failed to load default context class [" + contextClassName + "]", ex);
  288   			}
  289   		}
  290   	}
  291   
  292   	/**
  293   	 * Customize the {@link ConfigurableWebApplicationContext} created by this
  294   	 * ContextLoader after config locations have been supplied to the context
  295   	 * but before the context is <em>refreshed</em>.
  296   	 * <p>The default implementation is empty but can be overridden in subclasses
  297   	 * to customize the application context.
  298   	 * @param servletContext the current servlet context
  299   	 * @param applicationContext the newly created application context
  300   	 * @see #createWebApplicationContext(ServletContext, ApplicationContext)
  301   	 */
  302   	protected void customizeContext(
  303   			ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
  304   	}
  305   
  306   	/**
  307   	 * Template method with default implementation (which may be overridden by a
  308   	 * subclass), to load or obtain an ApplicationContext instance which will be
  309   	 * used as the parent context of the root WebApplicationContext. If the
  310   	 * return value from the method is null, no parent context is set.
  311   	 * <p>The main reason to load a parent context here is to allow multiple root
  312   	 * web application contexts to all be children of a shared EAR context, or
  313   	 * alternately to also share the same parent context that is visible to
  314   	 * EJBs. For pure web applications, there is usually no need to worry about
  315   	 * having a parent context to the root web application context.
  316   	 * <p>The default implementation uses
  317   	 * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
  318   	 * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
  319   	 * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
  320   	 * which will be shared by all other users of ContextsingletonBeanFactoryLocator
  321   	 * which also use the same configuration parameters.
  322   	 * @param servletContext current servlet context
  323   	 * @return the parent application context, or <code>null</code> if none
  324   	 * @throws BeansException if the context couldn't be initialized
  325   	 * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
  326   	 */
  327   	protected ApplicationContext loadParentContext(ServletContext servletContext)
  328   			throws BeansException {
  329   
  330   		ApplicationContext parentContext = null;
  331   		String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
  332   		String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
  333   
  334   		if (parentContextKey != null) {
  335   			// locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
  336   			BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
  337   			if (logger.isDebugEnabled()) {
  338   				logger.debug("Getting parent context definition: using parent context key of '" +
  339   						parentContextKey + "' with BeanFactoryLocator");
  340   			}
  341   			this.parentContextRef = locator.useBeanFactory(parentContextKey);
  342   			parentContext = (ApplicationContext) this.parentContextRef.getFactory();
  343   		}
  344   
  345   		return parentContext;
  346   	}
  347   
  348   	/**
  349   	 * Close Spring's web application context for the given servlet context. If
  350   	 * the default {@link #loadParentContext(ServletContext)} implementation,
  351   	 * which uses ContextSingletonBeanFactoryLocator, has loaded any shared
  352   	 * parent context, release one reference to that shared parent context.
  353   	 * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have
  354   	 * to override this method as well.
  355   	 * @param servletContext the ServletContext that the WebApplicationContext runs in
  356   	 */
  357   	public void closeWebApplicationContext(ServletContext servletContext) {
  358   		servletContext.log("Closing Spring root WebApplicationContext");
  359   		try {
  360   			if (this.context instanceof ConfigurableWebApplicationContext) {
  361   				((ConfigurableWebApplicationContext) this.context).close();
  362   			}
  363   		}
  364   		finally {
  365   			currentContextPerThread.remove(Thread.currentThread().getContextClassLoader());
  366   			servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
  367   			if (this.parentContextRef != null) {
  368   				this.parentContextRef.release();
  369   			}
  370   		}
  371   	}
  372   
  373   
  374   	/**
  375   	 * Obtain the Spring root web application context for the current thread
  376   	 * (i.e. for the current thread's context ClassLoader, which needs to be
  377   	 * the web application's ClassLoader).
  378   	 * @return the current root web application context, or <code>null</code>
  379   	 * if none found
  380   	 * @see org.springframework.web.context.support.SpringBeanAutowiringSupport
  381   	 */
  382   	public static WebApplicationContext getCurrentWebApplicationContext() {
  383   		return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
  384   	}
  385   
  386   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » web » context » [javadoc | source]