View Javadoc

1   package org.kuali.common.util.spring;
2   
3   import java.util.ArrayList;
4   import java.util.Arrays;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Properties;
11  
12  import org.codehaus.plexus.util.StringUtils;
13  import org.kuali.common.util.Assert;
14  import org.kuali.common.util.LocationUtils;
15  import org.kuali.common.util.LoggerLevel;
16  import org.kuali.common.util.LoggerUtils;
17  import org.kuali.common.util.Project;
18  import org.kuali.common.util.ProjectUtils;
19  import org.kuali.common.util.PropertyUtils;
20  import org.kuali.common.util.Str;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  import org.springframework.beans.factory.BeanFactoryUtils;
24  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
25  import org.springframework.context.ConfigurableApplicationContext;
26  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
27  import org.springframework.context.support.GenericXmlApplicationContext;
28  import org.springframework.core.env.ConfigurableEnvironment;
29  import org.springframework.core.env.EnumerablePropertySource;
30  import org.springframework.core.env.Environment;
31  import org.springframework.core.env.MutablePropertySources;
32  import org.springframework.core.env.PropertiesPropertySource;
33  import org.springframework.core.env.PropertySource;
34  
35  public class SpringUtils {
36  
37  	private static final Logger logger = LoggerFactory.getLogger(SpringUtils.class);
38  
39  	/**
40  	 * Converts a GAV into Spring's classpath style notation for the default project properties context.
41  	 * 
42  	 * <pre>
43  	 *  org.kuali.common:kuali-jdbc -> classpath:org/kuali/common/kuali-jdbc-properties-context.xml
44  	 * </pre>
45  	 */
46  	public static String getDefaultPropertyContextLocation(String gav) {
47  		Assert.hasText(gav, "gav has no text");
48  		Project p = ProjectUtils.getProject(gav);
49  		return "classpath:" + Str.getPath(p.getGroupId()) + "/" + p.getArtifactId() + "-properties-context.xml";
50  	}
51  
52  	/**
53  	 * Make sure all of the locations actually exist
54  	 */
55  	public static void validateExists(List<String> locations) {
56  		StringBuilder sb = new StringBuilder();
57  		for (String location : locations) {
58  			if (!LocationUtils.exists(location)) {
59  				sb.append("Location [" + location + "] does not exist\n");
60  			}
61  		}
62  		if (sb.length() > 0) {
63  			throw new IllegalArgumentException(sb.toString());
64  		}
65  	}
66  
67  	public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(List<String> beanNames, List<Object> beans) {
68  		Assert.isTrue(beanNames.size() == beans.size());
69  		GenericXmlApplicationContext appContext = new GenericXmlApplicationContext();
70  		appContext.refresh();
71  		ConfigurableListableBeanFactory factory = appContext.getBeanFactory();
72  		for (int i = 0; i < beanNames.size(); i++) {
73  			String beanName = beanNames.get(i);
74  			Object bean = beans.get(i);
75  			logger.debug("Registering bean - [{}] -> [{}]", beanName, bean.getClass().getName());
76  			factory.registerSingleton(beanName, bean);
77  		}
78  		return appContext;
79  	}
80  
81  	/**
82  	 * Null safe refresh for a context
83  	 */
84  	public static void refreshQuietly(ConfigurableApplicationContext context) {
85  		if (context != null) {
86  			context.refresh();
87  		}
88  	}
89  
90  	/**
91  	 * Null safe close for a context
92  	 */
93  	public static void closeQuietly(ConfigurableApplicationContext context) {
94  		if (context != null) {
95  			context.close();
96  		}
97  	}
98  
99  	public static ConfigurableApplicationContext getContextWithPreRegisteredBean(String beanName, Object bean) {
100 		return getContextWithPreRegisteredBeans(Arrays.asList(beanName), Arrays.asList(bean));
101 	}
102 
103 	public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass) {
104 		ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(annotatedClass);
105 		return extractPropertySourcesAndClose(context);
106 	}
107 
108 	public static List<PropertySource<?>> extractPropertySourcesAndClose(ConfigurableApplicationContext context) {
109 		// Extract PropertySources (if any)
110 		List<PropertySource<?>> sources = getPropertySources(context);
111 
112 		// Close the context
113 		closeQuietly(context);
114 
115 		// Return the list
116 		return sources;
117 	}
118 
119 	/**
120 	 * Scan the XML Spring context for any beans that implement <code>PropertySource</code>
121 	 */
122 	public static List<PropertySource<?>> getPropertySources(String location) {
123 		ConfigurableApplicationContext context = new GenericXmlApplicationContext(location);
124 		return extractPropertySourcesAndClose(context);
125 	}
126 
127 	/**
128 	 * This method returns a list of any PropertySource objects registered in the indicated context. They are sorted by property source name.
129 	 */
130 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context) {
131 		// Sort them by name
132 		return getPropertySources(context, new PropertySourceNameComparator());
133 	}
134 
135 	public static <T> Map<String, T> getAllBeans(List<String> locations, Class<T> type) {
136 		String[] locationsArray = locations.toArray(new String[locations.size()]);
137 		ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(locationsArray);
138 		Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
139 		ctx.close();
140 		return map;
141 	}
142 
143 	public static <T> Map<String, T> getAllBeans(String location, Class<T> type) {
144 		ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(location);
145 		Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
146 		ctx.close();
147 		return map;
148 	}
149 
150 	public static <T> Map<String, T> getAllBeans(ConfigurableApplicationContext ctx, Class<T> type) {
151 		return BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type);
152 	}
153 
154 	/**
155 	 * This method returns a list of any PropertySource objects registered in the indicated context. The comparator is responsible for putting them in correct order.
156 	 */
157 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context, Comparator<PropertySource<?>> comparator) {
158 		// Extract all beans that implement the PropertySource interface
159 		@SuppressWarnings("rawtypes")
160 		Map<String, PropertySource> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, PropertySource.class);
161 
162 		// Extract the PropertySource beans into a list
163 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
164 		for (PropertySource<?> source : map.values()) {
165 			list.add(source);
166 		}
167 
168 		// Sort them using the provided comparator
169 		Collections.sort(list, comparator);
170 
171 		// Return the list
172 		return list;
173 	}
174 
175 	/**
176 	 * Null safe method for converting an untyped array of property sources into a list. Never returns null.
177 	 */
178 	public static List<PropertySource<?>> asList(PropertySource<?>... sources) {
179 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
180 		if (sources == null) {
181 			return list;
182 		}
183 		for (PropertySource<?> element : sources) {
184 			if (element != null) {
185 				list.add(element);
186 			}
187 		}
188 		return list;
189 	}
190 
191 	public static void showPropertySources(List<PropertySource<?>> propertySources) {
192 		List<String> columns = Arrays.asList("Name", "Impl", "Source");
193 		List<Object[]> rows = new ArrayList<Object[]>();
194 		for (PropertySource<?> propertySource : propertySources) {
195 			String name = propertySource.getName();
196 			String impl = propertySource.getClass().getName();
197 			String source = propertySource.getSource().getClass().getName();
198 			Object[] row = { name, impl, source };
199 			rows.add(row);
200 		}
201 		LoggerUtils.logTable(columns, rows, LoggerLevel.INFO, logger, true);
202 	}
203 
204 	public static void showPropertySources(ConfigurableEnvironment env) {
205 		showPropertySources(getPropertySources(env));
206 	}
207 
208 	/**
209 	 * Get a fully resolved property value from the environment. If the property is not found or contains unresolvable placeholders an exception is thrown.
210 	 */
211 	public static String getProperty(Environment env, String key) {
212 		String value = env.getRequiredProperty(key);
213 		return env.resolveRequiredPlaceholders(value);
214 	}
215 
216 	/**
217 	 * Examine <code>ConfigurableEnvironment</code> for <code>PropertySource</code>'s that extend <code>EnumerablePropertySource</code> and aggregate them into a single
218 	 * <code>Properties</code> object
219 	 */
220 	public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) {
221 
222 		// Extract the list of PropertySources from the environment
223 		List<PropertySource<?>> sources = getPropertySources(env);
224 
225 		// Spring provides PropertySource objects ordered from highest priority to lowest priority
226 		// We reverse the order here so things follow the typical "last one in wins" strategy
227 		Collections.reverse(sources);
228 
229 		// Convert the list of PropertySource's to a list of Properties objects
230 		PropertySourceConversionResult result = convertEnumerablePropertySources(sources);
231 
232 		// Combine them into a single Properties object
233 		return PropertyUtils.combine(result.getPropertiesList());
234 	}
235 
236 	/**
237 	 * Remove any existing property sources and add one property source backed by the properties passed in
238 	 */
239 	public static void reconfigurePropertySources(ConfigurableEnvironment env, String name, Properties properties) {
240 		// Remove all existing property sources
241 		removeAllPropertySources(env);
242 
243 		// MutablePropertySources allow us to manipulate the list of property sources
244 		MutablePropertySources mps = env.getPropertySources();
245 
246 		// Make sure there are no existing property sources
247 		Assert.isTrue(mps.size() == 0);
248 
249 		// Create a property source backed by the properties object passed in
250 		PropertiesPropertySource pps = new PropertiesPropertySource(name, properties);
251 
252 		// Add it to the environment
253 		mps.addFirst(pps);
254 	}
255 
256 	/**
257 	 * Remove any existing property sources
258 	 */
259 	public static void removeAllPropertySources(ConfigurableEnvironment env) {
260 		MutablePropertySources mps = env.getPropertySources();
261 		List<PropertySource<?>> sources = getPropertySources(env);
262 		for (PropertySource<?> source : sources) {
263 			String name = source.getName();
264 			mps.remove(name);
265 		}
266 	}
267 
268 	/**
269 	 * Get all PropertySource objects from the environment as a List.
270 	 */
271 	public static List<PropertySource<?>> getPropertySources(ConfigurableEnvironment env) {
272 		MutablePropertySources mps = env.getPropertySources();
273 		List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
274 		Iterator<PropertySource<?>> itr = mps.iterator();
275 		while (itr.hasNext()) {
276 			PropertySource<?> source = itr.next();
277 			sources.add(source);
278 		}
279 		return sources;
280 	}
281 
282 	/**
283 	 * Convert any PropertySources that extend EnumerablePropertySource into Properties object's
284 	 */
285 	public static PropertySourceConversionResult convertEnumerablePropertySources(List<PropertySource<?>> sources) {
286 		PropertySourceConversionResult result = new PropertySourceConversionResult();
287 		List<Properties> list = new ArrayList<Properties>();
288 		List<PropertySource<?>> converted = new ArrayList<PropertySource<?>>();
289 		List<PropertySource<?>> skipped = new ArrayList<PropertySource<?>>();
290 		// Extract property values from the sources and place them in a Properties object
291 		for (PropertySource<?> source : sources) {
292 			logger.debug("Adding [{}]", source.getName());
293 			if (source instanceof EnumerablePropertySource) {
294 				EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) source;
295 				Properties sourceProperties = convert(eps);
296 				list.add(sourceProperties);
297 				converted.add(source);
298 			} else {
299 				logger.debug("Unable to obtain properties from property source [{}] -> [{}]", source.getName(), source.getClass().getName());
300 				skipped.add(source);
301 			}
302 		}
303 		result.setConverted(converted);
304 		result.setSkipped(skipped);
305 		result.setPropertiesList(list);
306 		return result;
307 	}
308 
309 	/**
310 	 * Convert an EnumerablePropertySource into a Properties object.
311 	 */
312 	public static Properties convert(EnumerablePropertySource<?> source) {
313 		Properties properties = new Properties();
314 		String[] names = source.getPropertyNames();
315 		for (String name : names) {
316 			Object object = source.getProperty(name);
317 			if (object != null) {
318 				String value = object.toString();
319 				properties.setProperty(name, value);
320 			} else {
321 				logger.warn("Property [{}] is null", name);
322 			}
323 		}
324 		return properties;
325 	}
326 
327 	/**
328 	 * Return true if, and only if, <code>property</code> is set in the environment and evaluates to true.
329 	 */
330 	public static boolean isTrue(Environment env, String property) {
331 		String value = env.getProperty(property);
332 		if (StringUtils.isBlank(value)) {
333 			return false;
334 		} else {
335 			return new Boolean(value);
336 		}
337 	}
338 
339 }