001 /**
002 * Copyright 2010-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 org.kuali.common.util.spring;
017
018 import java.io.File;
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.Collections;
022 import java.util.Comparator;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.Properties;
027
028 import org.codehaus.plexus.util.StringUtils;
029 import org.kuali.common.util.Assert;
030 import org.kuali.common.util.CollectionUtils;
031 import org.kuali.common.util.FormatUtils;
032 import org.kuali.common.util.LocationUtils;
033 import org.kuali.common.util.LoggerLevel;
034 import org.kuali.common.util.LoggerUtils;
035 import org.kuali.common.util.Project;
036 import org.kuali.common.util.ProjectContext;
037 import org.kuali.common.util.ProjectUtils;
038 import org.kuali.common.util.PropertyUtils;
039 import org.kuali.common.util.ReflectionUtils;
040 import org.kuali.common.util.Str;
041 import org.kuali.common.util.execute.Executable;
042 import org.kuali.common.util.execute.SpringExecutable;
043 import org.kuali.common.util.nullify.NullUtils;
044 import org.kuali.common.util.property.Constants;
045 import org.kuali.common.util.property.ProjectProperties;
046 import org.kuali.common.util.property.PropertiesContext;
047 import org.kuali.common.util.service.DefaultSpringService;
048 import org.kuali.common.util.service.PropertySourceContext;
049 import org.kuali.common.util.service.SpringContext;
050 import org.kuali.common.util.service.SpringService;
051 import org.slf4j.Logger;
052 import org.slf4j.LoggerFactory;
053 import org.springframework.beans.factory.BeanFactoryUtils;
054 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
055 import org.springframework.context.ApplicationContext;
056 import org.springframework.context.ConfigurableApplicationContext;
057 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
058 import org.springframework.context.support.ClassPathXmlApplicationContext;
059 import org.springframework.context.support.GenericXmlApplicationContext;
060 import org.springframework.core.env.ConfigurableEnvironment;
061 import org.springframework.core.env.EnumerablePropertySource;
062 import org.springframework.core.env.Environment;
063 import org.springframework.core.env.MutablePropertySources;
064 import org.springframework.core.env.PropertiesPropertySource;
065 import org.springframework.core.env.PropertySource;
066
067 public class SpringUtils {
068
069 private static final Logger logger = LoggerFactory.getLogger(SpringUtils.class);
070
071 private static final String GLOBAL_SPRING_PROPERTY_SOURCE_NAME = "springPropertySource";
072
073 public static SpringContext getSpringContext(List<Class<?>> annotatedClasses, ProjectContext project, List<ProjectContext> others) {
074 // This PropertySource object is backed by a set of properties that has been
075 // 1 - fully resolved
076 // 2 - contains all properties needed by Spring
077 // 3 - contains system/environment properties where system/env properties override loaded properties
078 PropertySource<?> source = getGlobalPropertySource(project, others);
079
080 // Setup a property source context such that our single property source is the only one registered with Spring
081 // This will make it so our PropertySource is the ONLY thing used to resolve placeholders
082 PropertySourceContext psc = new PropertySourceContext(source, true);
083
084 // Setup a Spring context
085 SpringContext context = new SpringContext();
086
087 // Supply Spring with our PropertySource
088 context.setPropertySourceContext(psc);
089
090 // Supply Spring with java classes containing annotated config
091 context.setAnnotatedClasses(annotatedClasses);
092
093 // Return a Spring context configured with a single property source
094 return context;
095 }
096
097 public static SpringContext getSpringContext(Class<?> annotatedClass, ProjectContext project, List<ProjectContext> others) {
098 return getSpringContext(CollectionUtils.asList(annotatedClass), project, others);
099 }
100
101 /**
102 * <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
103 * in wins.
104 */
105 public static PropertySource<?> getGlobalPropertySource(ProjectContext project, ProjectContext other) {
106 return getGlobalPropertySource(project, Arrays.asList(other));
107 }
108
109 /**
110 * <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
111 * in wins.
112 */
113 public static PropertySource<?> getGlobalPropertySource(ProjectContext project, List<ProjectContext> others) {
114 return getGlobalPropertySource(project, others, null);
115 }
116
117 /**
118 * <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
119 * in wins.
120 */
121 public static PropertySource<?> getGlobalPropertySource(ProjectContext project, List<ProjectContext> others, Properties properties) {
122
123 ProjectProperties projectProperties = ProjectUtils.loadProjectProperties(project);
124
125 Properties existing = projectProperties.getPropertiesContext().getProperties();
126 Properties combined = PropertyUtils.combine(existing, properties);
127 projectProperties.getPropertiesContext().setProperties(combined);
128
129 List<ProjectProperties> otherProjectProperties = new ArrayList<ProjectProperties>();
130 for (ProjectContext other : CollectionUtils.toEmptyList(others)) {
131 ProjectProperties opp = ProjectUtils.loadProjectProperties(other);
132 otherProjectProperties.add(opp);
133 }
134
135 // Get a PropertySource object backed by the properties loaded from the list as well as system/environment properties
136 return getGlobalPropertySource(projectProperties, otherProjectProperties);
137 }
138
139 /**
140 * <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
141 * in wins.
142 */
143 public static PropertySource<?> getGlobalPropertySource(ProjectProperties project) {
144 return getGlobalPropertySource(project, null);
145 }
146
147 /**
148 * <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
149 * in wins.
150 */
151 public static PropertySource<?> getGlobalPropertySource(ProjectProperties project, List<ProjectProperties> others) {
152 // Property loading uses a "last one in wins" strategy
153 List<ProjectProperties> list = new ArrayList<ProjectProperties>();
154
155 // Add project properties first so they can be used to resolve locations
156 list.add(project);
157
158 if (!CollectionUtils.isEmpty(others)) {
159 // Load in other project properties
160 list.addAll(others);
161
162 // Add project properties last so they override loaded properties
163 list.add(project);
164 }
165
166 // Get a PropertySource object backed by the properties loaded from the list as well as system/environment properties
167 return getGlobalPropertySource(GLOBAL_SPRING_PROPERTY_SOURCE_NAME, list);
168 }
169
170 public static List<String> getIncludes(Environment env, String key, String defaultValue) {
171 String includes = SpringUtils.getProperty(env, key, defaultValue);
172 if (NullUtils.isNull(includes) || StringUtils.equals(includes, Constants.WILDCARD)) {
173 return new ArrayList<String>();
174 } else {
175 return CollectionUtils.getTrimmedListFromCSV(includes);
176 }
177 }
178
179 public static List<String> getIncludes(Environment env, String key) {
180 return getIncludes(env, key, null);
181 }
182
183 public static List<String> getExcludes(Environment env, String key, String defaultValue) {
184 String excludes = SpringUtils.getProperty(env, key, defaultValue);
185 if (NullUtils.isNullOrNone(excludes)) {
186 return new ArrayList<String>();
187 } else {
188 return CollectionUtils.getTrimmedListFromCSV(excludes);
189 }
190 }
191
192 public static List<String> getExcludes(Environment env, String key) {
193 return getExcludes(env, key, null);
194 }
195
196 /**
197 * Given a property holding the name of a class, return an instance of that class
198 */
199 public static <T> T getInstance(Environment env, String key, Class<T> defaultValue) {
200 String className = getProperty(env, key, defaultValue.getCanonicalName());
201 return ReflectionUtils.newInstance(className);
202 }
203
204 /**
205 * Given a property holding the name of a class, return an instance of that class
206 */
207 public static <T> T getInstance(Environment env, String key) {
208 String className = getProperty(env, key, null);
209 return ReflectionUtils.newInstance(className);
210 }
211
212 public static List<String> getListFromCSV(Environment env, String key, String defaultValue) {
213 String csv = SpringUtils.getProperty(env, key, defaultValue);
214 return CollectionUtils.getTrimmedListFromCSV(csv);
215 }
216
217 @Deprecated
218 public static List<PropertySource<?>> getPropertySources(SpringService service, Class<?> annotatedClass, String propertiesBeanName, Properties properties) {
219 return getPropertySources(annotatedClass, propertiesBeanName, properties);
220 }
221
222 /**
223 * Scan the annotated class to find the single bean registered in the context that implements <code>PropertySource</code>. If more than one bean is located, throw
224 * <code>IllegalStateException</code>.
225 */
226 public static PropertySource<?> getSinglePropertySource(Class<?> annotatedClass) {
227 return getSinglePropertySource(annotatedClass, null, null);
228 }
229
230 /**
231 * Scan the annotated class to find the single bean registered in the context that implements <code>PropertySource</code>. If more than one bean is located, throw
232 * <code>IllegalStateException</code>.
233 */
234 public static PropertySource<?> getSinglePropertySource(Class<?> annotatedClass, String propertiesBeanName, Properties properties) {
235 List<PropertySource<?>> sources = getPropertySources(annotatedClass, propertiesBeanName, properties);
236 if (sources.size() > 1) {
237 throw new IllegalStateException("More than one PropertySource was registered in the context");
238 } else {
239 return sources.get(0);
240 }
241 }
242
243 public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass, String propertiesBeanName, Properties properties) {
244 ConfigurableApplicationContext parent = null;
245 if (properties == null) {
246 parent = getConfigurableApplicationContext();
247 } else {
248 parent = getContextWithPreRegisteredBean(propertiesBeanName, properties);
249 }
250 AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
251 child.setParent(parent);
252 child.register(annotatedClass);
253 child.refresh();
254 return getPropertySources(child);
255 }
256
257 @Deprecated
258 public static List<PropertySource<?>> getPropertySources(SpringService service, String location, String mavenPropertiesBeanName, Properties mavenProperties) {
259 return getPropertySources(location, mavenPropertiesBeanName, mavenProperties);
260 }
261
262 public static List<PropertySource<?>> getPropertySources(String location, String mavenPropertiesBeanName, Properties mavenProperties) {
263 String[] locationsArray = { location };
264 ConfigurableApplicationContext parent = getContextWithPreRegisteredBean(mavenPropertiesBeanName, mavenProperties);
265 ConfigurableApplicationContext child = new ClassPathXmlApplicationContext(locationsArray, parent);
266 return SpringUtils.getPropertySources(child);
267 }
268
269 public static Executable getSpringExecutable(Environment env, boolean skip, PropertySource<?> ps, List<Class<?>> annotatedClasses) {
270 /**
271 * 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
272 * Spring so it needs to include system properties and environment variables
273 */
274 PropertySourceContext psc = new PropertySourceContext(ps, true);
275
276 // Setup the Spring context
277 SpringContext context = new SpringContext();
278 context.setAnnotatedClasses(annotatedClasses);
279 context.setPropertySourceContext(psc);
280
281 // Load the context
282 SpringExecutable se = new SpringExecutable();
283 se.setService(new DefaultSpringService());
284 se.setContext(context);
285 se.setSkip(skip);
286 return se;
287 }
288
289 public static int getInteger(Environment env, String key) {
290 String value = getProperty(env, key);
291 return Integer.parseInt(value);
292 }
293
294 public static int getInteger(Environment env, String key, int defaultValue) {
295 String value = getProperty(env, key, Integer.toString(defaultValue));
296 return Integer.parseInt(value);
297 }
298
299 public static long getLong(Environment env, String key) {
300 String value = getProperty(env, key);
301 return Long.parseLong(value);
302 }
303
304 public static long getLong(Environment env, String key, long defaultValue) {
305 String value = getProperty(env, key, Long.toString(defaultValue));
306 return Long.parseLong(value);
307 }
308
309 public static double getDouble(Environment env, String key) {
310 String value = getProperty(env, key);
311 return Double.parseDouble(value);
312 }
313
314 public static double getDouble(Environment env, String key, double defaultValue) {
315 String value = getProperty(env, key, Double.toString(defaultValue));
316 return Double.parseDouble(value);
317 }
318
319 /**
320 * 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.
321 *
322 * @see FormatUtils.getMillis(String time)
323 */
324 public static long getMillis(Environment env, String key, String defaultValue) {
325 String value = getProperty(env, key, defaultValue);
326 return FormatUtils.getMillis(value);
327 }
328
329 /**
330 * 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.
331 *
332 * @see FormatUtils.getBytes(String size)
333 */
334 public static long getBytes(Environment env, String key, String defaultValue) {
335 String value = getProperty(env, key, defaultValue);
336 return FormatUtils.getBytes(value);
337 }
338
339 /**
340 * 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.
341 *
342 * @see FormatUtils.getBytes(String size)
343 */
344 public static long getBytes(Environment env, String key) {
345 String value = getProperty(env, key);
346 return FormatUtils.getBytes(value);
347 }
348
349 public static File getFile(Environment env, String key) {
350 String value = getProperty(env, key);
351 return new File(value);
352 }
353
354 public static boolean getBoolean(Environment env, String key, boolean defaultValue) {
355 String value = getProperty(env, key, Boolean.toString(defaultValue));
356 return Boolean.parseBoolean(value);
357 }
358
359 public static boolean getBoolean(Environment env, String key) {
360 String value = getProperty(env, key);
361 return Boolean.parseBoolean(value);
362 }
363
364 public static PropertySource<?> getGlobalPropertySource(String name, List<ProjectProperties> pps) {
365 // Load them from disk
366 Properties source = PropertyUtils.load(pps);
367
368 // Add in system/environment properties
369 Properties globalSource = PropertyUtils.getGlobalProperties(source);
370
371 logger.debug("Before prepareContextProperties()");
372 PropertyUtils.debug(globalSource);
373
374 // Prepare them so they are ready for use
375 PropertyUtils.prepareContextProperties(globalSource);
376
377 logger.debug("After prepareContextProperties()");
378 PropertyUtils.debug(globalSource);
379
380 // Return a PropertySource backed by the properties
381 return new PropertiesPropertySource(name, globalSource);
382 }
383
384 /**
385 * Return a SpringContext that resolves all placeholders from the PropertySource passed in
386 */
387 public static PropertySource<?> getGlobalPropertySource(List<String> locations, String encoding) {
388 Properties loaded = PropertyUtils.load(locations, encoding);
389 Properties global = PropertyUtils.getGlobalProperties(loaded);
390 PropertyUtils.prepareContextProperties(global);
391 return new PropertiesPropertySource(GLOBAL_SPRING_PROPERTY_SOURCE_NAME, global);
392 }
393
394 /**
395 * Return a SpringContext that resolves all placeholders from the list of property locations passed in + System/Environment properties
396 */
397 public static SpringContext getSinglePropertySourceContext(ProjectContext context, String location) {
398 PropertySource<?> source = getGlobalPropertySource(context, location);
399 return getSinglePropertySourceContext(source);
400 }
401
402 /**
403 * Return a SpringExecutable for the project, properties location, and config passed in.
404 */
405 public static SpringExecutable getSpringExecutable(ProjectContext project, String location, List<Class<?>> annotatedClasses) {
406 SpringContext context = getSinglePropertySourceContext(project, location);
407 context.setAnnotatedClasses(annotatedClasses);
408
409 SpringExecutable executable = new SpringExecutable();
410 executable.setContext(context);
411 return executable;
412 }
413
414 /**
415 * Return a SpringExecutable for the project, properties location, and config passed in.
416 */
417 public static SpringExecutable getSpringExecutable(ProjectContext project, String location, Class<?> annotatedClass) {
418 List<Class<?>> classes = new ArrayList<Class<?>>();
419 classes.add(annotatedClass);
420
421 return getSpringExecutable(project, location, classes);
422 }
423
424 /**
425 * Return a SpringContext that resolves all placeholders from the list of property locations passed in + System/Environment properties
426 */
427 public static SpringContext getSinglePropertySourceContext(List<String> locations, String encoding) {
428 PropertySource<?> source = getGlobalPropertySource(locations, encoding);
429 return getSinglePropertySourceContext(source);
430 }
431
432 /**
433 * Return a SpringContext that resolves all placeholders from the PropertySource passed in
434 */
435 public static SpringContext getSinglePropertySourceContext(PropertySource<?> source) {
436 // Setup a property source context such that our single property source is the only one registered with Spring
437 // This will make it so our PropertySource is the ONLY thing used to resolve placeholders
438 PropertySourceContext psc = new PropertySourceContext(source, true);
439
440 // Setup a Spring context
441 SpringContext context = new SpringContext();
442
443 // Supply Spring with our PropertySource
444 context.setPropertySourceContext(psc);
445
446 // Return a Spring context configured with a single property source
447 return context;
448 }
449
450 public static PropertySource<?> getGlobalPropertySource(ProjectContext context, String... locations) {
451 ProjectProperties pp = ProjectUtils.loadProjectProperties(context);
452 PropertiesContext pc = pp.getPropertiesContext();
453 List<String> existingLocations = CollectionUtils.toEmptyList(pc.getLocations());
454 if (locations != null) {
455 for (String location : locations) {
456 existingLocations.add(location);
457 }
458 }
459 pc.setLocations(existingLocations);
460 return getGlobalPropertySource(pp);
461 }
462
463 public static PropertySource<?> getPropertySource(String name, List<ProjectProperties> pps) {
464 // Load them from disk
465 Properties source = PropertyUtils.load(pps);
466
467 // Prepare them so they are ready for use
468 PropertyUtils.prepareContextProperties(source);
469
470 // Return a PropertySource backed by the properties
471 return new PropertiesPropertySource(name, source);
472 }
473
474 /**
475 * Converts a GAV into Spring's classpath style notation for the default project properties context.
476 *
477 * <pre>
478 * org.kuali.common:kuali-jdbc -> classpath:org/kuali/common/kuali-jdbc-properties-context.xml
479 * </pre>
480 */
481 public static String getDefaultPropertyContextLocation(String gav) {
482 Assert.hasText(gav, "gav has no text");
483 Project p = ProjectUtils.getProject(gav);
484 return "classpath:" + Str.getPath(p.getGroupId()) + "/" + p.getArtifactId() + "-properties-context.xml";
485 }
486
487 /**
488 * Make sure all of the locations actually exist
489 */
490 public static void validateExists(List<String> locations) {
491 StringBuilder sb = new StringBuilder();
492 for (String location : locations) {
493 if (!LocationUtils.exists(location)) {
494 sb.append("Location [" + location + "] does not exist\n");
495 }
496 }
497 if (sb.length() > 0) {
498 throw new IllegalArgumentException(sb.toString());
499 }
500 }
501
502 public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(String id, String displayName, List<String> beanNames, List<Object> beans) {
503 Assert.isTrue(beanNames.size() == beans.size());
504 GenericXmlApplicationContext appContext = new GenericXmlApplicationContext();
505 if (!StringUtils.isBlank(id)) {
506 appContext.setId(id);
507 }
508 if (!StringUtils.isBlank(displayName)) {
509 appContext.setDisplayName(displayName);
510 }
511 appContext.refresh();
512 ConfigurableListableBeanFactory factory = appContext.getBeanFactory();
513 for (int i = 0; i < beanNames.size(); i++) {
514 String beanName = beanNames.get(i);
515 Object bean = beans.get(i);
516 logger.debug("Registering bean - [{}] -> [{}]", beanName, bean.getClass().getName());
517 factory.registerSingleton(beanName, bean);
518 }
519 return appContext;
520 }
521
522 public static ConfigurableApplicationContext getConfigurableApplicationContext() {
523 return new GenericXmlApplicationContext();
524 }
525
526 public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(List<String> beanNames, List<Object> beans) {
527 return getContextWithPreRegisteredBeans(null, null, beanNames, beans);
528 }
529
530 /**
531 * Null safe refresh for a context
532 */
533 public static void refreshQuietly(ConfigurableApplicationContext context) {
534 if (context != null) {
535 context.refresh();
536 }
537 }
538
539 /**
540 * Null safe close for a context
541 */
542 public static void closeQuietly(ConfigurableApplicationContext context) {
543 if (context != null) {
544 context.close();
545 }
546 }
547
548 public static ConfigurableApplicationContext getContextWithPreRegisteredBean(String beanName, Object bean) {
549 return getContextWithPreRegisteredBeans(Arrays.asList(beanName), Arrays.asList(bean));
550 }
551
552 public static List<PropertySource<?>> getPropertySourcesFromAnnotatedClass(String annotatedClassName) {
553 Class<?> annotatedClass = ReflectionUtils.getClass(annotatedClassName);
554 return getPropertySources(annotatedClass);
555 }
556
557 public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass) {
558 ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(annotatedClass);
559 return extractPropertySourcesAndClose(context);
560 }
561
562 public static List<PropertySource<?>> extractPropertySourcesAndClose(ConfigurableApplicationContext context) {
563 // Extract PropertySources (if any)
564 List<PropertySource<?>> sources = getPropertySources(context);
565
566 // Close the context
567 closeQuietly(context);
568
569 // Return the list
570 return sources;
571 }
572
573 /**
574 * Scan the XML Spring context for any beans that implement <code>PropertySource</code>
575 */
576 public static List<PropertySource<?>> getPropertySources(String location) {
577 ConfigurableApplicationContext context = new GenericXmlApplicationContext(location);
578 return extractPropertySourcesAndClose(context);
579 }
580
581 /**
582 * This method returns a list of any PropertySource objects registered in the indicated context. They are sorted by property source name.
583 */
584 public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context) {
585 // Sort them by name
586 return getPropertySources(context, new PropertySourceNameComparator());
587 }
588
589 public static <T> Map<String, T> getAllBeans(List<String> locations, Class<T> type) {
590 String[] locationsArray = locations.toArray(new String[locations.size()]);
591 ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(locationsArray);
592 Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
593 ctx.close();
594 return map;
595 }
596
597 public static <T> Map<String, T> getAllBeans(String location, Class<T> type) {
598 ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(location);
599 Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
600 ctx.close();
601 return map;
602 }
603
604 public static <T> Map<String, T> getAllBeans(ConfigurableApplicationContext ctx, Class<T> type) {
605 return BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
606 }
607
608 /**
609 * This method returns a list of any PropertySource objects registered in the indicated context. The comparator is responsible for putting them in correct order.
610 */
611 public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context, Comparator<PropertySource<?>> comparator) {
612 // Extract all beans that implement the PropertySource interface
613 @SuppressWarnings("rawtypes")
614 Map<String, PropertySource> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, PropertySource.class);
615
616 // Extract the PropertySource beans into a list
617 List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
618 for (PropertySource<?> source : map.values()) {
619 list.add(source);
620 }
621
622 // Sort them using the provided comparator
623 Collections.sort(list, comparator);
624
625 // Return the list
626 return list;
627 }
628
629 /**
630 * Null safe method for converting an untyped array of property sources into a list. Never returns null.
631 */
632 public static List<PropertySource<?>> asList(PropertySource<?>... sources) {
633 List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
634 if (sources == null) {
635 return list;
636 }
637 for (PropertySource<?> element : sources) {
638 if (element != null) {
639 list.add(element);
640 }
641 }
642 return list;
643 }
644
645 public static void debug(ApplicationContext ctx) {
646 logger.debug("------------------------ Spring Context ------------------------------");
647 logger.debug("Id: [{}]", ctx.getId());
648 logger.debug("Display Name: [{}]", ctx.getDisplayName());
649 logger.debug("Application Name: [{}]", ctx.getApplicationName());
650 logger.debug("----------------------------------------------------------------------");
651 List<String> names = Arrays.asList(BeanFactoryUtils.beanNamesIncludingAncestors(ctx));
652 List<String> columns = Arrays.asList("Name", "Type", "Hashcode");
653 List<Object[]> rows = new ArrayList<Object[]>();
654 Collections.sort(names);
655 for (String name : names) {
656 Object bean = ctx.getBean(name);
657 String instance = (bean == null) ? Constants.NULL : bean.getClass().getSimpleName();
658 String hashcode = (bean == null) ? Constants.NULL : Integer.toHexString(bean.hashCode());
659 Object[] row = { name, instance, hashcode };
660 rows.add(row);
661 }
662 LoggerUtils.logTable(columns, rows, LoggerLevel.DEBUG, logger, true);
663 logger.debug("----------------------------------------------------------------------");
664 }
665
666 public static void showPropertySources(List<PropertySource<?>> propertySources) {
667 List<String> columns = Arrays.asList("Name", "Impl", "Source");
668 List<Object[]> rows = new ArrayList<Object[]>();
669 for (PropertySource<?> propertySource : propertySources) {
670 String name = propertySource.getName();
671 String impl = propertySource.getClass().getName();
672 String source = propertySource.getSource().getClass().getName();
673 Object[] row = { name, impl, source };
674 rows.add(row);
675 }
676 LoggerUtils.logTable(columns, rows, LoggerLevel.INFO, logger, true);
677 }
678
679 public static void showPropertySources(ConfigurableEnvironment env) {
680 showPropertySources(getPropertySources(env));
681 }
682
683 /**
684 * Get a fully resolved property value from the environment. If the property is not found or contains unresolvable placeholders an exception is thrown.
685 */
686 public static String getProperty(Environment env, String key) {
687 String value = env.getRequiredProperty(key);
688 return env.resolveRequiredPlaceholders(value);
689 }
690
691 /**
692 * Return true if the environment value for key is not null.
693 */
694 public static boolean exists(Environment env, String key) {
695 return env.getProperty(key) != null;
696 }
697
698 /**
699 * 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
700 * unresolvable placeholders.
701 */
702 public static String getProperty(Environment env, String key, String defaultValue) {
703 if (defaultValue == null) {
704 // No default value supplied, we must be able to locate this property in the environment
705 return getProperty(env, key);
706 } else {
707 // Look up a value from the environment
708 String value = env.getProperty(key);
709 if (value == null) {
710 // Resolve the default value against the environment
711 return env.resolveRequiredPlaceholders(defaultValue);
712 } else {
713 // Resolve the located value against the environment
714 return env.resolveRequiredPlaceholders(value);
715 }
716 }
717 }
718
719 /**
720 * Examine <code>ConfigurableEnvironment</code> for <code>PropertySource</code>'s that extend <code>EnumerablePropertySource</code> and aggregate them into a single
721 * <code>Properties</code> object
722 */
723 public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) {
724
725 // Extract the list of PropertySources from the environment
726 List<PropertySource<?>> sources = getPropertySources(env);
727
728 // Spring provides PropertySource objects ordered from highest priority to lowest priority
729 // We reverse the order here so things follow the typical "last one in wins" strategy
730 Collections.reverse(sources);
731
732 // Convert the list of PropertySource's to a list of Properties objects
733 PropertySourceConversionResult result = convertEnumerablePropertySources(sources);
734
735 // Combine them into a single Properties object
736 return PropertyUtils.combine(result.getPropertiesList());
737 }
738
739 /**
740 * Remove any existing property sources and add one property source backed by the properties passed in
741 */
742 public static void reconfigurePropertySources(ConfigurableEnvironment env, String name, Properties properties) {
743 // Remove all existing property sources
744 removeAllPropertySources(env);
745
746 // MutablePropertySources allow us to manipulate the list of property sources
747 MutablePropertySources mps = env.getPropertySources();
748
749 // Make sure there are no existing property sources
750 Assert.isTrue(mps.size() == 0);
751
752 // Create a property source backed by the properties object passed in
753 PropertiesPropertySource pps = new PropertiesPropertySource(name, properties);
754
755 // Add it to the environment
756 mps.addFirst(pps);
757 }
758
759 /**
760 * Remove any existing property sources
761 */
762 public static void removeAllPropertySources(ConfigurableEnvironment env) {
763 MutablePropertySources mps = env.getPropertySources();
764 List<PropertySource<?>> sources = getPropertySources(env);
765 for (PropertySource<?> source : sources) {
766 String name = source.getName();
767 mps.remove(name);
768 }
769 }
770
771 /**
772 * Get all PropertySource objects from the environment as a List.
773 */
774 public static List<PropertySource<?>> getPropertySources(ConfigurableEnvironment env) {
775 MutablePropertySources mps = env.getPropertySources();
776 List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
777 Iterator<PropertySource<?>> itr = mps.iterator();
778 while (itr.hasNext()) {
779 PropertySource<?> source = itr.next();
780 sources.add(source);
781 }
782 return sources;
783 }
784
785 /**
786 * Convert any PropertySources that extend EnumerablePropertySource into Properties object's
787 */
788 public static PropertySourceConversionResult convertEnumerablePropertySources(List<PropertySource<?>> sources) {
789 PropertySourceConversionResult result = new PropertySourceConversionResult();
790 List<Properties> list = new ArrayList<Properties>();
791 List<PropertySource<?>> converted = new ArrayList<PropertySource<?>>();
792 List<PropertySource<?>> skipped = new ArrayList<PropertySource<?>>();
793 // Extract property values from the sources and place them in a Properties object
794 for (PropertySource<?> source : sources) {
795 logger.debug("Adding [{}]", source.getName());
796 if (source instanceof EnumerablePropertySource) {
797 EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) source;
798 Properties sourceProperties = convert(eps);
799 list.add(sourceProperties);
800 converted.add(source);
801 } else {
802 logger.debug("Unable to obtain properties from property source [{}] -> [{}]", source.getName(), source.getClass().getName());
803 skipped.add(source);
804 }
805 }
806 result.setConverted(converted);
807 result.setSkipped(skipped);
808 result.setPropertiesList(list);
809 return result;
810 }
811
812 /**
813 * Convert an EnumerablePropertySource into a Properties object.
814 */
815 public static Properties convert(EnumerablePropertySource<?> source) {
816 Properties properties = new Properties();
817 String[] names = source.getPropertyNames();
818 for (String name : names) {
819 Object object = source.getProperty(name);
820 if (object != null) {
821 String value = object.toString();
822 properties.setProperty(name, value);
823 } else {
824 logger.warn("Property [{}] is null", name);
825 }
826 }
827 return properties;
828 }
829
830 /**
831 * Return true if, and only if, <code>property</code> is set in the environment and evaluates to true.
832 */
833 public static boolean isTrue(Environment env, String property) {
834 String value = env.getProperty(property);
835 if (StringUtils.isBlank(value)) {
836 return false;
837 } else {
838 return new Boolean(value);
839 }
840 }
841
842 }