001: /*
002: * Copyright 2001-2007 Hippo (www.hippo.nl)
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: * http://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: package nl.hippo.util.spring;
017:
018: import java.util.HashSet;
019: import java.util.Iterator;
020: import java.util.Set;
021: import javax.servlet.ServletContext;
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.springframework.beans.BeanUtils;
025: import org.springframework.beans.BeansException;
026: import org.springframework.context.ApplicationContext;
027: import org.springframework.context.ApplicationContextException;
028: import org.springframework.context.ConfigurableApplicationContext;
029: import org.springframework.web.context.ConfigurableWebApplicationContext;
030: import org.springframework.web.context.ContextLoader;
031: import org.springframework.web.context.WebApplicationContext;
032: import org.springframework.web.context.support.XmlWebApplicationContext;
033:
034: import javax.servlet.ServletContext;
035: import javax.servlet.ServletConfig;
036:
037: import org.apache.avalon.framework.logger.Logger;
038: import org.apache.avalon.framework.logger.LogKitLogger;
039: import org.apache.log.ContextMap;
040: import org.apache.log.ErrorHandler;
041: import org.apache.log.Hierarchy;
042: import org.apache.log.Priority;
043: import org.apache.log.output.ServletOutputLogTarget;
044: import org.apache.log.util.DefaultErrorHandler;
045: import org.apache.log4j.LogManager;
046:
047: public class MultipleXmlContextLoader {
048:
049: /**
050: * Config param for the root WebApplicationContext implementation class to use:
051: * "contextClass"
052: */
053: public static final String CONTEXT_CLASS_PARAM = "contextClass";
054:
055: /**
056: * Default context class for ContextLoader.
057: * @see org.springframework.web.context.support.XmlWebApplicationContext
058: */
059: public static final Class DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
060:
061: /**
062: * Name of servlet context parameter that can specify the config location
063: * for the root context, falling back to the implementation's default else.
064: * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
065: */
066: public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
067:
068: private final Log logger = LogFactory.getLog(ContextLoader.class);
069:
070: public MultipleXmlContextLoader(ServletContext servletContext) {
071: String logLevel = servletContext.getInitParameter("log-level");
072: if (logLevel == null || logLevel.equals(""))
073: logLevel = "WARN";
074: Priority logPriority = Priority.getPriorityForName(logLevel);
075: Hierarchy defaultHierarchy = Hierarchy.getDefaultHierarchy();
076: Logger logger = new LogKitLogger(Hierarchy
077: .getDefaultHierarchy().getLoggerFor(""));
078: defaultHierarchy.setDefaultPriority(logPriority);
079: }
080:
081: /**
082: * Initialize Spring's web application context for the given servlet context,
083: * regarding the "contextClass" and "contextConfigLocation" context-params.
084: * @param servletContext current servlet context
085: * @return the new WebApplicationContext
086: * @throws BeansException if the context couldn't be initialized
087: * @see #CONTEXT_CLASS_PARAM
088: * @see #CONFIG_LOCATION_PARAM
089: */
090: public WebApplicationContext initWebApplicationContext(
091: ServletContext servletContext) throws BeansException {
092: servletContext.log("Loading root WebApplicationContext");
093:
094: try {
095: // determine parent for root web application context, if any
096: ApplicationContext parent = loadParentContext(servletContext);
097:
098: WebApplicationContext wac = createWebApplicationContext(
099: servletContext, parent);
100: servletContext
101: .setAttribute(
102: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
103: wac);
104:
105: if (logger.isInfoEnabled()) {
106: logger.info("Using context class ["
107: + wac.getClass().getName()
108: + "] for root WebApplicationContext");
109: logger
110: .info("Published root WebApplicationContext ["
111: + wac
112: + "] as ServletContext attribute with name ["
113: + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
114: + "]");
115: }
116:
117: return wac;
118: } catch (RuntimeException ex) {
119: logger.error("Context initialization failed", ex);
120: servletContext
121: .setAttribute(
122: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
123: ex);
124: throw ex;
125: } catch (Error err) {
126: logger.error("Context initialization failed", err);
127: servletContext
128: .setAttribute(
129: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
130: err);
131: throw err;
132: }
133: }
134:
135: /**
136: * Instantiate the root WebApplicationContext for this loader, either a default
137: * XmlWebApplicationContext or a custom context class if specified.
138: * <p>This implementation expects custom contexts to implement ConfigurableWebApplicationContext.
139: * Can be overridden in subclasses.
140: * @param servletContext current servlet context
141: * @param parent the parent ApplicationContext to use, or null if none
142: * @throws BeansException if the context couldn't be initialized
143: * @see #CONTEXT_CLASS_PARAM
144: * @see #DEFAULT_CONTEXT_CLASS
145: * @see ConfigurableWebApplicationContext
146: * @see org.springframework.web.context.support.XmlWebApplicationContext
147: */
148: protected WebApplicationContext createWebApplicationContext(
149: ServletContext servletContext, ApplicationContext parent)
150: throws BeansException {
151:
152: String contextClassName = servletContext
153: .getInitParameter(CONTEXT_CLASS_PARAM);
154: Class contextClass = DEFAULT_CONTEXT_CLASS;
155: if (contextClassName != null) {
156: try {
157: contextClass = Class.forName(contextClassName, true,
158: Thread.currentThread().getContextClassLoader());
159: } catch (ClassNotFoundException ex) {
160: throw new ApplicationContextException(
161: "Failed to load context class ["
162: + contextClassName + "]", ex);
163: }
164: if (!ConfigurableWebApplicationContext.class
165: .isAssignableFrom(contextClass)) {
166: throw new ApplicationContextException(
167: "Custom context class ["
168: + contextClassName
169: + "] is not of type ConfigurableWebApplicationContext");
170: }
171: }
172:
173: ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils
174: .instantiateClass(contextClass);
175: wac.setParent(parent);
176: wac.setServletContext(servletContext);
177: String configLocation = servletContext
178: .getInitParameter(CONFIG_LOCATION_PARAM);
179: if (configLocation != null) {
180: // Don't be too strict.
181: if (!configLocation.startsWith("/")) {
182: configLocation = "/" + configLocation;
183: }
184: if (!configLocation.endsWith("/")) {
185: configLocation += "/";
186: }
187:
188: Set resourcePaths = servletContext
189: .getResourcePaths(configLocation);
190: if (resourcePaths != null) {
191: Set xmlFileResourcePaths = new HashSet();
192: for (Iterator resourcePathsIterator = resourcePaths
193: .iterator(); resourcePathsIterator.hasNext();) {
194: String resourcePath = (String) resourcePathsIterator
195: .next();
196: if (resourcePath.endsWith(".xml")) {
197: xmlFileResourcePaths.add(resourcePath);
198: }
199: }
200:
201: String[] configLocations = new String[xmlFileResourcePaths
202: .size()];
203: int i = 0;
204: for (Iterator xmlFileResourcePathsIterator = xmlFileResourcePaths
205: .iterator(); xmlFileResourcePathsIterator
206: .hasNext();) {
207: String xmlFileResourcePath = (String) xmlFileResourcePathsIterator
208: .next();
209: configLocations[i++] = xmlFileResourcePath;
210: }
211:
212: wac.setConfigLocations(configLocations);
213: }
214: }
215:
216: wac.refresh();
217: return wac;
218: }
219:
220: /**
221: * Template method which may be overridden by a subclass to load or obtain
222: * an ApplicationContext instance which will be used as the parent context
223: * of the root WebApplicationContext (if it is not null).
224: * <p>The main reason for this hook is to allow root web application contexts
225: * to be children of a shared EAR context that's also visible to EJBs.
226: * For pure web applications, there is usually no need to worry about this.
227: * @param servletContext current servlet context
228: * @return the parent application context, or null if none
229: * @throws BeansException if the context couldn't be initialized
230: */
231: protected ApplicationContext loadParentContext(
232: ServletContext servletContext) throws BeansException {
233: return null;
234: }
235:
236: /**
237: * Close Spring's web application context for the given servlet context.
238: * @param servletContext current servlet context
239: */
240: public void closeWebApplicationContext(ServletContext servletContext)
241: throws ApplicationContextException {
242: servletContext.log("Closing root WebApplicationContext");
243: Object wac = servletContext
244: .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
245: if (wac instanceof ConfigurableApplicationContext) {
246: ((ConfigurableApplicationContext) wac).close();
247: }
248: }
249:
250: }
|