Coverage Report - org.kuali.spring.util.PropertiesResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertiesResolver
79%
34/43
71%
10/14
1.647
PropertiesResolver$ResolvePropertyContext
56%
13/23
N/A
1.647
 
 1  
 package org.kuali.spring.util;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.Collections;
 5  
 import java.util.List;
 6  
 import java.util.Properties;
 7  
 
 8  
 import org.slf4j.Logger;
 9  
 import org.slf4j.LoggerFactory;
 10  
 import org.springframework.util.ObjectUtils;
 11  
 
 12  3
 public class PropertiesResolver extends PlaceholderStringResolver {
 13  1
         private static final Logger logger = LoggerFactory.getLogger(PropertiesResolver.class);
 14  
         public static final boolean DEFAULT_IS_SORT = true;
 15  3
         boolean sort = DEFAULT_IS_SORT;
 16  
 
 17  3
         protected class ResolvePropertyContext {
 18  
                 public ResolvePropertyContext(List<String> keys, String key, Properties properties,
 19  123
                                 Properties resolvedProperties, ValueRetriever retriever) {
 20  123
                         super();
 21  123
                         this.keys = keys;
 22  123
                         this.key = key;
 23  123
                         this.properties = properties;
 24  123
                         this.resolvedProperties = resolvedProperties;
 25  123
                         this.retriever = retriever;
 26  123
                 }
 27  
 
 28  
                 List<String> keys;
 29  
                 String key;
 30  
                 Properties properties;
 31  
                 Properties resolvedProperties;
 32  
                 ValueRetriever retriever;
 33  
 
 34  
                 public List<String> getKeys() {
 35  1
                         return keys;
 36  
                 }
 37  
 
 38  
                 public void setKeys(List<String> keys) {
 39  0
                         this.keys = keys;
 40  0
                 }
 41  
 
 42  
                 public String getKey() {
 43  246
                         return key;
 44  
                 }
 45  
 
 46  
                 public void setKey(String key) {
 47  0
                         this.key = key;
 48  0
                 }
 49  
 
 50  
                 public Properties getProperties() {
 51  246
                         return properties;
 52  
                 }
 53  
 
 54  
                 public void setProperties(Properties properties) {
 55  0
                         this.properties = properties;
 56  0
                 }
 57  
 
 58  
                 public Properties getResolvedProperties() {
 59  123
                         return resolvedProperties;
 60  
                 }
 61  
 
 62  
                 public void setResolvedProperties(Properties resolvedProperties) {
 63  0
                         this.resolvedProperties = resolvedProperties;
 64  0
                 }
 65  
 
 66  
                 public ValueRetriever getRetriever() {
 67  246
                         return retriever;
 68  
                 }
 69  
 
 70  
                 public void setRetriever(ValueRetriever retriever) {
 71  0
                         this.retriever = retriever;
 72  0
                 }
 73  
         }
 74  
 
 75  
         /**
 76  
          * Resolve any placeholders in the Properties object passed in
 77  
          */
 78  
         public Properties resolve(Properties properties) {
 79  1
                 return resolve(properties, new PropertiesRetriever(properties));
 80  
         }
 81  
 
 82  
         /**
 83  
          * Resolve any placeholders in the Properties object passed in, using the ValueRetriever passed in to locate
 84  
          * replacement values for placeholder text
 85  
          */
 86  
         public Properties resolve(Properties properties, ValueRetriever retriever) {
 87  
                 // Storage for resolved properties
 88  1
                 Properties resolvedProperties = new Properties();
 89  
                 // Store the keys in a list so we can sort them if needed
 90  1
                 List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
 91  1
                 if (isSort()) {
 92  
                         // Resolve them in sorted order
 93  1
                         Collections.sort(keys);
 94  
                 }
 95  
                 // Iterate through the keys, resolving both keys and values as we go
 96  1
                 for (String key : keys) {
 97  123
                         resolveProperty(new ResolvePropertyContext(keys, key, properties, resolvedProperties, retriever));
 98  
                 }
 99  1
                 return resolvedProperties;
 100  
         }
 101  
 
 102  
         protected void resolveProperty(ResolvePropertyContext ctx) {
 103  123
                 String key = ctx.getKey();
 104  123
                 Properties properties = ctx.getProperties();
 105  
                 // First resolve the key
 106  123
                 String resolvedKey = resolve(key, ctx.getRetriever());
 107  
 
 108  
                 // Emit an info level message when a key is resolved to a new value
 109  123
                 if (!key.equals(resolvedKey)) {
 110  1
                         logger.info("Resolved key [{}]->[{}]", key, resolvedKey);
 111  
                 }
 112  
 
 113  
                 // Obtain a value for the key
 114  123
                 String value = properties.getProperty(key);
 115  
                 // Resolve any placeholders in the value
 116  123
                 String resolvedValue = resolve(value, ctx.getRetriever());
 117  
 
 118  
                 // Check for duplicates
 119  123
                 duplicatePropertyCheck(ctx, resolvedKey, resolvedValue);
 120  
 
 121  
                 // Emit an info level message when a value gets resolved to something new
 122  123
                 if (!value.equals(resolvedValue)) {
 123  6
                         logger.info("Resolved value for '" + resolvedKey + "'  [{}]->[{}]",
 124  
                                         plogger.getLogValue(resolvedKey, value), plogger.getLogValue(resolvedKey, resolvedValue));
 125  
                 }
 126  123
                 logger.debug("Adding resolved property {}=[{}]", resolvedKey, plogger.getLogValue(resolvedKey, resolvedValue));
 127  
                 // Store the resolved key/value pair
 128  123
                 ctx.getResolvedProperties().setProperty(resolvedKey, resolvedValue);
 129  123
         }
 130  
 
 131  
         /**
 132  
          * A duplicate property can occur when resolving placeholders inside a key.
 133  
          * 
 134  
          * For example, given a properties file with the following entries:<br>
 135  
          * <code>
 136  
          * cat=lion
 137  
          * lion.speed=fast
 138  
          * ${cat}.speed=slow
 139  
          * </code>
 140  
          * 
 141  
          * The key ${cat}.speed will resolve to 'lion.speed' which duplicates the original entry in the properties file
 142  
          */
 143  
         protected void duplicatePropertyCheck(ResolvePropertyContext ctx, String resolvedKey, String resolvedValue) {
 144  123
                 String rawKey = ctx.getKey();
 145  123
                 Properties properties = ctx.getProperties();
 146  
                 // The keys are the same, can't be a duplicate property, nothing to do
 147  123
                 if (rawKey.equals(resolvedKey)) {
 148  122
                         return;
 149  
                 }
 150  
 
 151  
                 // The keys are not the same, but resolvedKey is a new key, nothing to do
 152  1
                 if (!ctx.getKeys().contains(resolvedKey)) {
 153  1
                         return;
 154  
                 }
 155  
 
 156  
                 // If we get this far, the keys are not the same, and resolved key points to a property that already exists
 157  
 
 158  
                 // Extract the existing value
 159  0
                 String existingValue = properties.getProperty(resolvedKey);
 160  
 
 161  
                 // Check the values
 162  0
                 if (ObjectUtils.nullSafeEquals(existingValue, resolvedValue)) {
 163  
                         // The values are the same, just emit a warning and proceed
 164  0
                         logger.warn("Duplicate property detected for '" + resolvedKey + "'.  '" + rawKey + "' resolved to '"
 165  
                                         + resolvedKey + "' which already has a value. Both values are the same: [{}]",
 166  
                                         plogger.getLogValue(resolvedKey, existingValue));
 167  0
                         return;
 168  
                 } else {
 169  
                         // Otherwise we have an issue
 170  0
                         StringBuilder sb = new StringBuilder();
 171  0
                         sb.append("Duplicate property value detected for '" + resolvedKey + "'.");
 172  0
                         sb.append("  The key '" + rawKey + "' resolved to '" + resolvedKey
 173  
                                         + "' and there is a value for that key already present.");
 174  0
                         sb.append("  Existing value:[" + existingValue + "]  New Value:[" + resolvedValue + "]");
 175  0
                         throw new IllegalArgumentException(sb.toString());
 176  
                 }
 177  
         }
 178  
 
 179  
         public boolean isSort() {
 180  2
                 return sort;
 181  
         }
 182  
 
 183  
         public void setSort(boolean sort) {
 184  1
                 this.sort = sort;
 185  1
         }
 186  
 
 187  
 }