Coverage Report - org.kuali.spring.util.PropertiesLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertiesLoader
80%
150/187
48%
26/54
2.106
PropertiesLoader$1
100%
1/1
N/A
2.106
 
 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  8
 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  8
     String environmentPropertyPrefix = DEFAULT_ENVIRONMENT_PROPERTY_PREFIX;
 33  8
     boolean useEnvironmentPropertyPrefix = DEFAULT_IS_USE_ENVIRONMENT_PROPERTY_PREFIX;
 34  8
     SystemPropertiesMode systemPropertiesMode = DEFAULT_SYSTEM_PROPERTIES_MODE;
 35  8
     boolean localOverride = DEFAULT_IS_LOCAL_OVERRIDE;
 36  8
     boolean ignoreResourceNotFound = DEFAULT_IS_IGNORE_RESOURCE_NOT_FOUND;
 37  8
     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  8
     PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
 46  8
     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  4
         Map<String, String> environmentMap = SystemUtils.getEnvironmentIgnoreExceptions();
 60  4
         Properties envProps = new Properties();
 61  4
         for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
 62  220
             String key = (prefix == null) ? entry.getKey() : prefix + entry.getKey();
 63  220
             envProps.setProperty(key, entry.getValue());
 64  220
         }
 65  4
         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  4
         Properties properties = new Properties();
 79  
         // Use XML style loading if it is an XML file
 80  4
         if (isXMLFile(location)) {
 81  0
             getPropertiesPersister().loadFromXml(properties, is);
 82  0
             return properties;
 83  
         }
 84  
 
 85  4
         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  4
             getPropertiesPersister().load(properties, is);
 91  
         }
 92  4
         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  5
         if (!location.exists()) {
 106  1
             return handleResourceNotFound(location);
 107  
         }
 108  
 
 109  
         // Proceed with loading
 110  4
         LOGGER.info("Loading properties from {}", location);
 111  4
         InputStream is = null;
 112  
         try {
 113  4
             is = location.getInputStream();
 114  4
             return getProperties(location, is);
 115  0
         } catch (IOException e) {
 116  0
             throw e;
 117  
         } finally {
 118  4
             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  4
         String filename = null;
 130  
         try {
 131  4
             filename = location.getFilename();
 132  0
         } catch (IllegalStateException ex) {
 133  
             // resource is not file-based. See SPR-7552.
 134  0
             return false;
 135  4
         }
 136  
         // May not have thrown an exception, but might still be null
 137  4
         if (filename == null) {
 138  0
             return false;
 139  
         }
 140  
         // It's an XML file
 141  4
         if (filename.endsWith(PropertiesLoaderSupport.XML_FILE_EXTENSION)) {
 142  0
             return true;
 143  
         } else {
 144  4
             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  1
         if (isIgnoreResourceNotFound()) {
 156  1
             LOGGER.info("Ignoring properties from {}.  Resource not found", location);
 157  1
             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  4
         if (is == null) {
 171  0
             return;
 172  
         }
 173  4
         is.close();
 174  4
     }
 175  
 
 176  
     /**
 177  
      * Merge the Properties[] into a single Properties object
 178  
      */
 179  
     protected Properties mergeLocalProperties() {
 180  4
         if (getLocalProperties() == null || getLocalProperties().length == 0) {
 181  
             // Nothing to do, return an empty Properties object
 182  4
             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  4
         if (isUseEnvironmentPropertyPrefix()) {
 197  0
             return getEnvironmentProperties(getEnvironmentPropertyPrefix());
 198  
         } else {
 199  4
             return getEnvironmentProperties(null);
 200  
         }
 201  
     }
 202  
 
 203  
     /**
 204  
      * Get system properties
 205  
      */
 206  
     protected Properties loadSystemProperties() {
 207  4
         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  4
         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  4
         PropertiesMergeContext context = new PropertiesMergeContext(result, local, PropertySource.LOCAL);
 220  4
         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  4
         boolean resourcePropertyWins = !isLocalOverride();
 225  4
         context = new PropertiesMergeContext(result, resource, resourcePropertyWins, PropertySource.RESOURCE);
 226  4
         mergeProperties(context);
 227  
 
 228  
         // Merge in system properties according to the SystemPropertiesMode being used
 229  4
         if (!getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_NEVER)) {
 230  4
             boolean override = getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_OVERRIDE);
 231  4
             context = new PropertiesMergeContext(result, sys, override, PropertySource.SYSTEM);
 232  4
             mergeProperties(context);
 233  
         }
 234  
 
 235  
         // Merge in environment properties. Environment properties never override properties from another source
 236  4
         if (isSearchSystemEnvironment()) {
 237  4
             context = new PropertiesMergeContext(result, env, false, PropertySource.ENVIRONMENT);
 238  4
             mergeProperties(context);
 239  
         }
 240  
 
 241  
         // Return the merged properties
 242  4
         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  4
             Properties local = mergeLocalProperties();
 252  4
             Properties resource = loadResourceProperties();
 253  4
             Properties sys = loadSystemProperties();
 254  4
             Properties env = loadEnvironmentProperties();
 255  
 
 256  
             // Store the properties locally
 257  4
             setMergedLocalProperties(local);
 258  4
             setResourceProperties(resource);
 259  4
             setSystemProperties(sys);
 260  4
             setEnvironmentProperties(env);
 261  
 
 262  
             // Merge them into a single properties object
 263  4
             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  450
         Properties newProps = context.getNewProperties();
 277  450
         Properties currentProps = context.getCurrentProperties();
 278  450
         boolean override = context.isOverride();
 279  
 
 280  
         // Extract the new value
 281  450
         String newValue = newProps.getProperty(key);
 282  
 
 283  
         // Extract the existing value
 284  450
         String currentValue = currentProps.getProperty(key);
 285  
 
 286  
         // If the new value is null, there is nothing further to do
 287  450
         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  450
         if (currentValue == null) {
 294  450
             currentProps.setProperty(key, newValue);
 295  450
             PropertyMergeType type = PropertyMergeType.ADD;
 296  450
             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  21
         List<String> keys = new ArrayList<String>(context.getNewProperties().stringPropertyNames());
 319  21
         if (context.isSort()) {
 320  21
             Collections.sort(keys);
 321  
         }
 322  21
         for (String key : keys) {
 323  450
             logResult(mergeProperty(context, key));
 324  
         }
 325  21
     }
 326  
 
 327  
     protected void logResult(PropertyMergeResult result) {
 328  450
         PropertySource source = result.getContext().getSource();
 329  450
         String key = result.getKey();
 330  450
         String newValue = result.getNewValue();
 331  450
         String currentValue = result.getOldValue();
 332  
 
 333  450
         switch (result.getType()) {
 334  
         case ADD:
 335  450
             LOGGER.debug("Add " + source + " property {}=[{}]", key, plogger.getLogValue(key, newValue));
 336  450
             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  4
         if (getLocations() == null || getLocations().length == 0) {
 356  0
             LOGGER.info("No resource property locations to load from");
 357  0
             return new Properties();
 358  
         }
 359  4
         Properties result = new Properties();
 360  9
         for (Resource location : getLocations()) {
 361  5
             Properties newProps = loadProperties(location);
 362  5
             PropertySource source = PropertySource.RESOURCE;
 363  
             // If a property is declared in more than one resource location, the last resource location "wins"
 364  5
             boolean override = true;
 365  5
             PropertiesMergeContext context = new PropertiesMergeContext(result, newProps, override, source);
 366  5
             mergeProperties(context);
 367  
         }
 368  4
         return result;
 369  
     }
 370  
 
 371  
     public boolean isIgnoreResourceNotFound() {
 372  2
         return ignoreResourceNotFound;
 373  
     }
 374  
 
 375  
     public String getFileEncoding() {
 376  5
         return fileEncoding;
 377  
     }
 378  
 
 379  
     public Properties[] getLocalProperties() {
 380  5
         return localProperties;
 381  
     }
 382  
 
 383  
     public void setLocalProperties(Properties[] localProperties) {
 384  1
         this.localProperties = localProperties;
 385  1
     }
 386  
 
 387  
     public Resource[] getLocations() {
 388  13
         return locations;
 389  
     }
 390  
 
 391  
     public boolean isLocalOverride() {
 392  5
         return localOverride;
 393  
     }
 394  
 
 395  
     public PropertiesPersister getPropertiesPersister() {
 396  5
         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 void setPropertiesPersister(PropertiesPersister propertiesPersister) {
 408  1
         this.propertiesPersister = propertiesPersister;
 409  1
     }
 410  
 
 411  
     public void setLocations(Resource[] locations) {
 412  4
         this.locations = locations;
 413  4
     }
 414  
 
 415  
     public void setLocalOverride(boolean localOverride) {
 416  1
         this.localOverride = localOverride;
 417  1
     }
 418  
 
 419  
     public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
 420  2
         this.ignoreResourceNotFound = ignoreResourceNotFound;
 421  2
     }
 422  
 
 423  
     public void setFileEncoding(String fileEncoding) {
 424  1
         this.fileEncoding = fileEncoding;
 425  1
     }
 426  
 
 427  
     public SystemPropertiesMode getSystemPropertiesMode() {
 428  9
         return systemPropertiesMode;
 429  
     }
 430  
 
 431  
     public void setSystemPropertiesMode(SystemPropertiesMode systemPropertiesMode) {
 432  1
         this.systemPropertiesMode = systemPropertiesMode;
 433  1
     }
 434  
 
 435  
     public boolean isSearchSystemEnvironment() {
 436  5
         return searchSystemEnvironment;
 437  
     }
 438  
 
 439  
     public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
 440  1
         this.searchSystemEnvironment = searchSystemEnvironment;
 441  1
     }
 442  
 
 443  
     public void setResourceProperties(Properties resourceProperties) {
 444  5
         this.resourceProperties = resourceProperties;
 445  5
     }
 446  
 
 447  
     public void setSystemProperties(Properties systemProperties) {
 448  5
         this.systemProperties = systemProperties;
 449  5
     }
 450  
 
 451  
     public void setEnvironmentProperties(Properties environmentProperties) {
 452  5
         this.environmentProperties = environmentProperties;
 453  5
     }
 454  
 
 455  
     public void setMergedLocalProperties(Properties mergedLocalProperties) {
 456  5
         this.mergedLocalProperties = mergedLocalProperties;
 457  5
     }
 458  
 
 459  
     public boolean isUseEnvironmentPropertyPrefix() {
 460  5
         return useEnvironmentPropertyPrefix;
 461  
     }
 462  
 
 463  
     public void setUseEnvironmentPropertyPrefix(boolean useEnvironmentPropertyPrefix) {
 464  1
         this.useEnvironmentPropertyPrefix = useEnvironmentPropertyPrefix;
 465  1
     }
 466  
 
 467  
     public PropertyLogger getPlogger() {
 468  5
         return plogger;
 469  
     }
 470  
 
 471  
     public void setPlogger(PropertyLogger propertiesLogger) {
 472  4
         this.plogger = propertiesLogger;
 473  4
     }
 474  
 
 475  
     public Properties getProperties() {
 476  1
         return properties;
 477  
     }
 478  
 
 479  
     public void setProperties(Properties properties) {
 480  1
         this.properties = properties;
 481  1
     }
 482  
 
 483  
     public Properties getSystemProperties() {
 484  1
         return systemProperties;
 485  
     }
 486  
 
 487  
     public Properties getEnvironmentProperties() {
 488  1
         return environmentProperties;
 489  
     }
 490  
 
 491  
     public Properties getResourceProperties() {
 492  1
         return resourceProperties;
 493  
     }
 494  
 
 495  
     public Properties getMergedLocalProperties() {
 496  1
         return mergedLocalProperties;
 497  
     }
 498  
 
 499  
 }