001/** 002 * Copyright 2010-2014 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 */ 016package org.kuali.common.util.spring; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.Comparator; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024import java.util.Properties; 025 026import org.kuali.common.util.Assert; 027import org.kuali.common.util.PropertyUtils; 028import org.kuali.common.util.log.LoggerUtils; 029import org.kuali.common.util.properties.Location; 030import org.kuali.common.util.properties.PropertiesService; 031import org.kuali.common.util.property.ImmutableProperties; 032import org.kuali.common.util.spring.service.PropertySourceContext; 033import org.kuali.common.util.spring.service.SpringContext; 034import org.slf4j.Logger; 035import org.springframework.beans.factory.BeanFactoryUtils; 036import org.springframework.context.ConfigurableApplicationContext; 037import org.springframework.context.annotation.AnnotationConfigApplicationContext; 038import org.springframework.core.env.ConfigurableEnvironment; 039import org.springframework.core.env.EnumerablePropertySource; 040import org.springframework.core.env.MutablePropertySources; 041import org.springframework.core.env.PropertiesPropertySource; 042import org.springframework.core.env.PropertySource; 043 044import com.google.common.base.Preconditions; 045 046public class PropertySourceUtils { 047 048 private static final String PROPERTIES_PROPERTY_SOURCE = "propertiesPropertySource"; 049 050 private static final Logger logger = LoggerUtils.make(); 051 052 /** 053 * Return a property source from system properties plus the environment 054 */ 055 public static PropertySource<?> getDefaultPropertySource() { 056 return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, PropertyUtils.getGlobalProperties()); 057 } 058 059 /** 060 * Return a property source based on the properties object passed in, but where system properties plus environment properties "win" 061 */ 062 public static PropertySource<?> getPropertySource(Properties properties) { 063 return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, PropertyUtils.getGlobalProperties(properties)); 064 } 065 066 /** 067 * Return a property source based on the properties loaded from the locations passed in. 068 */ 069 public static PropertySource<?> getPropertySource(PropertiesService service, List<Location> locations) { 070 return getPropertySource(service, locations, false); 071 } 072 073 /** 074 * Return a property source based on the properties loaded from the locations passed in, but where system properties plus environment properties "win" if 075 * {@code includeGlobal=true} 076 */ 077 public static PropertySource<?> getPropertySource(PropertiesService service, List<Location> locations, boolean includeGlobal) { 078 Properties properties = service.getProperties(locations); 079 if (includeGlobal) { 080 properties = PropertyUtils.getGlobalProperties(properties); 081 } 082 return new PropertiesPropertySource(PROPERTIES_PROPERTY_SOURCE, properties); 083 } 084 085 /** 086 * Aggregate every property from every <code>PropertySource</code> in the <code>ConfigurableEnvironment</code> into a <code>Properties</code> object. 087 * 088 * @throws IllegalArgumentException 089 * If any <code>PropertySource</code> is not an <code>EnumerablePropertySource</code> or if any values are not <code>java.lang.String</code> 090 */ 091 public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) { 092 093 // Extract the list of PropertySources from the environment 094 List<PropertySource<?>> sources = getPropertySources(env); 095 096 // Spring provides PropertySource objects ordered from highest priority to lowest priority 097 // We reverse the order here so things follow the "last one in wins" strategy 098 Collections.reverse(sources); 099 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<PropertySource<?>></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}