001package co.codewizards.cloudstore.core.util; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.FileOutputStream; 006import java.io.IOException; 007import java.util.ArrayList; 008import java.util.Collection; 009import java.util.HashMap; 010import java.util.Map; 011import java.util.Properties; 012import java.util.regex.Matcher; 013import java.util.regex.Pattern; 014 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018/** 019 * {@link java.util.Properties} utilities. 020 * @author Marc Klinger - marc[at]nightlabs[dot]de 021 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 022 */ 023public final class PropertiesUtil 024{ 025 private static final Logger logger = LoggerFactory.getLogger(PropertiesUtil.class); 026 027 private PropertiesUtil() { } 028 029 /** 030 * Suffix appended to the real property-key to store the boolean flag whether 031 * the real property represents the <code>null</code> value. 032 * <p> 033 * It is not possible to store a <code>null</code> value in a {@link Properties} instance (and neither it is 034 * in a properties file). But sometimes it is necessary to explicitly formulate a <code>null</code> value, 035 * for example when overriding a property in a way as if it had not been specified in the overridden properties. 036 * <p> 037 * For example, let there be these properties declared in a persistence unit: 038 * <pre> 039 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 040 * javax.jdo.option.TransactionType = JTA 041 * javax.persistence.jtaDataSource = jdbc/someDataSource 042 * javax.persistence.transactionType = JTA 043 * </pre> 044 * <p> 045 * If the transaction type is to be overridden by "RESOURCE_LOCAL", this is straight-forward: 046 * <p> 047 * <pre> 048 * javax.jdo.option.TransactionType = RESOURCE_LOCAL 049 * javax.persistence.transactionType = RESOURCE_LOCAL 050 * </pre> 051 * <p> 052 * But to override the datasource properties to be null, is not possible by simply writing 053 * "javax.jdo.option.ConnectionFactoryName = = " as this would 054 * be interpreted as empty string. Therefore it is possible to declare the <code>null</code> value using an additional 055 * key: 056 * <pre> 057 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 058 * javax.jdo.option.ConnectionFactoryName.null = true 059 * javax.persistence.jtaDataSource.null = true 060 * </pre> 061 * It is not necessary to quote the referenced key as shown in the 2nd example ("javax.persistence.jtaDataSource" is 062 * not present). However, if it is present ("javax.jdo.option.ConnectionFactoryName" above), the 063 * null-indicating meta-property "javax.jdo.option.ConnectionFactoryName.null" overrides the value "jdbc/someDataSource". 064 * 065 * @see #getMetaPropertyKeyNullValue(String) 066 * @see #filterProperties(Map, Map) 067 */ 068 public static final String SUFFIX_NULL_VALUE = ".null"; 069 070 /** 071 * Get all matches where property keys match the given pattern. 072 * @param properties The properties to match 073 * @param pattern The pattern to match against 074 * @return The {@link Matcher}s that matched. 075 */ 076 public static Collection<Matcher> getPropertyKeyMatches(java.util.Properties properties, Pattern pattern) 077 { 078 Collection<Matcher> matches = new ArrayList<Matcher>(); 079 for (Object element : properties.keySet()) { 080 String key = (String) element; 081 Matcher m = pattern.matcher(key); 082 if(m.matches()) 083 matches.add(m); 084 } 085 return matches; 086 } 087 088 /** 089 * Get all properties whose keys start with the given prefix. 090 * <p> 091 * The returned property elements have the form <code>(key,value)</code> where <code>key</code> is 092 * the part of the original key after the given <code>keyPrefix</code> and <code>value</code> is 093 * the original value. 094 * </p> 095 * @param properties The properties to filter. 096 * @param keyPrefix The kex prefix to use 097 * @return the properties that start with the given prefix 098 */ 099 public static java.util.Properties getProperties(java.util.Properties properties, String keyPrefix) 100 { 101 java.util.Properties newProperties = new java.util.Properties(); 102 Collection<Matcher> matches = getPropertyKeyMatches(properties, Pattern.compile("^"+Pattern.quote(keyPrefix)+"(.*)$")); 103 for (Matcher m : matches) 104 newProperties.put(m.group(1), properties.get(m.group(0))); 105 return newProperties; 106 } 107 108 public static void putAll(java.util.Properties source, java.util.Properties target) 109 { 110 for (Object element : source.keySet()) { 111 String key = (String) element; 112 target.setProperty(key, source.getProperty(key)); 113 } 114 } 115 116 public static java.util.Properties load(String filename) throws IOException 117 { 118 return load(filename != null ? new File(filename) : null); 119 } 120 121 public static java.util.Properties load(File file) throws IOException 122 { 123 FileInputStream in = new FileInputStream(file); 124 try { 125 java.util.Properties properties = new java.util.Properties(); 126 properties.load(in); 127 return properties; 128 } finally { 129 in.close(); 130 } 131 } 132 133 public static void store(String filename, java.util.Properties properties, String comment) throws IOException 134 { 135 store(filename != null ? new File(filename) : null, properties, comment); 136 } 137 138 public static void store(File file, java.util.Properties properties, String comment) throws IOException 139 { 140 FileOutputStream out = new FileOutputStream(file); 141 try { 142 properties.store(out, comment); 143 } finally { 144 out.close(); 145 } 146 } 147 148 /** 149 * Filter the given raw properties. 150 * <p> 151 * This is a convenience method delegating to 152 * {@link #filterProperties(Map, Map)} with <code>variables == null</code>. 153 * @param rawProperties the properties to be filtered; must not be <code>null</code>. 154 * @return the filtered properties. 155 * @see #filterProperties(Map, Map) 156 */ 157 public static Map<String, String> filterProperties(Map<?, ?> rawProperties) 158 { 159 return filterProperties(rawProperties, null); 160 } 161 162 /** 163 * Filter the given raw properties. 164 * <p> 165 * <u>Replace null-meta-data to <code>null</code> values:</u> Every property for which the 166 * method {@link #isNullValue(Map, String)} returns <code>true</code> is written with a <code>null</code> 167 * value into the result-map. Every property for which the method {@link #isMetaPropertyKeyNullValue(String)} 168 * returns <code>true</code>, is ignored (i.e. does not occur in the result-map). 169 * <p> 170 * It is not possible to store a <code>null</code> value in a {@link Properties} instance (and neither it is 171 * in a properties file). But sometimes it is necessary to explicitly formulate a <code>null</code> value, 172 * for example when overriding a property in a way as if it had not been specified in the overridden properties. 173 * <p> 174 * For example, let there be these properties declared in a persistence unit: 175 * <pre> 176 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 177 * javax.jdo.option.TransactionType = JTA 178 * javax.persistence.jtaDataSource = jdbc/someDataSource 179 * javax.persistence.transactionType = JTA 180 * </pre> 181 * <p> 182 * If the transaction type is to be overridden by "RESOURCE_LOCAL", this is straight-forward: 183 * <p> 184 * <pre> 185 * javax.jdo.option.TransactionType = RESOURCE_LOCAL 186 * javax.persistence.transactionType = RESOURCE_LOCAL 187 * </pre> 188 * <p> 189 * But to override the datasource properties to be null, is not possible by simply writing 190 * "javax.jdo.option.ConnectionFactoryName = = " as this would 191 * be interpreted as empty string. Therefore it is possible to declare the <code>null</code> value using an additional 192 * key: 193 * <pre> 194 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 195 * javax.jdo.option.ConnectionFactoryName.null = true 196 * javax.persistence.jtaDataSource.null = true 197 * </pre> 198 * It is not necessary to quote the referenced key as shown in the 2nd example ("javax.persistence.jtaDataSource" is 199 * not present). However, if it is present ("javax.jdo.option.ConnectionFactoryName" above), the 200 * null-indicating meta-property "javax.jdo.option.ConnectionFactoryName.null" overrides the value "jdbc/someDataSource". 201 * <p> 202 * <u>Replace template variables:</u> If the optional <code>variables</code> argument is present, every 203 * value is filtered using {@link IOUtil#replaceTemplateVariables(String, Map)}. 204 * <p> 205 * For example, let there be these properties: 206 * <pre> 207 * some.url1 = file:/tmp/myTempDir 208 * some.url2 = http://host.domain.tld/tomcat 209 * </pre> 210 * <p> 211 * If this method is called with {@link System#getProperties()} as <code>variables</code> and the user "marco" is currently 212 * working on a linux machine, the resulting <code>Map</code> will contain the following resolved properties: 213 * <pre> 214 * some.url1 = file:/tmp/myTempDir 215 * some.url2 = http://host.domain.tld/marco 216 * </pre> 217 * @param rawProperties the properties to be filtered; must not be <code>null</code>. 218 * @param variables optional template variables; if present, every value is filtered using 219 * {@link IOUtil#replaceTemplateVariables(String, Map)}. 220 * @return the filtered properties. 221 */ 222 public static Map<String, String> filterProperties(Map<?, ?> rawProperties, Map<?, ?> variables) 223 { 224 if (rawProperties == null) 225 throw new IllegalArgumentException("rawProperties == null"); 226 227 Map<String, String> filteredProperties = new HashMap<String, String>(); 228 for (Map.Entry<?, ?> me : rawProperties.entrySet()) { 229 String key = me.getKey() == null ? null : me.getKey().toString(); 230 String value = me.getValue() == null ? null : me.getValue().toString(); 231 232 if (isMetaPropertyKey(key)) { 233 if (isMetaPropertyKeyNullValue(key) && Boolean.parseBoolean(value)) { 234 String refKey = getReferencedPropertyKeyForMetaPropertyKey(key); 235 filteredProperties.put(refKey, null); 236 } 237 continue; 238 } 239 240 if (value != null && isNullValue(rawProperties, key)) 241 value = null; 242 243 if (value != null && variables != null) 244 value = IOUtil.replaceTemplateVariables(value, variables); 245 246 filteredProperties.put(key, value); 247 } 248 return filteredProperties; 249 } 250 251 /** 252 * Determine, if the given property-key is a <code>null</code>-indicating meta-property for another property. 253 * <p> 254 * It is not possible to store a <code>null</code> value in a {@link Properties} instance (and neither it is 255 * in a properties file). But sometimes it is necessary to explicitly formulate a <code>null</code> value, 256 * for example when overriding a property in a way as if it had not been specified in the overridden properties. 257 * <p> 258 * For example, let there be these properties declared in a persistence unit: 259 * <pre> 260 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 261 * javax.jdo.option.TransactionType = JTA 262 * javax.persistence.jtaDataSource = jdbc/someDataSource 263 * javax.persistence.transactionType = JTA 264 * </pre> 265 * <p> 266 * If the transaction type is to be overridden by "RESOURCE_LOCAL", this is straight-forward: 267 * <p> 268 * <pre> 269 * javax.jdo.option.TransactionType = RESOURCE_LOCAL 270 * javax.persistence.transactionType = RESOURCE_LOCAL 271 * </pre> 272 * <p> 273 * But to override the datasource properties to be null, is not possible by simply writing 274 * "javax.jdo.option.ConnectionFactoryName = = " as this would 275 * be interpreted as empty string. Therefore it is possible to declare the <code>null</code> value using an additional 276 * key: 277 * <pre> 278 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 279 * javax.jdo.option.ConnectionFactoryName.null = true 280 * javax.persistence.jtaDataSource.null = true 281 * </pre> 282 * It is not necessary to quote the referenced key as shown in the 2nd example ("javax.persistence.jtaDataSource" is 283 * not present). However, if it is present ("javax.jdo.option.ConnectionFactoryName" above), the 284 * null-indicating meta-property "javax.jdo.option.ConnectionFactoryName.null" overrides the value "jdbc/someDataSource". 285 * 286 * @param key the property-key to check. 287 * @return <code>true</code>, if the given key references a property that is a <code>null</code>-indicating 288 * meta-property for another property. 289 * @see #isMetaPropertyKey(String) 290 */ 291 public static boolean isMetaPropertyKeyNullValue(String key) 292 { 293 if (key == null) 294 return false; 295 296 return key.endsWith(SUFFIX_NULL_VALUE); 297 } 298 299 /** 300 * Determine, if the given property-key is a meta-property for another property. 301 * <p> 302 * Currently, this is equivalent to {@link #isMetaPropertyKeyNullValue(String)}, but other 303 * meta-properties might be introduced later. 304 * @param key the property-key to check. 305 * @return <code>true</code>, if the given key references a property that is a meta-property 306 * for another property. 307 */ 308 public static boolean isMetaPropertyKey(String key) 309 { 310 return isMetaPropertyKeyNullValue(key); 311 } 312 313 /** 314 * Get the referenced property-key for the given meta-property's key. 315 * @param key a meta-property's key - for example the <code>null</code>-indicating property-key 316 * "some.prop.null". 317 * @return the referenced property-key - for example "some.prop". 318 * @see #SUFFIX_NULL_VALUE 319 * @see #getMetaPropertyKeyNullValue(String) 320 */ 321 public static String getReferencedPropertyKeyForMetaPropertyKey(String key) 322 { 323 if (!isMetaPropertyKeyNullValue(key)) 324 throw new IllegalArgumentException("key='" + key + "' is not a meta-property!"); 325 326 return key.substring(0, key.length() - SUFFIX_NULL_VALUE.length()); 327 } 328 329 /** 330 * Get the <code>null</code>-indicating meta-property's key for the given real property's key. 331 * @param key a property-key - for example "some.prop". 332 * @return the <code>null</code>-indicating meta-property's key - for example "some.prop.null". 333 * @see #SUFFIX_NULL_VALUE 334 * @see #getReferencedPropertyKeyForMetaPropertyKey(String) 335 */ 336 public static String getMetaPropertyKeyNullValue(String key) 337 { 338 if (key == null) 339 key = String.valueOf(key); 340 341 if (isMetaPropertyKeyNullValue(key)) 342 throw new IllegalArgumentException("key='" + key + "' is already a meta-property indicating a null-value!"); 343 344 return key + SUFFIX_NULL_VALUE; 345 } 346 347 /** 348 * Determine, if the property identified by the given <code>key</code> has a <code>null</code>-value. 349 * <p> 350 * It is not possible to store a <code>null</code> value in a {@link Properties} instance (and neither it is 351 * in a properties file). But sometimes it is necessary to explicitly formulate a <code>null</code> value, 352 * for example when overriding a property in a way as if it had not been specified in the overridden properties. 353 * <p> 354 * For example, let there be these properties declared in a persistence unit: 355 * <pre> 356 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 357 * javax.jdo.option.TransactionType = JTA 358 * javax.persistence.jtaDataSource = jdbc/someDataSource 359 * javax.persistence.transactionType = JTA 360 * </pre> 361 * <p> 362 * If the transaction type is to be overridden by "RESOURCE_LOCAL", this is straight-forward: 363 * <p> 364 * <pre> 365 * javax.jdo.option.TransactionType = RESOURCE_LOCAL 366 * javax.persistence.transactionType = RESOURCE_LOCAL 367 * </pre> 368 * <p> 369 * But to override the datasource properties to be null, is not possible by simply writing 370 * "javax.jdo.option.ConnectionFactoryName = = " as this would 371 * be interpreted as empty string. Therefore it is possible to declare the <code>null</code> value using an additional 372 * key: 373 * <pre> 374 * javax.jdo.option.ConnectionFactoryName = jdbc/someDataSource 375 * javax.jdo.option.ConnectionFactoryName.null = true 376 * javax.persistence.jtaDataSource.null = true 377 * </pre> 378 * It is not necessary to quote the referenced key as shown in the 2nd example ("javax.persistence.jtaDataSource" is 379 * not present). However, if it is present ("javax.jdo.option.ConnectionFactoryName" above), the 380 * null-indicating meta-property "javax.jdo.option.ConnectionFactoryName.null" overrides the value "jdbc/someDataSource". 381 * 382 * @param properties the properties. Must not be <code>null</code>. 383 * @param key the property-key for which to determine, whether its value is <code>null</code>. 384 * @return <code>true</code>, if the property referenced by the given <code>key</code> is <code>null</code>; 385 * <code>false</code> otherwise. 386 */ 387 public static boolean isNullValue(Map<?, ?> properties, String key) 388 { 389 if (properties == null) 390 throw new IllegalArgumentException("properties == null"); 391 392 if (properties.get(key) == null) 393 return true; 394 395 if (isMetaPropertyKeyNullValue(key)) 396 return false; 397 398 String metaNullValue = String.valueOf(properties.get(getMetaPropertyKeyNullValue(key))); 399 return Boolean.parseBoolean(metaNullValue); 400 } 401 402 public static int getSystemPropertyValueAsInt(String key, int defaultValue) { 403 long value = getSystemPropertyValueAsLong(key, defaultValue); 404 if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { 405 logger.warn("System property '{}' is set to the value '{}' which is out of range for a 32-bit integer. Falling back to default value {}.", 406 key, value, defaultValue); 407 return defaultValue; 408 } 409 return (int) value; 410 } 411 412 public static long getSystemPropertyValueAsLong(String key, long defaultValue) { 413 String value = System.getProperty(key); 414 if (value == null) { 415 logger.debug("System property '{}' is not set. Falling back to default value {}.", key, defaultValue); 416 return defaultValue; 417 } 418 try { 419 return Integer.valueOf(value); 420 } catch (NumberFormatException x) { 421 logger.warn("System property '{}' is set to the value '{}' which is not an integer (long). Falling back to default value {}.", 422 key, value, defaultValue); 423 return defaultValue; 424 } 425 } 426}