View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2008 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  
18  package ch.elca.el4j.web.context;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.io.InputStreamReader;
23  import java.util.Collection;
24  
25  import javax.servlet.ServletContext;
26  
27  import org.apache.commons.lang.ArrayUtils;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
31  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
32  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
33  import org.springframework.context.ApplicationContext;
34  import org.springframework.core.io.DefaultResourceLoader;
35  import org.springframework.core.io.Resource;
36  import org.springframework.core.io.ResourceLoader;
37  import org.springframework.core.io.support.ResourcePatternResolver;
38  import org.springframework.util.Assert;
39  import org.springframework.util.StringUtils;
40  import org.springframework.web.context.support.ServletContextResourceLoader;
41  import org.springframework.web.context.support.ServletContextResourcePatternResolver;
42  import org.springframework.web.context.support.XmlWebApplicationContext;
43  
44  import ch.elca.el4j.core.context.ModuleApplicationContext;
45  import ch.elca.el4j.core.context.ModuleApplicationContextUtils;
46  import ch.elca.el4j.core.context.ModuleApplicationListener;
47  import ch.elca.el4j.core.context.RefreshableModuleApplicationContext;
48  import ch.elca.el4j.core.io.support.ListResourcePatternResolverDecorator;
49  import ch.elca.el4j.core.io.support.ManifestOrderedConfigLocationProvider;
50  
51  /**
52   * This web application context behaves exactly the same way as Spring's
53   * {@link org.springframework.web.context.support.XmlWebApplicationContext} but
54   * uses a {@link org.springframework.core.io.support.ResourcePatternResolver}
55   * that preserves the order defined by the EL4J's module hierarchy. Further,
56   * it allows to define configuration locations that have to be included and
57   * those, that have to be excluded. This allows removing individual
58   * configuration files that are included using using wildcard notation.
59   *
60   * @svnLink $Revision: 4204 $;$Date: 2010-11-02 11:44:37 +0100 (Di, 02. Nov 2010) $;$Author: swisswheel $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/web/jar/src/main/java/ch/elca/el4j/web/context/ModuleWebApplicationContext.java $
61   *
62   * @author Andreas Bur (ABU)
63   * @author Martin Zeltner (MZE)
64   * @see ch.elca.el4j.core.context.ModuleApplicationContext
65   */
66  public class ModuleWebApplicationContext extends XmlWebApplicationContext
67  	implements RefreshableModuleApplicationContext {
68  	
69  	/**
70  	 * This logger is used to print out some global debugging info. Consult it
71  	 * for info what is going on.
72  	 */
73  	protected static final Logger s_loggerEl4j
74  		= LoggerFactory.getLogger(ModuleApplicationContext.EL4J_DEBUGGING_LOGGER);
75  
76  	/**
77  	 * Inclusive config locations.
78  	 */
79  	private final String[] m_inclusiveConfigLocations;
80  
81  	/**
82  	 * Exclusive config locations.
83  	 */
84  	private final String[] m_exclusiveConfigLocations;
85  
86  	/**
87  	 * Config locations.
88  	 */
89  	private final String[] m_configLocations;
90  
91  	/**
92  	 * Indicates if bean definition overriding is enabled.
93  	 */
94  	private final boolean m_allowBeanDefinitionOverriding;
95  	
96  	/**
97  	 * Indicates if unordered/unknown resource should be used.
98  	 */
99  	private final boolean m_mergeWithOuterResources;
100 	
101 	/**
102 	 * Indicates if the most specific resource should be the last resource
103 	 * in the fetched resource array. If its value is set to <code>true</code>
104 	 * and only one resource is requested the least specific resource will be
105 	 * returned. Default is set to <code>false</code>.
106 	 */
107 	private final boolean m_mostSpecificResourceLast;
108 	
109 	/**
110 	 * Indicates if the most specific bean definition counts.
111 	 */
112 	private final boolean m_mostSpecificBeanDefinitionCounts;
113 	
114 	/**
115 	 * The resource pattern resolver.
116 	 */
117 	private ListResourcePatternResolverDecorator m_patternResolver;
118 	
119 	/**
120 	 * Is context refreshed i.e. fully initialized?
121 	 */
122 	private boolean m_refreshed = false;
123 	
124 	/**
125 	 * Synchronization monitor for the "refreshed" flag.
126 	 */
127 	private final Object m_refreshedMonitor = new Object();
128 	
129 	/**
130 	 * There is no parent app context.
131 	 *
132 	 * @see #ModuleWebApplicationContext(String[], String[], boolean,
133 	 *       ServletContext, boolean, ApplicationContext))
134 	 */
135 	public ModuleWebApplicationContext(String[] inclusiveConfigLocations,
136 		String[] exclusiveConfigLocations,
137 		boolean allowBeanDefinitionOverriding, ServletContext servletContext,
138 		boolean mergeWithOuterResources) {
139 		this(inclusiveConfigLocations, exclusiveConfigLocations,
140 			allowBeanDefinitionOverriding, servletContext,
141 			mergeWithOuterResources, null);
142 	}
143 	
144 	/**
145 	 * <ul>
146 	 * <li>Most specific resource last is set to <code>false</code>.</li>
147 	 * <li>Most specific bean definition counts is set to <code>true</code>.</li>
148 	 * </ul>
149 	 *
150 	 * @see #ModuleWebApplicationContext(String[], String[], boolean,
151 	 *       ServletContext, boolean, boolean, boolean))
152 	 */
153 	public ModuleWebApplicationContext(String[] inclusiveConfigLocations,
154 		String[] exclusiveConfigLocations,
155 		boolean allowBeanDefinitionOverriding, ServletContext servletContext,
156 		boolean mergeWithOuterResources, ApplicationContext parent) {
157 		this(inclusiveConfigLocations, exclusiveConfigLocations,
158 			allowBeanDefinitionOverriding, servletContext,
159 			mergeWithOuterResources, false, true, parent);
160 	}
161 	
162 	/**
163 	 * There is no parent app context.
164 	 *
165 	 * @see #ModuleWebApplicationContext(String[], String[], boolean,
166 	 *       ServletContext, boolean, boolean, boolean))
167 	 */
168 	public ModuleWebApplicationContext(String[] inclusiveConfigLocations,
169 		String[] exclusiveConfigLocations,
170 		boolean allowBeanDefinitionOverriding, ServletContext servletContext,
171 		boolean mergeWithOuterResources, boolean mostSpecificResourceLast,
172 		boolean mostSpecificBeanDefinitionCounts) {
173 		this(inclusiveConfigLocations, exclusiveConfigLocations,
174 			allowBeanDefinitionOverriding, servletContext,
175 			mergeWithOuterResources, mostSpecificResourceLast, mostSpecificBeanDefinitionCounts, null);
176 	}
177 	
178 	/**
179 	 * Create a new ModuleApplicationContext with the given parent, loading the
180 	 * definitions from the given XML files in "inclusiveConfigLocations"
181 	 * excluded the XML files defined in "exclusiveConfigLocations". If the
182 	 * parameter "allowBeanDefinitionOverriding" is set to true then the
183 	 * BeanFactory is allowed to override a bean if there is another one with
184 	 * the same name.
185 	 *
186 	 * @param inclusiveConfigLocations
187 	 *            array of file paths
188 	 * @param exclusiveConfigLocations
189 	 *            array of file paths which are excluded
190 	 * @param allowBeanDefinitionOverriding
191 	 *            a boolean which defines if overriding of bean definitions is
192 	 *            allowed
193 	 * @param servletContext
194 	 *            the servlet context where this application context is used
195 	 * @param mergeWithOuterResources
196 	 *            a boolean which defines if the resources retrieved by the
197 	 *            configuration files section of the manifest files should be
198 	 *            merged with resources found by searching in the file system.
199 	 * @param mostSpecificResourceLast
200 	 *            Indicates if the most specific resource should be the last
201 	 *            resource in the fetched resource array. If its value is set to
202 	 *            <code>true</code> and only one resource is requested the
203 	 *            least specific resource will be returned.
204 	 * @param mostSpecificBeanDefinitionCounts
205 	 *            Indicates that the most specific bean definition is used.
206 	 * @param parent
207 	 *            Is the parent application context. 
208 	 */
209 	public ModuleWebApplicationContext(String[] inclusiveConfigLocations,
210 		String[] exclusiveConfigLocations,
211 		boolean allowBeanDefinitionOverriding, ServletContext servletContext,
212 		boolean mergeWithOuterResources, boolean mostSpecificResourceLast,
213 		boolean mostSpecificBeanDefinitionCounts, ApplicationContext parent) {
214 
215 		super();
216 		
217 		m_inclusiveConfigLocations = inclusiveConfigLocations;
218 		m_exclusiveConfigLocations = exclusiveConfigLocations;
219 		m_allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
220 		m_mergeWithOuterResources = mergeWithOuterResources;
221 		m_mostSpecificResourceLast = mostSpecificResourceLast;
222 		m_mostSpecificBeanDefinitionCounts = mostSpecificBeanDefinitionCounts;
223 		setServletContext(servletContext);
224 		setParent(parent);
225 		
226 		/**
227 		 * HACK: The pattern resolver is initialized by a super class
228 		 * via method <code>getResourcePatternResolver</code>.
229 		 */
230 		Assert.notNull(m_patternResolver);
231 		Assert.isInstanceOf(ListResourcePatternResolverDecorator.class, m_patternResolver);
232 		ListResourcePatternResolverDecorator listResourcePatternResolver
233 			= (ListResourcePatternResolverDecorator) m_patternResolver;
234 		listResourcePatternResolver.setMostSpecificResourceLast(isMostSpecificResourceLast());
235 		listResourcePatternResolver.setMergeWithOuterResources(isMergeWithOuterResources());
236 		
237 		if (servletContext != null) {
238 			listResourcePatternResolver.setPatternResolver(
239 				new ServletContextResourcePatternResolver(servletContext));
240 		}
241 
242 		ModuleApplicationContextUtils utils
243 			= new ModuleApplicationContextUtils(this);
244 		utils.setReverseConfigLocationResourceArray(
245 			isMostSpecificResourceLast() != isMostSpecificBeanDefinitionCounts());
246 		
247 		m_configLocations = utils.calculateInputFiles(inclusiveConfigLocations,
248 				exclusiveConfigLocations, allowBeanDefinitionOverriding);
249 
250 		if (!ArrayUtils.isEmpty(m_configLocations)) {
251 			setConfigLocations(m_configLocations);
252 		}
253 		
254 		additionalLoggingOutput(allowBeanDefinitionOverriding,
255 			mergeWithOuterResources, mostSpecificResourceLast,
256 			mostSpecificBeanDefinitionCounts);
257 	}
258 
259 	/**
260 	 * Log some interesting values. Not nice: Code duplication between the 2
261 	 * ModuleApplicationContext classes!
262 	 *
263 	 * @param allowBeanDefinitionOverriding
264 	 *            a boolean which defines if overriding of bean definitions is
265 	 *            allowed
266 	 * @param mergeWithOuterResources
267 	 *            a boolean which defines if the resources retrieved by the
268 	 *            configuration files section of the manifest files should be
269 	 *            merged with resources found by searching in the file system.
270 	 * @param mostSpecificResourceLast
271 	 *            Indicates if the most specific resource should be the last
272 	 *            resource in the fetched resource array. If its value is set to
273 	 *            <code>true</code> and only one resource is requested the
274 	 *            least specific resource will be returned.
275 	 * @param mostSpecificBeanDefinitionCounts
276 	 *            Indicates that the most specific bean definition is used.
277 	 */
278 	private void additionalLoggingOutput(boolean allowBeanDefinitionOverriding,
279 		boolean mergeWithOuterResources, boolean mostSpecificResourceLast,
280 		boolean mostSpecificBeanDefinitionCounts) {
281 
282 		s_loggerEl4j.info("Starting up ModuleApplicationContext. configLocations :"
283 			+ StringUtils.arrayToDelimitedString(m_configLocations, ", "));
284 		if (s_loggerEl4j.isDebugEnabled()) {
285 			s_loggerEl4j.debug("inclusiveLocation:"
286 				+ StringUtils.arrayToDelimitedString(m_inclusiveConfigLocations, ", "));
287 			s_loggerEl4j.debug("exclusiveLocation:"
288 				+ StringUtils.arrayToDelimitedString(m_exclusiveConfigLocations, ", "));
289 			s_loggerEl4j.debug("allowBeanDefinitionOverriding:"
290 				+ allowBeanDefinitionOverriding);
291 			s_loggerEl4j.debug("mergeWithOuterResources:"
292 				+ mergeWithOuterResources);
293 			s_loggerEl4j.debug("mostSpecificResourceLast:"
294 				+ mostSpecificResourceLast);
295 			s_loggerEl4j.debug("mostSpecificBeanDefinitionCounts:"
296 				+ mostSpecificBeanDefinitionCounts);
297 
298 			BufferedReader reader = null;
299 			try {
300 				for (String configLocation : m_configLocations) {
301 					Resource res = getResource(configLocation);
302 					reader = new BufferedReader(new InputStreamReader(res.getInputStream()));
303 					StringBuffer buf = new StringBuffer();
304 					while (reader.ready()) {
305 						buf.append(reader.readLine());
306 						buf.append("\n");
307 					}
308 					s_loggerEl4j.debug("Content of " + configLocation + " : "
309 						+ buf.toString() + "\n---");
310 
311 				}
312 			} catch (Exception e) {
313 				// deliberately ignore exception
314 				s_loggerEl4j.debug("Error during printing of config location "
315 					+ StringUtils.arrayToCommaDelimitedString(m_configLocations)
316 					, e);
317 			} finally {
318 				if (reader != null) {
319 					try {
320 						reader.close();
321 					} catch (IOException e) {
322 						s_loggerEl4j.debug("Error closing reader", e);
323 					}
324 				}
325 			}
326 		}
327 	}
328 	
329 	/**
330 	 * @return Returns the exclusiveConfigLocations.
331 	 */
332 	public String[] getExclusiveConfigLocations() {
333 		return m_exclusiveConfigLocations;
334 	}
335 
336 	/**
337 	 * @return Returns the inclusiveConfigLocations.
338 	 */
339 	public String[] getInclusiveConfigLocations() {
340 		return m_inclusiveConfigLocations;
341 	}
342 
343 	/**
344 	 * @return Returns the allowBeanDefinitionOverriding.
345 	 */
346 	public boolean isAllowBeanDefinitionOverriding() {
347 		return m_allowBeanDefinitionOverriding;
348 	}
349 
350 	/**
351 	 * @return Returns the mergeWithOuterResources.
352 	 */
353 	public boolean isMergeWithOuterResources() {
354 		return m_mergeWithOuterResources;
355 	}
356 
357 	/**
358 	 * @return Returns the mostSpecificResourceLast.
359 	 */
360 	public boolean isMostSpecificResourceLast() {
361 		return m_mostSpecificResourceLast;
362 	}
363 
364 	/**
365 	 * @return Returns the mostSpecificBeanDefinitionCounts.
366 	 */
367 	public boolean isMostSpecificBeanDefinitionCounts() {
368 		return m_mostSpecificBeanDefinitionCounts;
369 	}
370 	
371 	/**
372 	 * {@inheritDoc}
373 	 */
374 	@Override
375 	public Resource getResource(String location) {
376 		return m_patternResolver.getResource(location);
377 	}
378 
379 	/**
380 	 * {@inheritDoc}
381 	 */
382 	@Override
383 	public Resource[] getResources(String locationPattern) throws IOException {
384 		return m_patternResolver.getResources(locationPattern);
385 	}
386 	
387 	/**
388 	 * Override method createBeanFactory() in class
389 	 * AbstractRefreshableApplicationContext. The property
390 	 * m_allowBeanDefinitionOverriding can be set and is handed over to the
391 	 * DefaultListableBeanFactory which creates the BeanFactory.
392 	 *
393 	 * @return the DefaultListableBeanFactory
394 	 */
395 	@Override
396 	protected DefaultListableBeanFactory createBeanFactory() {
397 		DefaultListableBeanFactory dlbf = new DefaultListableBeanFactory(
398 				getInternalParentBeanFactory());
399 		dlbf.setAllowBeanDefinitionOverriding(
400 			isAllowBeanDefinitionOverriding());
401 		return dlbf;
402 	}
403 
404 	/**
405 	 * {@inheritDoc}
406 	 */
407 	@Override
408 	protected ResourcePatternResolver getResourcePatternResolver() {
409 		ResourceLoader resourceLoader;
410 		ServletContext servletContext = getServletContext();
411 		if (servletContext == null) {
412 			resourceLoader = new DefaultResourceLoader(getClassLoader());
413 		} else {
414 			resourceLoader = new ServletContextResourceLoader(servletContext);
415 		}
416 		
417 		ListResourcePatternResolverDecorator patternResolver
418 			= new ListResourcePatternResolverDecorator(
419 				new ManifestOrderedConfigLocationProvider(),
420 				new ServletContextResourcePatternResolver(
421 					resourceLoader));
422 		patternResolver.setMostSpecificResourceLast(
423 			isMostSpecificResourceLast());
424 		patternResolver.setMergeWithOuterResources(
425 			isMergeWithOuterResources());
426 		m_patternResolver = patternResolver;
427 		return m_patternResolver;
428 	}
429 	
430 	/**
431 	 * Not just method {@link BeanFactoryPostProcessor#postProcessBeanFactory(
432 	 * ConfigurableListableBeanFactory)} is invoked ordered but also the
433 	 * creation of the factory post processor beans!
434 	 *
435 	 * {@inheritDoc}
436 	 */
437 	protected void invokeBeanFactoryPostProcessors(
438 		ConfigurableListableBeanFactory beanFactory) {
439 		ModuleApplicationContextUtils ctxUtil
440 			= new ModuleApplicationContextUtils(this);
441 		ctxUtil.invokeBeanFactoryPostProcessorsStrictlyOrdered(beanFactory);
442 	}
443 	
444 	
445 	/** {@inheritDoc} */
446 	@Override
447 	protected void prepareRefresh() {
448 		synchronized (m_refreshedMonitor) {
449 			m_refreshed = false;
450 		}
451 		
452 		super.prepareRefresh();
453 	}
454 	
455 	/**
456 	 * Notify all {@link ModuleApplicationListener}s that context has been refreshed.
457 	 * 
458 	 * {@inheritDoc}
459 	 */
460 	@SuppressWarnings("unchecked")
461 	@Override
462 	protected void finishRefresh() {
463 		synchronized (m_refreshedMonitor) {
464 			m_refreshed = true;
465 		}
466 		
467 		Collection<ModuleApplicationListener> listeners = getBeansOfType(
468 			ModuleApplicationListener.class, true, false).values();
469 		for (ModuleApplicationListener listener : listeners) {
470 			listener.onContextRefreshed();
471 		}
472 		super.finishRefresh();
473 	}
474 	
475 	/** {@inheritDoc} */
476 	public boolean isRefreshed() {
477 		synchronized (m_refreshedMonitor) {
478 			return m_refreshed;
479 		}
480 	}
481 }