KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > context > ContextLoader


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

16
17 package org.springframework.web.context;
18
19 import java.io.IOException JavaDoc;
20 import java.util.Properties JavaDoc;
21
22 import javax.servlet.ServletContext JavaDoc;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import org.springframework.beans.BeanUtils;
28 import org.springframework.beans.BeansException;
29 import org.springframework.beans.factory.access.BeanFactoryLocator;
30 import org.springframework.beans.factory.access.BeanFactoryReference;
31 import org.springframework.context.ApplicationContext;
32 import org.springframework.context.ApplicationContextException;
33 import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
34 import org.springframework.core.io.ClassPathResource;
35 import org.springframework.core.io.support.PropertiesLoaderUtils;
36 import org.springframework.util.ClassUtils;
37 import org.springframework.util.StringUtils;
38 import org.springframework.web.context.support.XmlWebApplicationContext;
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 "contextClass" parameter at the web.xml context-param level
45  * to specify the context class type, falling back to the default of
46  * {@link XmlWebApplicationContext} if not found. With the default ContextLoader
47  * implementation, any context class specified needs to implement
48  * ConfigurableWebApplicationContext.
49  *
50  * <p>Passes a "contextConfigLocation" context-param to the context instance,
51  * parsing it into potentially multiple file paths which can be separated by
52  * any number of commas and spaces, like "applicationContext1.xml,
53  * applicationContext2.xml". If not explicitly specified, the context
54  * implementation is supposed to use a default location (with
55  * XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
56  *
57  * <p>Note: In case of multiple config locations, later bean definitions will
58  * override ones defined in earlier loaded files, at least when using one of
59  * Spring's default ApplicationContext implementations. This can be leveraged
60  * to deliberately override certain bean definitions via an extra XML file.
61  *
62  * <p>Above and beyond loading the root application context, this class can
63  * optionally load or obtain and hook up a shared parent context to the root
64  * application context. See the
65  * {@link #loadParentContext(ServletContext)} method for more information.
66  *
67  * @author Juergen Hoeller
68  * @author Colin Sampaleanu
69  * @since 17.02.2003
70  * @see ContextLoaderListener
71  * @see ContextLoaderServlet
72  * @see ConfigurableWebApplicationContext
73  * @see org.springframework.web.context.support.XmlWebApplicationContext
74  */

75 public class ContextLoader {
76
77     /**
78      * Config param for the root WebApplicationContext implementation class to
79      * use: "contextClass"
80      */

81     public static final String JavaDoc CONTEXT_CLASS_PARAM = "contextClass";
82
83     /**
84      * Name of servlet context parameter that can specify the config location
85      * for the root context, falling back to the implementation's default
86      * otherwise.
87      * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
88      */

89     public static final String JavaDoc CONFIG_LOCATION_PARAM = "contextConfigLocation";
90
91     /**
92      * Optional servlet context parameter used only when obtaining a parent
93      * context using the default implementation of
94      * {@link #loadParentContext(ServletContext servletContext)}. Specifies the
95      * 'selector' used in the
96      * {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)}
97      * method call used to obtain the BeanFactoryLocator instance from which the
98      * parent context is obtained.
99      * <p>This will normally be set to <code>classpath*:beanRefContext.xml</code>
100      * to match the default applied for the
101      * {@link ContextSingletonBeanFactoryLocator#getInstance()} method.
102      */

103     public static final String JavaDoc LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
104
105     /**
106      * Optional servlet context parameter used only when obtaining a parent
107      * context using the default implementation of
108      * {@link #loadParentContext(ServletContext servletContext)}. Specifies the
109      * 'factoryKey' used in the
110      * {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call
111      * used to obtain the parent application context from the BeanFactoryLocator
112      * instance.
113      */

114     public static final String JavaDoc LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
115
116     /**
117      * Name of the class path resource (relative to the ContextLoader class)
118      * that defines ContextLoader's default strategy names.
119      */

120     private static final String JavaDoc DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
121
122
123     private static final Properties JavaDoc defaultStrategies;
124
125     static {
126         // Load default strategy implementations from properties file.
127
// This is currently strictly internal and not meant to be customized
128
// by application developers.
129
try {
130             ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
131             defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
132         }
133         catch (IOException JavaDoc ex) {
134             throw new IllegalStateException JavaDoc("Could not load 'ContextLoader.properties': " + ex.getMessage());
135         }
136     }
137
138
139     private final Log logger = LogFactory.getLog(ContextLoader.class);
140
141     /**
142      * The root WebApplicationContext instance that this loaded manages.
143      */

144     private WebApplicationContext context;
145
146     /**
147      * Holds BeanFactoryReference when loading parent factory via
148      * ContextSingletonBeanFactoryLocator.
149      */

150     private BeanFactoryReference parentContextRef;
151
152
153     /**
154      * Initialize Spring's web application context for the given servlet context,
155      * according to the "contextClass" and "contextConfigLocation" context-params.
156      * @param servletContext current servlet context
157      * @return the new WebApplicationContext
158      * @throws IllegalStateException if there is already a root application context present
159      * @throws BeansException if the context failed to initialize
160      * @see #CONTEXT_CLASS_PARAM
161      * @see #CONFIG_LOCATION_PARAM
162      */

163     public WebApplicationContext initWebApplicationContext(ServletContext JavaDoc servletContext)
164             throws IllegalStateException JavaDoc, BeansException {
165
166         if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
167             throw new IllegalStateException JavaDoc(
168                     "Cannot initialize context because there is already a root application context present - " +
169                     "check whether you have multiple ContextLoader* definitions in your web.xml!");
170         }
171
172         servletContext.log("Initializing Spring root WebApplicationContext");
173         if (logger.isInfoEnabled()) {
174             logger.info("Root WebApplicationContext: initialization started");
175         }
176         long startTime = System.currentTimeMillis();
177
178         try {
179             // Determine parent for root web application context, if any.
180
ApplicationContext parent = loadParentContext(servletContext);
181
182             // Store context in local instance variable, to guarantee that
183
// it is available on ServletContext shutdown.
184
this.context = createWebApplicationContext(servletContext, parent);
185             servletContext.setAttribute(
186                     WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
187
188             if (logger.isDebugEnabled()) {
189                 logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
190                         WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
191             }
192             if (logger.isInfoEnabled()) {
193                 long elapsedTime = System.currentTimeMillis() - startTime;
194                 logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
195             }
196
197             return this.context;
198         }
199         catch (RuntimeException JavaDoc ex) {
200             logger.error("Context initialization failed", ex);
201             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
202             throw ex;
203         }
204         catch (Error JavaDoc err) {
205             logger.error("Context initialization failed", err);
206             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
207             throw err;
208         }
209     }
210
211     /**
212      * Instantiate the root WebApplicationContext for this loader, either the
213      * default context class or a custom context class if specified.
214      * <p>This implementation expects custom contexts to implement
215      * ConfigurableWebApplicationContext. Can be overridden in subclasses.
216      * @param servletContext current servlet context
217      * @param parent the parent ApplicationContext to use, or <code>null</code> if none
218      * @return the root WebApplicationContext
219      * @throws BeansException if the context couldn't be initialized
220      * @see ConfigurableWebApplicationContext
221      */

222     protected WebApplicationContext createWebApplicationContext(
223             ServletContext JavaDoc servletContext, ApplicationContext parent) throws BeansException {
224
225         Class JavaDoc contextClass = determineContextClass(servletContext);
226         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
227             throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
228                     "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
229         }
230
231         ConfigurableWebApplicationContext wac =
232                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
233         wac.setParent(parent);
234         wac.setServletContext(servletContext);
235         String JavaDoc configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
236         if (configLocation != null) {
237             wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
238                     ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
239         }
240
241         wac.refresh();
242         return wac;
243     }
244
245     /**
246      * Return the WebApplicationContext implementation class to use, either the
247      * default XmlWebApplicationContext or a custom context class if specified.
248      * @param servletContext current servlet context
249      * @return the WebApplicationContext implementation class to use
250      * @throws ApplicationContextException if the context class couldn't be loaded
251      * @see #CONTEXT_CLASS_PARAM
252      * @see org.springframework.web.context.support.XmlWebApplicationContext
253      */

254     protected Class JavaDoc determineContextClass(ServletContext JavaDoc servletContext) throws ApplicationContextException {
255         String JavaDoc contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
256         if (contextClassName != null) {
257             try {
258                 return ClassUtils.forName(contextClassName);
259             }
260             catch (ClassNotFoundException JavaDoc ex) {
261                 throw new ApplicationContextException(
262                         "Failed to load custom context class [" + contextClassName + "]", ex);
263             }
264         }
265         else {
266             contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
267             try {
268                 return ClassUtils.forName(contextClassName);
269             }
270             catch (ClassNotFoundException JavaDoc ex) {
271                 throw new ApplicationContextException(
272                         "Failed to load default context class [" + contextClassName + "]", ex);
273             }
274         }
275     }
276
277     /**
278      * Template method with default implementation (which may be overridden by a
279      * subclass), to load or obtain an ApplicationContext instance which will be
280      * used as the parent context of the root WebApplicationContext. If the
281      * return value from the method is null, no parent context is set.
282      * <p>The main reason to load a parent context here is to allow multiple root
283      * web application contexts to all be children of a shared EAR context, or
284      * alternately to also share the same parent context that is visible to
285      * EJBs. For pure web applications, there is usually no need to worry about
286      * having a parent context to the root web application context.
287      * <p>The default implementation uses ContextSingletonBeanFactoryLocator,
288      * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
289      * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
290      * which will be shared by all other users of ContextsingletonBeanFactoryLocator
291      * which also use the same configuration parameters.
292      * @param servletContext current servlet context
293      * @return the parent application context, or <code>null</code> if none
294      * @throws BeansException if the context couldn't be initialized
295      * @see org.springframework.beans.factory.access.BeanFactoryLocator
296      * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
297      */

298     protected ApplicationContext loadParentContext(ServletContext JavaDoc servletContext)
299             throws BeansException {
300
301         ApplicationContext parentContext = null;
302         String JavaDoc locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
303         String JavaDoc parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
304
305         if (locatorFactorySelector != null) {
306             BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
307             if (logger.isDebugEnabled()) {
308                 logger.debug("Getting parent context definition: using parent context key of '" +
309                         parentContextKey + "' with BeanFactoryLocator");
310             }
311             this.parentContextRef = locator.useBeanFactory(parentContextKey);
312             parentContext = (ApplicationContext) this.parentContextRef.getFactory();
313         }
314
315         return parentContext;
316     }
317
318
319     /**
320      * Close Spring's web application context for the given servlet context. If
321      * the default {@link #loadParentContext(ServletContext)}implementation,
322      * which uses ContextSingletonBeanFactoryLocator, has loaded any shared
323      * parent context, release one reference to that shared parent context.
324      * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have
325      * to override this method as well.
326      */

327     public void closeWebApplicationContext(ServletContext JavaDoc servletContext) {
328         servletContext.log("Closing Spring root WebApplicationContext");
329         try {
330             if (this.context instanceof ConfigurableWebApplicationContext) {
331                 ((ConfigurableWebApplicationContext) this.context).close();
332             }
333         }
334         finally {
335             if (this.parentContextRef != null) {
336                 this.parentContextRef.release();
337             }
338         }
339     }
340
341 }
342
Popular Tags