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) 2005 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.core.context;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.commons.lang.ArrayUtils;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  import org.springframework.beans.PropertyValue;
30  import org.springframework.beans.PropertyValues;
31  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
32  import org.springframework.beans.factory.config.BeanDefinition;
33  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
34  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
35  import org.springframework.beans.factory.config.TypedStringValue;
36  import org.springframework.context.ApplicationContext;
37  import org.springframework.context.support.AbstractApplicationContext;
38  import org.springframework.core.OrderComparator;
39  import org.springframework.core.Ordered;
40  import org.springframework.core.PriorityOrdered;
41  import org.springframework.core.io.Resource;
42  import org.springframework.util.Assert;
43  
44  import ch.elca.el4j.services.monitoring.notification.CoreNotificationHelper;
45  
46  /**
47   * This class allows excluding some items out of a file list.
48   *
49   * @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/core/src/main/java/ch/elca/el4j/core/context/ModuleApplicationContextUtils.java $
50   *
51   * @author Andreas Bur (ABU)
52   */
53  public class ModuleApplicationContextUtils {
54  
55  	/**
56  	 * String to find all spring configuration files in folder
57  	 * <code>mandatory</code>.
58  	 */
59  	private static final String MANDATORY = "mandatory/*.xml";
60  	
61  	/** The static logger. */
62  	private static Logger s_logger = LoggerFactory.getLogger(
63  			ModuleApplicationContextUtils.class);
64  	
65  	/** The application context that uses this instance. */
66  	private ApplicationContext m_appContext;
67  	
68  	/**
69  	 * @see #setReverseConfigLocationResourceArray(boolean)
70  	 */
71  	private boolean m_reverseConfigLocationResourceArray = false;
72  	
73  	/**
74  	 * Creates a new instance that is connected to the given application
75  	 * contet.
76  	 *
77  	 * @param context
78  	 *          The application context to connect to.
79  	 */
80  	public ModuleApplicationContextUtils(ApplicationContext context) {
81  		m_appContext = context;
82  	}
83  	
84  	/**
85  	 * Calculate the array of xml configuration files which are loaded into the
86  	 * ApplicationContext, i.e. exclude the xml files in inclusiveFileNames
87  	 * which are in exclusiveFileNames.
88  	 *
89  	 * @param inclusiveConfigLocations
90  	 *            array of file paths
91  	 * @param exclusiveConfigLocations
92  	 *            array of file paths which are excluded
93  	 * @param allowBeanDefinitionOverriding
94  	 *            a boolean which defines if overriding of bean definitions is
95  	 *            allowed
96  	 * @return Returns the adapted list of configuration locations.
97  	 */
98  	public String[] calculateInputFiles(String[] inclusiveConfigLocations,
99  			String[] exclusiveConfigLocations,
100 			boolean allowBeanDefinitionOverriding) {
101 		
102 		if (ArrayUtils.isEmpty(inclusiveConfigLocations)) {
103 			s_logger.warn("No inclusive configuration locations given!");
104 			return null;
105 		}
106 
107 		// check config location but only for the root application context, not for
108 		// child contexts
109 		if (m_appContext.getParent() == null) {
110 			checkConfigLocations(inclusiveConfigLocations[0]);
111 		}
112 		
113 		List<String> inclusiveFileNames
114 			= getResolvedFileNames(inclusiveConfigLocations);
115 
116 		List<String> exclusiveFileNames
117 			= getResolvedFileNames(exclusiveConfigLocations);
118 
119 		//remove the xml files in inclusiveFileNames which are in
120 		// exclusiveFileNames
121 		for (int i = 0; i < inclusiveFileNames.size(); i++) {
122 			Object obj = inclusiveFileNames.get(i);
123 			if (exclusiveFileNames.contains(obj)) {
124 				inclusiveFileNames.remove(i);
125 				i--;
126 			}
127 		}
128 
129 		String[] conLoc = new String[inclusiveFileNames.size()];
130 
131 		for (int i = 0; i < inclusiveFileNames.size(); i++) {
132 			conLoc[i] = (String) inclusiveFileNames.get(i);
133 		}
134 
135 		return conLoc;
136 	}
137 	
138 	/**
139 	 * Check whether the 'classpath*:mandatory/*.xml' config location is loaded.
140 	 *
141 	 * @param configLocation
142 	 *            The config location
143 	 */
144 	protected void checkConfigLocations(String configLocation) {
145 		if (!(configLocation.equals("classpath*:" + MANDATORY)
146 				|| (configLocation.equals("classpath*:/" + MANDATORY)))) {
147 
148 			s_logger.warn("The config location 'classpath*:" + MANDATORY
149 					+ "' is not loaded or is not the first config location"
150 					+ " which is loaded.");
151 		}
152 	}
153 
154 	/**
155 	 * Changes the syntax of the pathnames, i.e. filepaths beginning with
156 	 * "file:$Drive" and not with "file:/$Drive" are changed and "\" characters
157 	 * are changed to "/". This is necessary for the
158 	 * PathMatchingResourcePatternResolver to resolve ant-style filepaths.
159 	 *
160 	 * @param unresolvedFileNames Are the names of unresolved file names.
161 	 * @return Returns a list of resolved file names.
162 	 */
163 	protected List<String> getResolvedFileNames(String[] unresolvedFileNames) {
164 
165 		List<String> result = new ArrayList<String>();
166 
167 		if (unresolvedFileNames == null) {
168 			return result;
169 		}
170 
171 		for (int i = 0; i < unresolvedFileNames.length; i++) {
172 			String[] resolvedFileNames = resolveAttribute(unresolvedFileNames[i]
173 					.replace('\\', '/'));
174 			for (int j = 0; j < resolvedFileNames.length; j++) {
175 				if ((resolvedFileNames[j].startsWith("file:"))
176 						&& (!resolvedFileNames[j].startsWith("file:/"))) {
177 					resolvedFileNames[j] = resolvedFileNames[j].replaceFirst(
178 							"file:", "file:/");
179 				}
180 				result.add(resolvedFileNames[j]);
181 			}
182 		}
183 		return result;
184 	}
185 
186 	/**
187 	 * Resolves a path (i.e. file- or classpath) by applying Ant-style path
188 	 * matching. Returns all resolved xml files. A warning will be displayed if
189 	 * a resource does not exist.
190 	 *
191 	 * @param path
192 	 *            a path of an xml file, either absolute, relative or Ant-style
193 	 * @return all resolved xml files
194 	 */
195 	protected String[] resolveAttribute(String path) {
196 		List<String> resolvedAttributes = new ArrayList<String>();
197 
198 		try {
199 			Resource[] resLocal = m_appContext.getResources(path);
200 			if (isReverseConfigLocationResourceArray()) {
201 				ArrayUtils.reverse(resLocal);
202 			}
203 
204 			for (int i = 0; i < resLocal.length; i++) {
205 				if (resLocal[i].exists()) {
206 					resolvedAttributes.add(resLocal[i].getURL().toString());
207 				} else {
208 					s_logger.warn("The file '" + resLocal[i].toString()
209 							+ "' does not exist.");
210 				}
211 			}
212 		} catch (IOException e) {
213 			String message = "An IOException has occurred.";
214 			CoreNotificationHelper.notifyMisconfiguration(message, e);
215 		}
216 
217 		String[] result = new String[resolvedAttributes.size()];
218 
219 		for (int i = 0; i < result.length; i++) {
220 			result[i] = (String) resolvedAttributes.get(i);
221 		}
222 		return result;
223 	}
224 
225 	/**
226 	 * @return Returns the reverseConfigLocationResourceArray.
227 	 */
228 	public boolean isReverseConfigLocationResourceArray() {
229 		return m_reverseConfigLocationResourceArray;
230 	}
231 
232 	/**
233 	 * Flag to indicate if the resource array of a config location should be
234 	 * reversed. The default is set to <code>false</code>.
235 	 *
236 	 * @param reverseConfigLocationResourceArray
237 	 *            Is the reverseConfigLocationResourceArray to set.
238 	 */
239 	public void setReverseConfigLocationResourceArray(
240 		boolean reverseConfigLocationResourceArray) {
241 		m_reverseConfigLocationResourceArray
242 			= reverseConfigLocationResourceArray;
243 	}
244 	
245 	/**
246 	 * All bean factory post processors of the given bean factory will be
247 	 * created and invoked in strict order. First the {@link PriorityOrdered},
248 	 * then the {@link Ordered} and as last the unordered bean factory post
249 	 * processors.
250 	 *
251 	 * @param beanFactory
252 	 *            Is the factory to create the bean factory post processors.
253 	 */
254 	@SuppressWarnings("unchecked")
255 	public void invokeBeanFactoryPostProcessorsStrictlyOrdered(
256 		ConfigurableListableBeanFactory beanFactory) {
257 		
258 		// The given application context must be an AbstractApplicationContext
259 		Assert.isInstanceOf(AbstractApplicationContext.class, m_appContext);
260 		AbstractApplicationContext ctx
261 			= (AbstractApplicationContext) m_appContext;
262 		
263 		// Invoke factory processors registered with the context instance.
264 		for (Iterator it = ctx.getBeanFactoryPostProcessors().iterator();
265 			it.hasNext();) {
266 			
267 			BeanFactoryPostProcessor factoryProcessor
268 				= (BeanFactoryPostProcessor) it.next();
269 			factoryProcessor.postProcessBeanFactory(beanFactory);
270 		}
271 
272 		// Do not initialize FactoryBeans here: We need to leave all regular
273 		// beans uninitialized to let the bean factory post-processors apply to
274 		// them!
275 		String[] postProcessorNames
276 			= beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class,
277 				true, false);
278 
279 		// Separate between BeanFactoryPostProcessors that implement
280 		// PriorityOrdered, Ordered, and the rest.
281 		List<OrderedBeanNameHolder> priorityOrderedPostProcessorHolders
282 			= new ArrayList<OrderedBeanNameHolder>();
283 		List<OrderedBeanNameHolder> orderedPostProcessorHolders
284 			= new ArrayList<OrderedBeanNameHolder>();
285 		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
286 		for (int i = 0; i < postProcessorNames.length; i++) {
287 			String postProcessorName = postProcessorNames[i];
288 			if (ctx.isTypeMatch(postProcessorName, PriorityOrdered.class)) {
289 				priorityOrderedPostProcessorHolders.add(
290 					getOrderedBeanNameHolder(beanFactory, postProcessorName));
291 			} else if (ctx.isTypeMatch(postProcessorName, Ordered.class)) {
292 				orderedPostProcessorHolders.add(
293 					getOrderedBeanNameHolder(beanFactory, postProcessorName));
294 			} else {
295 				nonOrderedPostProcessorNames.add(postProcessorName);
296 			}
297 		}
298 
299 		// First, invoke the BeanFactoryPostProcessors that implement
300 		// PriorityOrdered.
301 		Collections.sort(priorityOrderedPostProcessorHolders,
302 			new OrderComparator());
303 		invokeBeanFactoryPostProcessors(beanFactory,
304 			priorityOrderedPostProcessorHolders);
305 
306 		// Second, invoke the BeanFactoryPostProcessors that implement Ordered.
307 		Collections.sort(orderedPostProcessorHolders,
308 			new OrderComparator());
309 		invokeBeanFactoryPostProcessors(beanFactory,
310 			orderedPostProcessorHolders);
311 
312 		// Finally, invoke all other BeanFactoryPostProcessors.
313 		for (String nonOrderedPostProcessorName
314 			: nonOrderedPostProcessorNames) {
315 			Object bean = ctx.getBean(nonOrderedPostProcessorName);
316 			BeanFactoryPostProcessor postProcessor
317 				= (BeanFactoryPostProcessor) bean;
318 			postProcessor.postProcessBeanFactory(beanFactory);
319 		}
320 	}
321 
322 	/**
323 	 * Invoke the given BeanFactoryPostProcessor beans.
324 	 *
325 	 * @param beanFactory
326 	 *            Is the factory where to create the
327 	 *            <code>BeanFactoryPostProcessor</code>s
328 	 * @param postProcessorHolders
329 	 *            Are the holders of the factory post processor bean names.
330 	 */
331 	protected void invokeBeanFactoryPostProcessors(
332 		ConfigurableListableBeanFactory beanFactory,
333 		List<OrderedBeanNameHolder> postProcessorHolders) {
334 		for (OrderedBeanNameHolder orderedBeanNameHolder
335 			: postProcessorHolders) {
336 			Object bean = m_appContext.getBean(
337 				orderedBeanNameHolder.getBeanName());
338 			BeanFactoryPostProcessor postProcessor
339 				= (BeanFactoryPostProcessor) bean;
340 			postProcessor.postProcessBeanFactory(beanFactory);
341 		}
342 	}
343 	
344 	/**
345 	 * Returns a ordered bean name holder for the given bean.
346 	 *
347 	 * @param beanFactory
348 	 *            Is the factory where the bean is configured.
349 	 * @param orderedBeanName
350 	 *            Is the name of the ordered bean.
351 	 * @return Returns a ordered bean name holder for the given bean.
352 	 * @throws NoSuchBeanDefinitionException
353 	 *             If the given bean name does not exist.
354 	 */
355 	protected OrderedBeanNameHolder getOrderedBeanNameHolder(
356 		ConfigurableListableBeanFactory beanFactory, String orderedBeanName)
357 		throws NoSuchBeanDefinitionException {
358 		
359 		BeanDefinition beanDefinition
360 			= beanFactory.getBeanDefinition(orderedBeanName);
361 		PropertyValues processorDefinitionProps
362 			= beanDefinition.getPropertyValues();
363 		PropertyValue order
364 			= processorDefinitionProps.getPropertyValue("order");
365 		int orderAsInt = 0;
366 		if (order != null) {
367 			try {
368 				Object orderValue = order.getValue();
369 				String orderAsString;
370 				if (orderValue instanceof TypedStringValue) {
371 					TypedStringValue orderValueString
372 						= (TypedStringValue) order.getValue();
373 					orderAsString = orderValueString.getValue();
374 				} else {
375 					orderAsString = orderValue.toString();
376 				}
377 				orderAsInt = Integer.parseInt(orderAsString);
378 			} catch (NumberFormatException e) {
379 				orderAsInt = 0;
380 			}
381 		}
382 		return new OrderedBeanNameHolder(orderAsInt, orderedBeanName);
383 	}
384 }