View Javadoc
1   /**
2    * Copyright 2010-2014 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.util.ArrayList;
19  import java.util.Collections;
20  import java.util.Comparator;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Properties;
25  
26  import org.kuali.common.util.Assert;
27  import org.kuali.common.util.PropertyUtils;
28  import org.kuali.common.util.log.LoggerUtils;
29  import org.kuali.common.util.properties.Location;
30  import org.kuali.common.util.properties.PropertiesService;
31  import org.kuali.common.util.property.ImmutableProperties;
32  import org.kuali.common.util.spring.service.PropertySourceContext;
33  import org.kuali.common.util.spring.service.SpringContext;
34  import org.slf4j.Logger;
35  import org.springframework.beans.factory.BeanFactoryUtils;
36  import org.springframework.context.ConfigurableApplicationContext;
37  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
38  import org.springframework.core.env.ConfigurableEnvironment;
39  import org.springframework.core.env.EnumerablePropertySource;
40  import org.springframework.core.env.MutablePropertySources;
41  import org.springframework.core.env.PropertiesPropertySource;
42  import org.springframework.core.env.PropertySource;
43  
44  import com.google.common.base.Preconditions;
45  
46  public class PropertySourceUtils {
47  
48  	private static final String PROPERTIES_PROPERTY_SOURCE = "propertiesPropertySource";
49  
50  	private static final Logger logger = LoggerUtils.make();
51  
52  	/**
53  	 * Return a property source from system properties plus the environment
54  	 */
55  	public static PropertySource<?> getDefaultPropertySource() {
56  		return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, PropertyUtils.getGlobalProperties());
57  	}
58  
59  	/**
60  	 * Return a property source based on the properties object passed in, but where system properties plus environment properties "win"
61  	 */
62  	public static PropertySource<?> getPropertySource(Properties properties) {
63  		return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, PropertyUtils.getGlobalProperties(properties));
64  	}
65  
66  	/**
67  	 * Return a property source based on the properties loaded from the locations passed in.
68  	 */
69  	public static PropertySource<?> getPropertySource(PropertiesService service, List<Location> locations) {
70  		return getPropertySource(service, locations, false);
71  	}
72  
73  	/**
74  	 * Return a property source based on the properties loaded from the locations passed in, but where system properties plus environment properties "win" if
75  	 * {@code includeGlobal=true}
76  	 */
77  	public static PropertySource<?> getPropertySource(PropertiesService service, List<Location> locations, boolean includeGlobal) {
78  		Properties properties = service.getProperties(locations);
79  		if (includeGlobal) {
80  			properties = PropertyUtils.getGlobalProperties(properties);
81  		}
82  		return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, properties);
83  	}
84  
85  	/**
86  	 * Aggregate every property from every <code>PropertySource</code> in the <code>ConfigurableEnvironment</code> into a <code>Properties</code> object.
87  	 * 
88  	 * @throws IllegalArgumentException
89  	 *             If any <code>PropertySource</code> is not an <code>EnumerablePropertySource</code> or if any values are not <code>java.lang.String</code>
90  	 */
91  	public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) {
92  
93  		// Extract the list of PropertySources from the environment
94  		List<PropertySource<?>> sources = getPropertySources(env);
95  
96  		// Spring provides PropertySource objects ordered from highest priority to lowest priority
97  		// We reverse the order here so things follow the "last one in wins" strategy
98  		Collections.reverse(sources);
99  
100 		// Make sure every property source is enumerable
101 		List<EnumerablePropertySource<?>> enumerables = asEnumerableList(sources);
102 
103 		// Combine them into a single Properties object
104 		return convert(enumerables);
105 	}
106 
107 	/**
108 	 * Aggregate every property from every <code>PropertySource</code> in the <code>ConfigurableEnvironment</code> into a <code>Properties</code> object.
109 	 * 
110 	 * If a PropertySource is not Enumerable, just omit it from the list, but do not throw an exception
111 	 */
112 	public static Properties getAllEnumerablePropertiesQuietly(ConfigurableEnvironment env) {
113 
114 		// Extract the list of PropertySources from the environment
115 		List<PropertySource<?>> sources = getPropertySources(env);
116 
117 		// Spring provides PropertySource objects ordered from highest priority to lowest priority
118 		// We reverse the order here so things follow the "last one in wins" strategy
119 		Collections.reverse(sources);
120 
121 		// Make sure every property source is enumerable
122 		List<EnumerablePropertySource<?>> enumerables = asEnumerableListQuietly(sources);
123 
124 		// Combine them into a single Properties object
125 		return convert(enumerables);
126 	}
127 
128 	/**
129 	 * Aggregate every property from every <code>PropertySource</code> in the <code>ConfigurableEnvironment</code> into an <code>Properties</code> object.
130 	 * 
131 	 * @throws IllegalArgumentException
132 	 *             If any <code>PropertySource</code> is not an <code>EnumerablePropertySource</code> or if any values are not <code>java.lang.String</code>
133 	 */
134 	public static ImmutableProperties getEnvAsImmutableProperties(ConfigurableEnvironment env) {
135 		return new ImmutableProperties(getAllEnumerableProperties(env));
136 	}
137 
138 	/**
139 	 * Create an <code>EnumerablePropertySource</code> list from a <code>PropertySource</code> list
140 	 * 
141 	 * @throws <code>IllegalArgumentException</code> if any element in <code>sources</code> is not an <code>EnumerablePropertySource</code>
142 	 */
143 	public static List<EnumerablePropertySource<?>> asEnumerableList(List<PropertySource<?>> sources) {
144 		List<EnumerablePropertySource<?>> list = new ArrayList<EnumerablePropertySource<?>>();
145 		for (PropertySource<?> source : sources) {
146 			boolean expression = source instanceof EnumerablePropertySource<?>;
147 			String errorMessage = "'%s' is not enumerable [%s]";
148 			Object[] args = { source.getName(), source.getClass().getCanonicalName() };
149 			Preconditions.checkState(expression, errorMessage, args);
150 			EnumerablePropertySource<?> element = (EnumerablePropertySource<?>) source;
151 			list.add(element);
152 		}
153 		return list;
154 	}
155 
156 	/**
157 	 * Create an <code>EnumerablePropertySource</code> list from a <code>PropertySource</code> list
158 	 * 
159 	 * @throws <code>IllegalArgumentException</code> if any element in <code>sources</code> is not an <code>EnumerablePropertySource</code>
160 	 */
161 	public static List<EnumerablePropertySource<?>> asEnumerableListQuietly(List<PropertySource<?>> sources) {
162 		List<EnumerablePropertySource<?>> list = new ArrayList<EnumerablePropertySource<?>>();
163 		for (PropertySource<?> source : sources) {
164 			if (source instanceof EnumerablePropertySource<?>) {
165 				EnumerablePropertySource<?> element = (EnumerablePropertySource<?>) source;
166 				list.add(element);
167 			} else {
168 				logger.warn("'{}' is not enumerable [{}]", source.getName(), source.getClass().getCanonicalName());
169 			}
170 		}
171 		return list;
172 	}
173 
174 	public static List<PropertySource<?>> getPropertySources(Class<?> annotatedClass) {
175 		ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(annotatedClass);
176 		return extractPropertySourcesAndClose(context);
177 	}
178 
179 	public static List<PropertySource<?>> extractPropertySourcesAndClose(ConfigurableApplicationContext context) {
180 		// Extract PropertySources (if any)
181 		List<PropertySource<?>> sources = getPropertySources(context);
182 
183 		// Close the context
184 		SpringUtils.closeQuietly(context);
185 
186 		// Return the list
187 		return sources;
188 	}
189 
190 	/**
191 	 * Copy the key/value pairs from <code>source</code> into a <code>java.util.Properties</code> object.
192 	 * 
193 	 * @throws <code>IllegalArgumentException</code> if any value is <code>null</code> or is not a <code>java.lang.String</code>
194 	 */
195 	public static Properties convert(EnumerablePropertySource<?> source) {
196 		Assert.notNull(source, "source is null");
197 		Properties properties = new Properties();
198 		String[] names = source.getPropertyNames();
199 		for (String name : names) {
200 			Object object = source.getProperty(name);
201 			Assert.notNull(object, "[" + name + "] is null");
202 			Assert.isTrue(object instanceof String, "[" + name + "] is not a string");
203 			properties.setProperty(name, (String) object);
204 		}
205 		return properties;
206 	}
207 
208 	/**
209 	 * Copy the key/value pairs from <code>sources</code> into a <code>java.util.Properties</code> object.
210 	 * 
211 	 * @throws <code>IllegalArgumentException</code> if any value is <code>null</code> or is not a <code>java.lang.String</code>
212 	 */
213 	public static Properties convert(List<EnumerablePropertySource<?>> sources) {
214 		Properties converted = new Properties();
215 		for (EnumerablePropertySource<?> source : sources) {
216 			Properties properties = convert(source);
217 			converted.putAll(properties);
218 		}
219 		return converted;
220 	}
221 
222 	/**
223 	 * Aggregate all <code>PropertySource<?><code> objects from the environment into a <code>List</code>
224 	 */
225 	public static List<PropertySource<?>> getPropertySources(ConfigurableEnvironment env) {
226 		Preconditions.checkNotNull(env, "'env' cannot be null");
227 		MutablePropertySources mps = env.getPropertySources();
228 		List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
229 		Iterator<PropertySource<?>> itr = mps.iterator();
230 		while (itr.hasNext()) {
231 			PropertySource<?> source = itr.next();
232 			sources.add(source);
233 		}
234 		return sources;
235 	}
236 
237 	/**
238 	 * Remove all property sources from <code>env</code> and replace them with a single <code>PropertiesPropertySource</code> backed by <code>properties</code>
239 	 */
240 	public static void reconfigurePropertySources(ConfigurableEnvironment env, String name, Properties properties) {
241 		// Remove all existing property sources
242 		removeAllPropertySources(env);
243 
244 		// MutablePropertySources allow us to manipulate the list of property sources
245 		MutablePropertySources mps = env.getPropertySources();
246 
247 		// Make sure there are no existing property sources
248 		Assert.isTrue(mps.size() == 0);
249 
250 		// Create a property source backed by the properties object passed in
251 		PropertiesPropertySource pps = new PropertiesPropertySource(name, properties);
252 
253 		// Add it to the environment
254 		mps.addFirst(pps);
255 	}
256 
257 	/**
258 	 * Remove all property sources from <code>env</code>.
259 	 */
260 	public static void removeAllPropertySources(ConfigurableEnvironment env) {
261 		MutablePropertySources mps = env.getPropertySources();
262 		List<PropertySource<?>> sources = getPropertySources(env);
263 		for (PropertySource<?> source : sources) {
264 			String name = source.getName();
265 			mps.remove(name);
266 		}
267 	}
268 
269 	/**
270 	 * Null safe conversion of <code>PropertySource<?>[]</code> into <code>List&lt;PropertySource&lt;?>></code>
271 	 */
272 	public static List<PropertySource<?>> asList(PropertySource<?>... sources) {
273 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
274 		if (sources == null) {
275 			return list;
276 		}
277 		for (PropertySource<?> element : sources) {
278 			if (element != null) {
279 				list.add(element);
280 			}
281 		}
282 		return list;
283 	}
284 
285 	/**
286 	 * Return all <code>PropertySource</code> beans registered in the context, sorted use <code>comparator</code>
287 	 */
288 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context, Comparator<PropertySource<?>> comparator) {
289 		// Extract all beans that implement the PropertySource interface
290 		@SuppressWarnings("rawtypes")
291 		Map<String, PropertySource> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, PropertySource.class);
292 
293 		// Extract the PropertySource beans into a list
294 		List<PropertySource<?>> list = new ArrayList<PropertySource<?>>();
295 		for (PropertySource<?> source : map.values()) {
296 			list.add(source);
297 		}
298 
299 		// Sort them using the provided comparator
300 		Collections.sort(list, comparator);
301 
302 		// Return the list
303 		return list;
304 	}
305 
306 	/**
307 	 * Return all <code>PropertySource</code> beans registered in the context, sorted by name.
308 	 */
309 	public static List<PropertySource<?>> getPropertySources(ConfigurableApplicationContext context) {
310 		// Sort them by name
311 		return getPropertySources(context, new PropertySourceNameComparator());
312 	}
313 
314 	/**
315 	 * Return a <code>SpringContext</code> such that <code>source</code> is the only thing Spring uses to resolve placeholders
316 	 */
317 	public static SpringContext getSinglePropertySourceContext(PropertySource<?> source) {
318 		// Setup a property source context such that this property source is the only one registered with Spring
319 		// This PropertySource will be the ONLY thing used to resolve placeholders
320 		PropertySourceContext psc = new PropertySourceContext(source, true);
321 
322 		// Setup a Spring context
323 		SpringContext context = new SpringContext();
324 
325 		// Supply Spring with our PropertySource
326 		context.setPropertySourceContext(psc);
327 
328 		// Return a Spring context configured with a single property source
329 		return context;
330 	}
331 
332 }