Coverage Report - org.kuali.spring.util.PropertiesLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertiesLoader
77%
146/188
44%
24/54
2.083
PropertiesLoader$1
100%
1/1
N/A
2.083
 
 1  
 package org.kuali.spring.util;
 2  
 
 3  
 import java.io.IOException;
 4  
 import java.io.InputStream;
 5  
 import java.io.InputStreamReader;
 6  
 import java.util.ArrayList;
 7  
 import java.util.Collections;
 8  
 import java.util.List;
 9  
 import java.util.Map;
 10  
 import java.util.Properties;
 11  
 
 12  
 import org.slf4j.Logger;
 13  
 import org.slf4j.LoggerFactory;
 14  
 import org.springframework.core.io.Resource;
 15  
 import org.springframework.core.io.support.PropertiesLoaderSupport;
 16  
 import org.springframework.util.CollectionUtils;
 17  
 import org.springframework.util.DefaultPropertiesPersister;
 18  
 import org.springframework.util.ObjectUtils;
 19  
 import org.springframework.util.PropertiesPersister;
 20  
 
 21  3
 public class PropertiesLoader {
 22  1
         private static final Logger logger = LoggerFactory.getLogger(PropertiesLoader.class);
 23  
 
 24  
         public static final String DEFAULT_ENVIRONMENT_PROPERTY_PREFIX = "env.";
 25  
         public static final boolean DEFAULT_IS_USE_ENVIRONMENT_PROPERTY_PREFIX = false;
 26  
         public static final boolean DEFAULT_IS_LOCAL_OVERRIDE = false;
 27  
         public static final boolean DEFAULT_IS_IGNORE_RESOURCE_NOT_FOUND = false;
 28  
         public static final boolean DEFAULT_IS_SEARCH_SYSTEM_ENVIRONMENT = true;
 29  1
         public static final SystemPropertiesMode DEFAULT_SYSTEM_PROPERTIES_MODE = SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_OVERRIDE;
 30  
 
 31  
         // Properties with defaults
 32  3
         String environmentPropertyPrefix = DEFAULT_ENVIRONMENT_PROPERTY_PREFIX;
 33  3
         boolean useEnvironmentPropertyPrefix = DEFAULT_IS_USE_ENVIRONMENT_PROPERTY_PREFIX;
 34  3
         SystemPropertiesMode systemPropertiesMode = DEFAULT_SYSTEM_PROPERTIES_MODE;
 35  3
         boolean localOverride = DEFAULT_IS_LOCAL_OVERRIDE;
 36  3
         boolean ignoreResourceNotFound = DEFAULT_IS_IGNORE_RESOURCE_NOT_FOUND;
 37  3
         boolean searchSystemEnvironment = DEFAULT_IS_SEARCH_SYSTEM_ENVIRONMENT;
 38  
 
 39  
         // Properties without defaults
 40  
         String fileEncoding;
 41  
         Properties[] localProperties;
 42  
         Resource[] locations;
 43  
 
 44  
         // Default component beans
 45  3
         PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
 46  3
         PropertyLogger plogger = new PropertyLogger();
 47  
 
 48  
         // Instance variables filled in during loading
 49  
         Properties systemProperties;
 50  
         Properties environmentProperties;
 51  
         Properties resourceProperties;
 52  
         Properties mergedLocalProperties;
 53  
         Properties properties;
 54  
 
 55  
         /**
 56  
          * Get a handle to our environment properties. Prefix is optional
 57  
          */
 58  
         protected Properties getEnvironmentProperties(String prefix) {
 59  1
                 Map<String, String> environmentMap = SystemUtils.getEnvironmentIgnoreExceptions();
 60  1
                 Properties envProps = new Properties();
 61  1
                 for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
 62  55
                         String key = (prefix == null) ? entry.getKey() : prefix + entry.getKey();
 63  55
                         envProps.setProperty(key, entry.getValue());
 64  55
                 }
 65  1
                 return envProps;
 66  
         }
 67  
 
 68  
         /**
 69  
          * Given a resource and an InputStream, load and return a Properties object. Supports regular as well as XML style
 70  
          * properties files
 71  
          * 
 72  
          * @param location
 73  
          * @param is
 74  
          * @return
 75  
          * @throws IOException
 76  
          */
 77  
         protected Properties getProperties(Resource location, InputStream is) throws IOException {
 78  1
                 Properties properties = new Properties();
 79  
                 // Use XML style loading if it is an XML file
 80  1
                 if (isXMLFile(location)) {
 81  0
                         getPropertiesPersister().loadFromXml(properties, is);
 82  0
                         return properties;
 83  
                 }
 84  
 
 85  1
                 if (getFileEncoding() != null) {
 86  
                         // Use a Reader if they've specified a fileEncoding
 87  0
                         getPropertiesPersister().load(properties, new InputStreamReader(is, getFileEncoding()));
 88  
                 } else {
 89  
                         // Otherwise use an InputStream
 90  1
                         getPropertiesPersister().load(properties, is);
 91  
                 }
 92  1
                 return properties;
 93  
         }
 94  
 
 95  
         /**
 96  
          * Load properties from a Resource object. Returns an empty Properties object if ignoreResourceNotFound is true and
 97  
          * the resource could not be located
 98  
          * 
 99  
          * @param location
 100  
          * @return
 101  
          * @throws IOException
 102  
          */
 103  
         protected Properties loadProperties(Resource location) throws IOException {
 104  
                 // Handle locations that don't exist
 105  1
                 if (!location.exists()) {
 106  0
                         return handleResourceNotFound(location);
 107  
                 }
 108  
 
 109  
                 // Proceed with loading
 110  1
                 logger.info("Loading properties from {}", location);
 111  1
                 InputStream is = null;
 112  
                 try {
 113  1
                         is = location.getInputStream();
 114  1
                         return getProperties(location, is);
 115  0
                 } catch (IOException e) {
 116  0
                         throw e;
 117  
                 } finally {
 118  1
                         nullSafeClose(is);
 119  
                 }
 120  
         }
 121  
 
 122  
         /**
 123  
          * True if this location represents an XML file, false otherwise
 124  
          * 
 125  
          * @param location
 126  
          * @return
 127  
          */
 128  
         protected boolean isXMLFile(Resource location) {
 129  1
                 String filename = null;
 130  
                 try {
 131  1
                         filename = location.getFilename();
 132  0
                 } catch (IllegalStateException ex) {
 133  
                         // resource is not file-based. See SPR-7552.
 134  0
                         return false;
 135  1
                 }
 136  
                 // May not have thrown an exception, but might still be null
 137  1
                 if (filename == null) {
 138  0
                         return false;
 139  
                 }
 140  
                 // It's an XML file
 141  1
                 if (filename.endsWith(PropertiesLoaderSupport.XML_FILE_EXTENSION)) {
 142  0
                         return true;
 143  
                 } else {
 144  1
                         return false;
 145  
                 }
 146  
         }
 147  
 
 148  
         /**
 149  
          * Throw an exception unless ignoreResourceNotFound is true
 150  
          * 
 151  
          * @param location
 152  
          * @return
 153  
          */
 154  
         protected Properties handleResourceNotFound(Resource location) {
 155  0
                 if (isIgnoreResourceNotFound()) {
 156  0
                         logger.info("Ignoring properties from {}.  Resource not found", location);
 157  0
                         return new Properties();
 158  
                 } else {
 159  0
                         throw new PropertiesLoadException("Resource not found: " + location);
 160  
                 }
 161  
         }
 162  
 
 163  
         /**
 164  
          * Close the InputStream
 165  
          * 
 166  
          * @param is
 167  
          * @throws IOException
 168  
          */
 169  
         protected void nullSafeClose(InputStream is) throws IOException {
 170  1
                 if (is == null) {
 171  0
                         return;
 172  
                 }
 173  1
                 is.close();
 174  1
         }
 175  
 
 176  
         /**
 177  
          * Merge the Properties[] into a single Properties object
 178  
          */
 179  
         protected Properties mergeLocalProperties() {
 180  1
                 if (getLocalProperties() == null || getLocalProperties().length == 0) {
 181  
                         // Nothing to do, return an empty Properties object
 182  1
                         return new Properties();
 183  
                 }
 184  
                 // Merge the Properties[] into a single Properties object
 185  0
                 Properties result = new Properties();
 186  0
                 for (Properties localProp : getLocalProperties()) {
 187  0
                         CollectionUtils.mergePropertiesIntoMap(localProp, result);
 188  
                 }
 189  0
                 return result;
 190  
         }
 191  
 
 192  
         /**
 193  
          * Get properties from the environment
 194  
          */
 195  
         protected Properties loadEnvironmentProperties() {
 196  1
                 if (isUseEnvironmentPropertyPrefix()) {
 197  0
                         return getEnvironmentProperties(getEnvironmentPropertyPrefix());
 198  
                 } else {
 199  1
                         return getEnvironmentProperties(null);
 200  
                 }
 201  
         }
 202  
 
 203  
         /**
 204  
          * Get system properties
 205  
          */
 206  
         protected Properties loadSystemProperties() {
 207  1
                 return SystemUtils.getSystemPropertiesIgnoreExceptions();
 208  
         }
 209  
 
 210  
         /**
 211  
          * Merge local, resource, system, and environment properties into a single Properties object. User supplied settings
 212  
          * control which property "wins" if a property is defined in multiple areas
 213  
          */
 214  
         public Properties mergeProperties(Properties local, Properties resource, Properties sys, Properties env) {
 215  
                 // Storage for our merged properties
 216  1
                 Properties result = new Properties();
 217  
 
 218  
                 // Merge in local properties (nothing to actually merge here, but this also logs them when DEBUG is on)
 219  1
                 PropertiesMergeContext context = new PropertiesMergeContext(result, local, PropertySource.LOCAL);
 220  1
                 mergeProperties(context);
 221  
 
 222  
                 // Merge in resource properties. localOverride controls what property "wins" if the same
 223  
                 // property is declared both locally and in a resource
 224  1
                 boolean resourcePropertyWins = !isLocalOverride();
 225  1
                 context = new PropertiesMergeContext(result, resource, resourcePropertyWins, PropertySource.RESOURCE);
 226  1
                 mergeProperties(context);
 227  
 
 228  
                 // Merge in system properties according to the SystemPropertiesMode being used
 229  1
                 if (!getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_NEVER)) {
 230  1
                         boolean override = getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_OVERRIDE);
 231  1
                         context = new PropertiesMergeContext(result, sys, override, PropertySource.SYSTEM);
 232  1
                         mergeProperties(context);
 233  
                 }
 234  
 
 235  
                 // Merge in environment properties. Environment properties never override properties from another source
 236  1
                 if (isSearchSystemEnvironment()) {
 237  1
                         context = new PropertiesMergeContext(result, env, false, PropertySource.ENVIRONMENT);
 238  1
                         mergeProperties(context);
 239  
                 }
 240  
 
 241  
                 // Return the merged properties
 242  1
                 return result;
 243  
         }
 244  
 
 245  
         /**
 246  
          * Fill in a Properties object
 247  
          */
 248  
         public Properties loadProperties() {
 249  
                 try {
 250  
                         // Populate properties from the default set of locations
 251  1
                         Properties local = mergeLocalProperties();
 252  1
                         Properties resource = loadResourceProperties();
 253  1
                         Properties sys = loadSystemProperties();
 254  1
                         Properties env = loadEnvironmentProperties();
 255  
 
 256  
                         // Store the properties locally
 257  1
                         setMergedLocalProperties(local);
 258  1
                         setResourceProperties(resource);
 259  1
                         setSystemProperties(sys);
 260  1
                         setEnvironmentProperties(env);
 261  
 
 262  
                         // Merge them into a single properties object
 263  1
                         return mergeProperties(local, resource, sys, env);
 264  0
                 } catch (IOException e) {
 265  0
                         throw new PropertiesLoadException("Unexpected error loading properties", e);
 266  
                 }
 267  
         }
 268  
 
 269  
         /**
 270  
          * Merge a property under 'key' from newProps into currentProps using the settings from PropertiesMergeContext
 271  
          * 
 272  
          * @param context
 273  
          * @param key
 274  
          */
 275  
         public PropertyMergeResult mergeProperty(PropertiesMergeContext context, String key) {
 276  136
                 Properties newProps = context.getNewProperties();
 277  136
                 Properties currentProps = context.getCurrentProperties();
 278  136
                 boolean override = context.isOverride();
 279  
 
 280  
                 // Extract the new value
 281  136
                 String newValue = newProps.getProperty(key);
 282  
 
 283  
                 // Extract the existing value
 284  136
                 String currentValue = currentProps.getProperty(key);
 285  
 
 286  
                 // If the new value is null, there is nothing further to do
 287  136
                 if (newValue == null) {
 288  0
                         PropertyMergeType type = PropertyMergeType.NULL_NEW_VALUE;
 289  0
                         return new PropertyMergeResult(context, key, currentValue, newValue, type);
 290  
                 }
 291  
 
 292  
                 // The newValue is not null, and there is no existing value for this key
 293  136
                 if (currentValue == null) {
 294  136
                         currentProps.setProperty(key, newValue);
 295  136
                         PropertyMergeType type = PropertyMergeType.ADD;
 296  136
                         return new PropertyMergeResult(context, key, currentValue, newValue, type);
 297  
                 }
 298  
 
 299  
                 // Neither value is null and the values are the same, nothing further to do
 300  0
                 if (ObjectUtils.nullSafeEquals(newValue, currentValue)) {
 301  0
                         PropertyMergeType type = PropertyMergeType.IDENTICAL_VALUES;
 302  0
                         return new PropertyMergeResult(context, key, currentValue, newValue, type);
 303  
                 }
 304  
 
 305  0
                 if (override) {
 306  
                         // There is an existing property, but the new property wins
 307  0
                         currentProps.setProperty(key, newValue);
 308  0
                         PropertyMergeType type = PropertyMergeType.OVERRIDE;
 309  0
                         return new PropertyMergeResult(context, key, currentValue, newValue, type);
 310  
                 } else {
 311  
                         // There is already an existing property, and the existing property wins
 312  0
                         PropertyMergeType type = PropertyMergeType.EXISTING_PROPERTY_WINS;
 313  0
                         return new PropertyMergeResult(context, key, currentValue, newValue, type);
 314  
                 }
 315  
         }
 316  
 
 317  
         public void mergeProperties(PropertiesMergeContext context) {
 318  5
                 List<String> keys = new ArrayList<String>(context.getNewProperties().stringPropertyNames());
 319  5
                 if (context.isSort()) {
 320  5
                         Collections.sort(keys);
 321  
                 }
 322  5
                 for (String key : keys) {
 323  136
                         logResult(mergeProperty(context, key));
 324  
                 }
 325  5
         }
 326  
 
 327  
         protected void logResult(PropertyMergeResult result) {
 328  136
                 PropertySource source = result.getContext().getSource();
 329  136
                 String key = result.getKey();
 330  136
                 String newValue = result.getNewValue();
 331  136
                 String currentValue = result.getOldValue();
 332  
 
 333  136
                 switch (result.getType()) {
 334  
                 case ADD:
 335  136
                         logger.debug("Add " + source + " property {}=[{}]", key, plogger.getLogValue(key, newValue));
 336  136
                         return;
 337  
                 case OVERRIDE:
 338  0
                         logger.info(source + " property override for '" + key + "' [{}]->[{}]",
 339  
                                         plogger.getLogValue(key, currentValue), plogger.getLogValue(key, newValue));
 340  0
                         return;
 341  
                 case EXISTING_PROPERTY_WINS:
 342  0
                         logger.debug("The existing value for '" + key + "' is not being overridden by the " + source
 343  
                                         + " value. Existing:[{}] New:[{}]", plogger.getLogValue(key, currentValue),
 344  
                                         plogger.getLogValue(key, newValue));
 345  0
                         return;
 346  
                 default:
 347  0
                         logger.trace("Merge property result: {} for {}", result.getType(), key);
 348  0
                         return;
 349  
 
 350  
                 }
 351  
 
 352  
         }
 353  
 
 354  
         public Properties loadResourceProperties() throws IOException {
 355  1
                 if (getLocations() == null || getLocations().length == 0) {
 356  0
                         logger.info("No resource property locations to load from");
 357  0
                         return new Properties();
 358  
                 }
 359  1
                 Properties result = new Properties();
 360  2
                 for (Resource location : getLocations()) {
 361  1
                         Properties newProps = loadProperties(location);
 362  1
                         PropertySource source = PropertySource.RESOURCE;
 363  
                         // If a property is declared in more than one resource location, the last resource location "wins"
 364  1
                         boolean override = true;
 365  1
                         PropertiesMergeContext context = new PropertiesMergeContext(result, newProps, override, source);
 366  1
                         mergeProperties(context);
 367  
                 }
 368  1
                 return result;
 369  
         }
 370  
 
 371  
         public boolean isIgnoreResourceNotFound() {
 372  1
                 return ignoreResourceNotFound;
 373  
         }
 374  
 
 375  
         public String getFileEncoding() {
 376  2
                 return fileEncoding;
 377  
         }
 378  
 
 379  
         public Properties[] getLocalProperties() {
 380  2
                 return localProperties;
 381  
         }
 382  
 
 383  
         public void setLocalProperties(Properties[] localProperties) {
 384  1
                 this.localProperties = localProperties;
 385  1
         }
 386  
 
 387  
         public Resource[] getLocations() {
 388  4
                 return locations;
 389  
         }
 390  
 
 391  
         public boolean isLocalOverride() {
 392  2
                 return localOverride;
 393  
         }
 394  
 
 395  
         public PropertiesPersister getPropertiesPersister() {
 396  2
                 return propertiesPersister;
 397  
         }
 398  
 
 399  
         public String getEnvironmentPropertyPrefix() {
 400  1
                 return environmentPropertyPrefix;
 401  
         }
 402  
 
 403  
         public void setEnvironmentPropertyPrefix(String environmentPropertyPrefix) {
 404  1
                 this.environmentPropertyPrefix = environmentPropertyPrefix;
 405  1
         }
 406  
 
 407  
         public Logger getLogger() {
 408  0
                 return logger;
 409  
         }
 410  
 
 411  
         public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
 412  1
                 this.propertiesPersister = propertiesPersister;
 413  1
         }
 414  
 
 415  
         public void setLocations(Resource[] locations) {
 416  2
                 this.locations = locations;
 417  2
         }
 418  
 
 419  
         public void setLocalOverride(boolean localOverride) {
 420  1
                 this.localOverride = localOverride;
 421  1
         }
 422  
 
 423  
         public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
 424  1
                 this.ignoreResourceNotFound = ignoreResourceNotFound;
 425  1
         }
 426  
 
 427  
         public void setFileEncoding(String fileEncoding) {
 428  1
                 this.fileEncoding = fileEncoding;
 429  1
         }
 430  
 
 431  
         public SystemPropertiesMode getSystemPropertiesMode() {
 432  3
                 return systemPropertiesMode;
 433  
         }
 434  
 
 435  
         public void setSystemPropertiesMode(SystemPropertiesMode systemPropertiesMode) {
 436  1
                 this.systemPropertiesMode = systemPropertiesMode;
 437  1
         }
 438  
 
 439  
         public boolean isSearchSystemEnvironment() {
 440  2
                 return searchSystemEnvironment;
 441  
         }
 442  
 
 443  
         public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
 444  1
                 this.searchSystemEnvironment = searchSystemEnvironment;
 445  1
         }
 446  
 
 447  
         public void setResourceProperties(Properties resourceProperties) {
 448  2
                 this.resourceProperties = resourceProperties;
 449  2
         }
 450  
 
 451  
         public void setSystemProperties(Properties systemProperties) {
 452  2
                 this.systemProperties = systemProperties;
 453  2
         }
 454  
 
 455  
         public void setEnvironmentProperties(Properties environmentProperties) {
 456  2
                 this.environmentProperties = environmentProperties;
 457  2
         }
 458  
 
 459  
         public void setMergedLocalProperties(Properties mergedLocalProperties) {
 460  2
                 this.mergedLocalProperties = mergedLocalProperties;
 461  2
         }
 462  
 
 463  
         public boolean isUseEnvironmentPropertyPrefix() {
 464  2
                 return useEnvironmentPropertyPrefix;
 465  
         }
 466  
 
 467  
         public void setUseEnvironmentPropertyPrefix(boolean useEnvironmentPropertyPrefix) {
 468  1
                 this.useEnvironmentPropertyPrefix = useEnvironmentPropertyPrefix;
 469  1
         }
 470  
 
 471  
         public PropertyLogger getPlogger() {
 472  1
                 return plogger;
 473  
         }
 474  
 
 475  
         public void setPlogger(PropertyLogger propertiesLogger) {
 476  2
                 this.plogger = propertiesLogger;
 477  2
         }
 478  
 
 479  
         public Properties getProperties() {
 480  1
                 return properties;
 481  
         }
 482  
 
 483  
         public void setProperties(Properties properties) {
 484  1
                 this.properties = properties;
 485  1
         }
 486  
 
 487  
         public Properties getSystemProperties() {
 488  1
                 return systemProperties;
 489  
         }
 490  
 
 491  
         public Properties getEnvironmentProperties() {
 492  1
                 return environmentProperties;
 493  
         }
 494  
 
 495  
         public Properties getResourceProperties() {
 496  1
                 return resourceProperties;
 497  
         }
 498  
 
 499  
         public Properties getMergedLocalProperties() {
 500  1
                 return mergedLocalProperties;
 501  
         }
 502  
 
 503  
 }