View Javadoc

1   /**
2    * Copyright 2010-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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  package org.kuali.common.util.spring;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  
28  import org.codehaus.plexus.util.StringUtils;
29  import org.kuali.common.util.Assert;
30  import org.kuali.common.util.CollectionUtils;
31  import org.kuali.common.util.FormatUtils;
32  import org.kuali.common.util.LocationUtils;
33  import org.kuali.common.util.LoggerLevel;
34  import org.kuali.common.util.LoggerUtils;
35  import org.kuali.common.util.Project;
36  import org.kuali.common.util.ProjectContext;
37  import org.kuali.common.util.ProjectUtils;
38  import org.kuali.common.util.PropertyUtils;
39  import org.kuali.common.util.ReflectionUtils;
40  import org.kuali.common.util.Str;
41  import org.kuali.common.util.execute.Executable;
42  import org.kuali.common.util.execute.SpringExecutable;
43  import org.kuali.common.util.nullify.NullUtils;
44  import org.kuali.common.util.property.Constants;
45  import org.kuali.common.util.property.ProjectProperties;
46  import org.kuali.common.util.service.DefaultSpringService;
47  import org.kuali.common.util.service.PropertySourceContext;
48  import org.kuali.common.util.service.SpringContext;
49  import org.kuali.common.util.service.SpringService;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  import org.springframework.beans.factory.BeanFactoryUtils;
53  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
54  import org.springframework.context.ApplicationContext;
55  import org.springframework.context.ConfigurableApplicationContext;
56  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
57  import org.springframework.context.support.ClassPathXmlApplicationContext;
58  import org.springframework.context.support.GenericXmlApplicationContext;
59  import org.springframework.core.env.ConfigurableEnvironment;
60  import org.springframework.core.env.EnumerablePropertySource;
61  import org.springframework.core.env.Environment;
62  import org.springframework.core.env.MutablePropertySources;
63  import org.springframework.core.env.PropertiesPropertySource;
64  import org.springframework.core.env.PropertySource;
65  
66  public class SpringUtils {
67  
68  	private static final Logger logger = LoggerFactory.getLogger(SpringUtils.class);
69  
70  	private static final String GLOBAL_SPRING_PROPERTY_SOURCE_NAME = "springPropertySource";
71  
72  	public static SpringContext getSpringContext(List<Class<?>> annotatedClasses, ProjectContext project, List<ProjectContext> others) {
73  		// This PropertySource object is backed by a set of properties that has been
74  		// 1 - fully resolved
75  		// 2 - contains all properties needed by Spring
76  		// 3 - contains system/environment properties where system/env properties override loaded properties
77  		PropertySource<?> source = getGlobalPropertySource(project, others);
78  
79  		// Setup a property source context such that our single property source is the only one registered with Spring
80  		// This will make it so our PropertySource is the ONLY thing used to resolve placeholders
81  		PropertySourceContext psc = new PropertySourceContext(source, true);
82  
83  		// Setup a Spring context
84  		SpringContext context = new SpringContext();
85  
86  		// Supply Spring with our PropertySource
87  		context.setPropertySourceContext(psc);
88  
89  		// Supply Spring with java classes containing annotated config
90  		context.setAnnotatedClasses(annotatedClasses);
91  
92  		// Return a Spring context configured with a single property source
93  		return context;
94  	}
95  
96  	public static SpringContext getSpringContext(Class<?> annotatedClass, ProjectContext project, List<ProjectContext> others) {
97  		return getSpringContext(CollectionUtils.asList(annotatedClass), project, others);
98  	}
99  
100 	/**
101 	 * <code>project</code> needs to be a top level project eg rice-sampleapp, olefs-webapp. <code>others</code> is projects for submodules organized into a list where the last one
102 	 * in wins.
103 	 */
104 	public static PropertySource<?> getGlobalPropertySource(ProjectContext project, ProjectContext other) {
105 		return getGlobalPropertySource(project, Arrays.asList(other));
106 	}
107 
108 	/**
109 	 * <code>project</code> needs to be a top level project eg rice-sampleapp, olefs-webapp. <code>others</code> is projects for submodules organized into a list where the last one
110 	 * in wins.
111 	 */
112 	public static PropertySource<?> getGlobalPropertySource(ProjectContext project, List<ProjectContext> others) {
113 		return getGlobalPropertySource(project, others, null);
114 	}
115 
116 	/**
117 	 * <code>project</code> needs to be a top level project eg rice-sampleapp, olefs-webapp. <code>others</code> is projects for submodules organized into a list where the last one
118 	 * in wins.
119 	 */
120 	public static PropertySource<?> getGlobalPropertySource(ProjectContext project, List<ProjectContext> others, Properties properties) {
121 
122 		ProjectProperties projectProperties = ProjectUtils.loadProjectProperties(project);
123 
124 		Properties existing = projectProperties.getPropertiesContext().getProperties();
125 		Properties combined = PropertyUtils.combine(existing, properties);
126 		projectProperties.getPropertiesContext().setProperties(combined);
127 
128 		List<ProjectProperties> otherProjectProperties = new ArrayList<ProjectProperties>();
129 		for (ProjectContext other : CollectionUtils.toEmptyList(others)) {
130 			ProjectProperties opp = ProjectUtils.loadProjectProperties(other);
131 			otherProjectProperties.add(opp);
132 		}
133 
134 		// Get a PropertySource object backed by the properties loaded from the list as well as system/environment properties
135 		return getGlobalPropertySource(projectProperties, otherProjectProperties);
136 	}
137 
138 	/**
139 	 * <code>project</code> needs to be a top level project eg rice-sampleapp, olefs-webapp. <code>others</code> is projects for submodules organized into a list where the last one
140 	 * in wins.
141 	 */
142 	public static PropertySource<?> getGlobalPropertySource(ProjectProperties project, List<ProjectProperties> others) {
143 		// Property loading uses a "last one in wins" strategy
144 		List<ProjectProperties> list = new ArrayList<ProjectProperties>();
145 
146 		// Add project properties first so they can be used to resolve locations
147 		list.add(project);
148 
149 		// Load in project properties
150 		list.addAll(others);
151 
152 		// Add project properties last so they override loaded properties
153 		list.add(project);
154 
155 		// Get a PropertySource object backed by the properties loaded from the list as well as system/environment properties
156 		return getGlobalPropertySource(GLOBAL_SPRING_PROPERTY_SOURCE_NAME, list);
157 	}
158 
159 	public static List<String> getIncludes(Environment env, String key, String defaultValue) {
160 		String includes = SpringUtils.getProperty(env, key, defaultValue);
161 		if (NullUtils.isNull(includes) || StringUtils.equals(includes, Constants.WILDCARD)) {
162 			return new ArrayList<String>();
163 		} else {
164 			return CollectionUtils.getTrimmedListFromCSV(includes);
165 		}
166 	}
167 
168 	public static List<String> getIncludes(Environment env, String key) {
169 		return getIncludes(env, key, null);
170 	}
171 
172 	public static List<String> getExcludes(Environment env, String key, String defaultValue) {
173 		String excludes = SpringUtils.getProperty(env, key, defaultValue);
174 		if (NullUtils.isNullOrNone(excludes)) {
175 			return new ArrayList<String>();
176 		} else {
177 			return CollectionUtils.getTrimmedListFromCSV(excludes);
178 		}
179 	}
180 
181 	public static List<String> getExcludes(Environment env, String key) {
182 		return getExcludes(env, key, null);
183 	}
184 
185 	/**
186 	 * Given a property holding the name of a class, return an instance of that class
187 	 */
188 	public static <T> T getInstance(Environment env, String key, Class<T> defaultValue) {
189 		String className = getProperty(env, key, defaultValue.getCanonicalName());
190 		return ReflectionUtils.newInstance(className);
191 	}
192 
193 	/**
194 	 * Given a property holding the name of a class, return an instance of that class
195 	 */
196 	public static <T> T getInstance(Environment env, String key) {
197 		String className = getProperty(env, key, null);
198 		return ReflectionUtils.newInstance(className);
199 	}
200 
201 	public static List<String> getListFromCSV(Environment env, String key, String defaultValue) {
202 		String csv = SpringUtils.getProperty(env, key, defaultValue);
203 		return CollectionUtils.getTrimmedListFromCSV(csv);
204 	}
205 
206 	@Deprecated
207 	public static List<PropertySource<?>> getPropertySources(SpringService service, Class<?> annotatedClass, String mavenPropertiesBeanName, Properties mavenProperties) {
208 		return getPropertySources(annotatedClass, mavenPropertiesBeanName, mavenProperties);
209 	}
210 
211 	public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass, String mavenPropertiesBeanName, Properties mavenProperties) {
212 		ConfigurableApplicationContext parent = null;
213 		if (mavenProperties == null) {
214 			parent = getConfigurableApplicationContext();
215 		} else {
216 			parent = getContextWithPreRegisteredBean(mavenPropertiesBeanName, mavenProperties);
217 		}
218 		AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
219 		child.setParent(parent);
220 		child.register(annotatedClass);
221 		child.refresh();
222 		return SpringUtils.getPropertySources(child);
223 	}
224 
225 	@Deprecated
226 	public static List<PropertySource<?>> getPropertySources(SpringService service, String location, String mavenPropertiesBeanName, Properties mavenProperties) {
227 		return getPropertySources(location, mavenPropertiesBeanName, mavenProperties);
228 	}
229 
230 	public static List<PropertySource<?>> getPropertySources(String location, String mavenPropertiesBeanName, Properties mavenProperties) {
231 		String[] locationsArray = { location };
232 		ConfigurableApplicationContext parent = getContextWithPreRegisteredBean(mavenPropertiesBeanName, mavenProperties);
233 		ConfigurableApplicationContext child = new ClassPathXmlApplicationContext(locationsArray, parent);
234 		return SpringUtils.getPropertySources(child);
235 	}
236 
237 	public static Executable getSpringExecutable(Environment env, boolean skip, PropertySource<?> ps, List<Class<?>> annotatedClasses) {
238 		/**
239 		 * This line creates a property source containing 100% of the properties needed by Spring to resolve any/all placeholders. It will be the only property source available to
240 		 * Spring so it needs to include system properties and environment variables
241 		 */
242 		PropertySourceContext psc = new PropertySourceContext(ps, true);
243 
244 		// Setup the Spring context
245 		SpringContext context = new SpringContext();
246 		context.setAnnotatedClasses(annotatedClasses);
247 		context.setPropertySourceContext(psc);
248 
249 		// Load the context
250 		SpringExecutable se = new SpringExecutable();
251 		se.setService(new DefaultSpringService());
252 		se.setContext(context);
253 		se.setSkip(skip);
254 		return se;
255 	}
256 
257 	public static int getInteger(Environment env, String key) {
258 		String value = getProperty(env, key);
259 		return Integer.parseInt(value);
260 	}
261 
262 	public static int getInteger(Environment env, String key, int defaultValue) {
263 		String value = getProperty(env, key, Integer.toString(defaultValue));
264 		return Integer.parseInt(value);
265 	}
266 
267 	public static long getLong(Environment env, String key) {
268 		String value = getProperty(env, key);
269 		return Long.parseLong(value);
270 	}
271 
272 	public static long getLong(Environment env, String key, long defaultValue) {
273 		String value = getProperty(env, key, Long.toString(defaultValue));
274 		return Long.parseLong(value);
275 	}
276 
277 	public static double getDouble(Environment env, String key) {
278 		String value = getProperty(env, key);
279 		return Double.parseDouble(value);
280 	}
281 
282 	public static double getDouble(Environment env, String key, double defaultValue) {
283 		String value = getProperty(env, key, Double.toString(defaultValue));
284 		return Double.parseDouble(value);
285 	}
286 
287 	/**
288 	 * Parse milliseconds from a time string that ends with a unit of measure. If no unit of measure is provided, milliseconds is assumed. Unit of measure is case insensitive.
289 	 * 
290 	 * @see FormatUtils.getMillis(String time)
291 	 */
292 	public static long getMillis(Environment env, String key, String defaultValue) {
293 		String value = getProperty(env, key, defaultValue);
294 		return FormatUtils.getMillis(value);
295 	}
296 
297 	/**
298 	 * Parse bytes from a size string that ends with a unit of measure. If no unit of measure is provided, bytes is assumed. Unit of measure is case insensitive.
299 	 * 
300 	 * @see FormatUtils.getBytes(String size)
301 	 */
302 	public static long getBytes(Environment env, String key, String defaultValue) {
303 		String value = getProperty(env, key, defaultValue);
304 		return FormatUtils.getBytes(value);
305 	}
306 
307 	/**
308 	 * Parse bytes from a size string that ends with a unit of measure. If no unit of measure is provided, bytes is assumed. Unit of measure is case insensitive.
309 	 * 
310 	 * @see FormatUtils.getBytes(String size)
311 	 */
312 	public static long getBytes(Environment env, String key) {
313 		String value = getProperty(env, key);
314 		return FormatUtils.getBytes(value);
315 	}
316 
317 	public static File getFile(Environment env, String key) {
318 		String value = getProperty(env, key);
319 		return new File(value);
320 	}
321 
322 	public static boolean getBoolean(Environment env, String key, boolean defaultValue) {
323 		String value = getProperty(env, key, Boolean.toString(defaultValue));
324 		return Boolean.parseBoolean(value);
325 	}
326 
327 	public static boolean getBoolean(Environment env, String key) {
328 		String value = getProperty(env, key);
329 		return Boolean.parseBoolean(value);
330 	}
331 
332 	public static PropertySource<?> getGlobalPropertySource(String name, List<ProjectProperties> pps) {
333 		// Load them from disk
334 		Properties source = PropertyUtils.load(pps);
335 
336 		// Add in system/environment properties
337 		Properties globalSource = PropertyUtils.getGlobalProperties(source);
338 
339 		// Prepare them so they are ready for use
340 		PropertyUtils.prepareContextProperties(globalSource);
341 
342 		// PropertyUtils.info(globalSource);
343 
344 		// Return a PropertySource backed by the properties
345 		return new PropertiesPropertySource(name, globalSource);
346 	}
347 
348 	public static PropertySource<?> getPropertySource(String name, List<ProjectProperties> pps) {
349 		// Load them from disk
350 		Properties source = PropertyUtils.load(pps);
351 
352 		// Prepare them so they are ready for use
353 		PropertyUtils.prepareContextProperties(source);
354 
355 		// Return a PropertySource backed by the properties
356 		return new PropertiesPropertySource(name, source);
357 	}
358 
359 	/**
360 	 * Converts a GAV into Spring's classpath style notation for the default project properties context.
361 	 * 
362 	 * <pre>
363 	 *  org.kuali.common:kuali-jdbc -> classpath:org/kuali/common/kuali-jdbc-properties-context.xml
364 	 * </pre>
365 	 */
366 	public static String getDefaultPropertyContextLocation(String gav) {
367 		Assert.hasText(gav, "gav has no text");
368 		Project p = ProjectUtils.getProject(gav);
369 		return "classpath:" + Str.getPath(p.getGroupId()) + "/" + p.getArtifactId() + "-properties-context.xml";
370 	}
371 
372 	/**
373 	 * Make sure all of the locations actually exist
374 	 */
375 	public static void validateExists(List<String> locations) {
376 		StringBuilder sb = new StringBuilder();
377 		for (String location : locations) {
378 			if (!LocationUtils.exists(location)) {
379 				sb.append("Location [" + location + "] does not exist\n");
380 			}
381 		}
382 		if (sb.length() > 0) {
383 			throw new IllegalArgumentException(sb.toString());
384 		}
385 	}
386 
387 	public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(String id, String displayName, List<String> beanNames, List<Object> beans) {
388 		Assert.isTrue(beanNames.size() == beans.size());
389 		GenericXmlApplicationContext appContext = new GenericXmlApplicationContext();
390 		if (!StringUtils.isBlank(id)) {
391 			appContext.setId(id);
392 		}
393 		if (!StringUtils.isBlank(displayName)) {
394 			appContext.setDisplayName(displayName);
395 		}
396 		appContext.refresh();
397 		ConfigurableListableBeanFactory factory = appContext.getBeanFactory();
398 		for (int i = 0; i < beanNames.size(); i++) {
399 			String beanName = beanNames.get(i);
400 			Object bean = beans.get(i);
401 			logger.debug("Registering bean - [{}] -> [{}]", beanName, bean.getClass().getName());
402 			factory.registerSingleton(beanName, bean);
403 		}
404 		return appContext;
405 	}
406 
407 	public static ConfigurableApplicationContext getConfigurableApplicationContext() {
408 		return new GenericXmlApplicationContext();
409 	}
410 
411 	public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(List<String> beanNames, List<Object> beans) {
412 		return getContextWithPreRegisteredBeans(null, null, beanNames, beans);
413 	}
414 
415 	/**
416 	 * Null safe refresh for a context
417 	 */
418 	public static void refreshQuietly(ConfigurableApplicationContext context) {
419 		if (context != null) {
420 			context.refresh();
421 		}
422 	}
423 
424 	/**
425 	 * Null safe close for a context
426 	 */
427 	public static void closeQuietly(ConfigurableApplicationContext context) {
428 		if (context != null) {
429 			context.close();
430 		}
431 	}
432 
433 	public static ConfigurableApplicationContext getContextWithPreRegisteredBean(String beanName, Object bean) {
434 		return getContextWithPreRegisteredBeans(Arrays.asList(beanName), Arrays.asList(bean));
435 	}
436 
437 	public static List<PropertySource<?>> getPropertySourcesFromAnnotatedClass(String annotatedClassName) {
438 		Class<?> annotatedClass = ReflectionUtils.getClass(annotatedClassName);
439 		return getPropertySources(annotatedClass);
440 	}
441 
442 	public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass) {
443 		ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(annotatedClass);
444 		return extractPropertySourcesAndClose(context);
445 	}
446 
447 	public static List<PropertySource<?>> extractPropertySourcesAndClose(ConfigurableApplicationContext context) {
448 		// Extract PropertySources (if any)
449 		List<PropertySource<?>> sources = getPropertySources(context);
450 
451 		// Close the context
452 		closeQuietly(context);
453 
454 		// Return the list
455 		return sources;
456 	}
457 
458 	/**
459 	 * Scan the XML Spring context for any beans that implement <code>PropertySource</code>
460 	 */
461 	public static List<PropertySource<?>> getPropertySources(String location) {
462 		ConfigurableApplicationContext context = new GenericXmlApplicationContext(location);
463 		return extractPropertySourcesAndClose(context);
464 	}
465 
466 	/**
467 	 * This method returns a list of any PropertySource objects registered in the indicated context. They are sorted by property source name.
468 	 */
469 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context) {
470 		// Sort them by name
471 		return getPropertySources(context, new PropertySourceNameComparator());
472 	}
473 
474 	public static <T> Map<String, T> getAllBeans(List<String> locations, Class<T> type) {
475 		String[] locationsArray = locations.toArray(new String[locations.size()]);
476 		ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(locationsArray);
477 		Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
478 		ctx.close();
479 		return map;
480 	}
481 
482 	public static <T> Map<String, T> getAllBeans(String location, Class<T> type) {
483 		ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(location);
484 		Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
485 		ctx.close();
486 		return map;
487 	}
488 
489 	public static <T> Map<String, T> getAllBeans(ConfigurableApplicationContext ctx, Class<T> type) {
490 		return BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
491 	}
492 
493 	/**
494 	 * This method returns a list of any PropertySource objects registered in the indicated context. The comparator is responsible for putting them in correct order.
495 	 */
496 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context, Comparator<PropertySource<?>> comparator) {
497 		// Extract all beans that implement the PropertySource interface
498 		@SuppressWarnings("rawtypes")
499 		Map<String, PropertySource> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, PropertySource.class);
500 
501 		// Extract the PropertySource beans into a list
502 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
503 		for (PropertySource<?> source : map.values()) {
504 			list.add(source);
505 		}
506 
507 		// Sort them using the provided comparator
508 		Collections.sort(list, comparator);
509 
510 		// Return the list
511 		return list;
512 	}
513 
514 	/**
515 	 * Null safe method for converting an untyped array of property sources into a list. Never returns null.
516 	 */
517 	public static List<PropertySource<?>> asList(PropertySource<?>... sources) {
518 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
519 		if (sources == null) {
520 			return list;
521 		}
522 		for (PropertySource<?> element : sources) {
523 			if (element != null) {
524 				list.add(element);
525 			}
526 		}
527 		return list;
528 	}
529 
530 	public static void debug(ApplicationContext ctx) {
531 		logger.debug("------------------------ Spring Context ------------------------------");
532 		logger.debug("Id: [{}]", ctx.getId());
533 		logger.debug("Display Name: [{}]", ctx.getDisplayName());
534 		logger.debug("Application Name: [{}]", ctx.getApplicationName());
535 		logger.debug("----------------------------------------------------------------------");
536 		List<String> names = Arrays.asList(BeanFactoryUtils.beanNamesIncludingAncestors(ctx));
537 		List<String> columns = Arrays.asList("Name", "Type", "Hashcode");
538 		List<Object[]> rows = new ArrayList<Object[]>();
539 		Collections.sort(names);
540 		for (String name : names) {
541 			Object bean = ctx.getBean(name);
542 			String instance = (bean == null) ? Constants.NULL : bean.getClass().getSimpleName();
543 			String hashcode = (bean == null) ? Constants.NULL : Integer.toHexString(bean.hashCode());
544 			Object[] row = { name, instance, hashcode };
545 			rows.add(row);
546 		}
547 		LoggerUtils.logTable(columns, rows, LoggerLevel.DEBUG, logger, true);
548 		logger.debug("----------------------------------------------------------------------");
549 	}
550 
551 	public static void showPropertySources(List<PropertySource<?>> propertySources) {
552 		List<String> columns = Arrays.asList("Name", "Impl", "Source");
553 		List<Object[]> rows = new ArrayList<Object[]>();
554 		for (PropertySource<?> propertySource : propertySources) {
555 			String name = propertySource.getName();
556 			String impl = propertySource.getClass().getName();
557 			String source = propertySource.getSource().getClass().getName();
558 			Object[] row = { name, impl, source };
559 			rows.add(row);
560 		}
561 		LoggerUtils.logTable(columns, rows, LoggerLevel.INFO, logger, true);
562 	}
563 
564 	public static void showPropertySources(ConfigurableEnvironment env) {
565 		showPropertySources(getPropertySources(env));
566 	}
567 
568 	/**
569 	 * Get a fully resolved property value from the environment. If the property is not found or contains unresolvable placeholders an exception is thrown.
570 	 */
571 	public static String getProperty(Environment env, String key) {
572 		String value = env.getRequiredProperty(key);
573 		return env.resolveRequiredPlaceholders(value);
574 	}
575 
576 	/**
577 	 * Return true if the environment value for key is not null.
578 	 */
579 	public static boolean exists(Environment env, String key) {
580 		return env.getProperty(key) != null;
581 	}
582 
583 	/**
584 	 * Always return a fully resolved value. Use <code>defaultValue</code> if a value cannot be located in the environment. Throw an exception if the return value contains
585 	 * unresolvable placeholders.
586 	 */
587 	public static String getProperty(Environment env, String key, String defaultValue) {
588 		if (defaultValue == null) {
589 			// No default value supplied, we must be able to locate this property in the environment
590 			return getProperty(env, key);
591 		} else {
592 			// Look up a value from the environment
593 			String value = env.getProperty(key);
594 			if (value == null) {
595 				// Resolve the default value against the environment
596 				return env.resolveRequiredPlaceholders(defaultValue);
597 			} else {
598 				// Resolve the located value against the environment
599 				return env.resolveRequiredPlaceholders(value);
600 			}
601 		}
602 	}
603 
604 	/**
605 	 * Examine <code>ConfigurableEnvironment</code> for <code>PropertySource</code>'s that extend <code>EnumerablePropertySource</code> and aggregate them into a single
606 	 * <code>Properties</code> object
607 	 */
608 	public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) {
609 
610 		// Extract the list of PropertySources from the environment
611 		List<PropertySource<?>> sources = getPropertySources(env);
612 
613 		// Spring provides PropertySource objects ordered from highest priority to lowest priority
614 		// We reverse the order here so things follow the typical "last one in wins" strategy
615 		Collections.reverse(sources);
616 
617 		// Convert the list of PropertySource's to a list of Properties objects
618 		PropertySourceConversionResult result = convertEnumerablePropertySources(sources);
619 
620 		// Combine them into a single Properties object
621 		return PropertyUtils.combine(result.getPropertiesList());
622 	}
623 
624 	/**
625 	 * Remove any existing property sources and add one property source backed by the properties passed in
626 	 */
627 	public static void reconfigurePropertySources(ConfigurableEnvironment env, String name, Properties properties) {
628 		// Remove all existing property sources
629 		removeAllPropertySources(env);
630 
631 		// MutablePropertySources allow us to manipulate the list of property sources
632 		MutablePropertySources mps = env.getPropertySources();
633 
634 		// Make sure there are no existing property sources
635 		Assert.isTrue(mps.size() == 0);
636 
637 		// Create a property source backed by the properties object passed in
638 		PropertiesPropertySource pps = new PropertiesPropertySource(name, properties);
639 
640 		// Add it to the environment
641 		mps.addFirst(pps);
642 	}
643 
644 	/**
645 	 * Remove any existing property sources
646 	 */
647 	public static void removeAllPropertySources(ConfigurableEnvironment env) {
648 		MutablePropertySources mps = env.getPropertySources();
649 		List<PropertySource<?>> sources = getPropertySources(env);
650 		for (PropertySource<?> source : sources) {
651 			String name = source.getName();
652 			mps.remove(name);
653 		}
654 	}
655 
656 	/**
657 	 * Get all PropertySource objects from the environment as a List.
658 	 */
659 	public static List<PropertySource<?>> getPropertySources(ConfigurableEnvironment env) {
660 		MutablePropertySources mps = env.getPropertySources();
661 		List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
662 		Iterator<PropertySource<?>> itr = mps.iterator();
663 		while (itr.hasNext()) {
664 			PropertySource<?> source = itr.next();
665 			sources.add(source);
666 		}
667 		return sources;
668 	}
669 
670 	/**
671 	 * Convert any PropertySources that extend EnumerablePropertySource into Properties object's
672 	 */
673 	public static PropertySourceConversionResult convertEnumerablePropertySources(List<PropertySource<?>> sources) {
674 		PropertySourceConversionResult result = new PropertySourceConversionResult();
675 		List<Properties> list = new ArrayList<Properties>();
676 		List<PropertySource<?>> converted = new ArrayList<PropertySource<?>>();
677 		List<PropertySource<?>> skipped = new ArrayList<PropertySource<?>>();
678 		// Extract property values from the sources and place them in a Properties object
679 		for (PropertySource<?> source : sources) {
680 			logger.debug("Adding [{}]", source.getName());
681 			if (source instanceof EnumerablePropertySource) {
682 				EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) source;
683 				Properties sourceProperties = convert(eps);
684 				list.add(sourceProperties);
685 				converted.add(source);
686 			} else {
687 				logger.debug("Unable to obtain properties from property source [{}] -> [{}]", source.getName(), source.getClass().getName());
688 				skipped.add(source);
689 			}
690 		}
691 		result.setConverted(converted);
692 		result.setSkipped(skipped);
693 		result.setPropertiesList(list);
694 		return result;
695 	}
696 
697 	/**
698 	 * Convert an EnumerablePropertySource into a Properties object.
699 	 */
700 	public static Properties convert(EnumerablePropertySource<?> source) {
701 		Properties properties = new Properties();
702 		String[] names = source.getPropertyNames();
703 		for (String name : names) {
704 			Object object = source.getProperty(name);
705 			if (object != null) {
706 				String value = object.toString();
707 				properties.setProperty(name, value);
708 			} else {
709 				logger.warn("Property [{}] is null", name);
710 			}
711 		}
712 		return properties;
713 	}
714 
715 	/**
716 	 * Return true if, and only if, <code>property</code> is set in the environment and evaluates to true.
717 	 */
718 	public static boolean isTrue(Environment env, String property) {
719 		String value = env.getProperty(property);
720 		if (StringUtils.isBlank(value)) {
721 			return false;
722 		} else {
723 			return new Boolean(value);
724 		}
725 	}
726 
727 }