001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.util; 021 022 023 024 import java.io.File; 025 import java.io.InputStream; 026 import java.io.IOException; 027 import java.io.FileInputStream; 028 import java.util.Enumeration; 029 import java.util.Hashtable; 030 import java.util.Properties; 031 032 import org.apache.directory.shared.ldap.NotImplementedException; 033 034 035 /** 036 * A utility class used for accessing, finding, merging and macro expanding 037 * properties, on disk, via URLS or as resources. 038 * 039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 040 * @version $Rev: 686082 $ 041 */ 042 public class PropertiesUtils 043 { 044 /** default properties file extension */ 045 private static final String DOTPROPERTIES = ".properties"; 046 047 048 // ------------------------------------------------------------------------ 049 // Utilities for discovering Properties 050 // ------------------------------------------------------------------------ 051 052 /** 053 * Loads a properties object in a properties file if it exists relative to 054 * the filename ${user.home}. If the file ${user.home}/[filename] does not 055 * exist then one last attempt to find the file is made if filename does not 056 * have a .properties extension. If so and 057 * ${user.home}/[filename].properties exists then it is loaded. 058 * 059 * @param filename 060 * the properties file name with or without an extension 061 * @return the user properties object 062 */ 063 public static Properties findUserProperties( String filename ) 064 { 065 return findProperties( new File( System.getProperty( "user.home" ) ), filename ); 066 } 067 068 069 /** 070 * Create a new properties object and load the properties file if it exists 071 * relative to [dir]/[filename] or [dir]/[filename].properties. 072 * 073 * @param dir 074 * the base directory 075 * @param filename 076 * the full fine name or the base name w/o the extension 077 * @return the loaded properties object 078 */ 079 public static Properties findProperties( File dir, String filename ) 080 { 081 final File asis = new File( dir, filename ); 082 083 if ( asis.exists() ) 084 { 085 return getProperties( asis ); 086 } 087 088 if ( filename.endsWith( DOTPROPERTIES ) ) 089 { 090 String noExt = filename.substring( 0, filename.length() - 11 ); 091 if ( new File( dir, noExt ).exists() ) 092 { 093 return getProperties( new File( dir, noExt ) ); 094 } 095 096 return new Properties(); 097 } 098 099 File withExt = new File( dir, filename + DOTPROPERTIES ); 100 if ( withExt.exists() ) 101 { 102 return getProperties( withExt ); 103 } 104 105 return new Properties(); 106 } 107 108 109 /** 110 * Load a properties from a resource relative to a supplied class. First an 111 * attempt is made to locate a property file colocated with the class with 112 * the name [class].properties. If this cannot be found or errors result an 113 * empty Properties file is returned. 114 * 115 * @param ref 116 * a class to use for relative path references 117 * @return the static properties 118 */ 119 public static Properties getStaticProperties( Class<?> ref ) 120 { 121 final Properties properties = new Properties(); 122 final String address = ref.toString().replace( '.', '/' ); 123 final String path = address + ".properties"; 124 InputStream input = ref.getResourceAsStream( path ); 125 126 if ( null != input ) 127 { 128 try 129 { 130 properties.load( input ); 131 } 132 catch ( IOException e ) 133 { 134 return properties; 135 } 136 } 137 138 return properties; 139 } 140 141 142 /** 143 * Load properties from a resource relative to a supplied class and path. 144 * 145 * @param ref 146 * a class to use for relative path references 147 * @param path 148 * the relative path to the resoruce 149 * @return the static properties 150 */ 151 public static Properties getStaticProperties( Class<?> ref, String path ) 152 { 153 Properties properties = new Properties(); 154 InputStream input = ref.getResourceAsStream( path ); 155 156 if ( input == null ) 157 { 158 return properties; 159 } 160 161 try 162 { 163 properties.load( input ); 164 } 165 catch ( IOException e ) 166 { 167 return properties; 168 } 169 170 return properties; 171 } 172 173 174 /** 175 * Creates a properties object and loads the properties in the file 176 * otherwise and empty property object will be returned. 177 * 178 * @param file 179 * the properties file 180 * @return the properties object 181 */ 182 public static Properties getProperties( File file ) 183 { 184 Properties properties = new Properties(); 185 186 if ( null == file ) 187 { 188 return properties; 189 } 190 191 if ( file.exists() ) 192 { 193 try 194 { 195 final FileInputStream fis = new FileInputStream( file ); 196 try 197 { 198 properties.load( fis ); 199 } 200 finally 201 { 202 fis.close(); 203 } 204 } 205 catch ( IOException e ) 206 { 207 return properties; 208 } 209 } 210 211 return properties; 212 } 213 214 215 /** 216 * Loads a properties file as a CL resource if it exists and returns an 217 * empty Properties object otherwise. 218 * 219 * @param classloader 220 * the loader to use for the resources 221 * @param path 222 * the path to the resource 223 * @return the loaded or new Properties 224 */ 225 public static Properties getProperties( ClassLoader classloader, String path ) 226 { 227 Properties properties = new Properties(); 228 InputStream input = classloader.getResourceAsStream( path ); 229 230 if ( input != null ) 231 { 232 try 233 { 234 properties.load( input ); 235 } 236 catch ( IOException e ) 237 { 238 return properties; 239 } 240 } 241 242 return properties; 243 } 244 245 246 /** 247 * Loads a properties file as a class resource if it exists and returns an 248 * empty Properties object otherwise. 249 * 250 * @param clazz 251 * the class to use for resolving the resources 252 * @param path 253 * the relative path to the resource 254 * @return the loaded or new Properties 255 */ 256 public static Properties getProperties( Class<?> clazz, String path ) 257 { 258 Properties properties = new Properties(); 259 InputStream input = clazz.getResourceAsStream( path ); 260 261 if ( input != null ) 262 { 263 try 264 { 265 properties.load( input ); 266 } 267 catch ( IOException e ) 268 { 269 return properties; 270 } 271 } 272 273 return properties; 274 } 275 276 277 // ------------------------------------------------------------------------ 278 // Utilities for operating on or setting Properties values 279 // ------------------------------------------------------------------------ 280 281 /** 282 * Expands out a set of property key macros in the following format 283 * ${foo.bar} where foo.bar is a property key, by dereferencing the value of 284 * the key using the original source Properties and other optional 285 * Properties. If the original expanded Properties contain the value for the 286 * macro key, foo.bar, then dereferencing stops by using the value in the 287 * expanded Properties: the other optional Properties are NOT used at all. 288 * If the original expanded Properties do NOT contain the value for the 289 * macro key, then the optional Properties are used in order. The first of 290 * the optionals to contain the value for the macro key (foo.bar) shorts the 291 * search. Hence the first optional Properties in the array to contain a 292 * value for the macro key (foo.bar) is used to set the expanded value. If a 293 * macro cannot be expanded because it's key was not defined within the 294 * expanded Properties or one of the optional Properties then it is left as 295 * is. 296 * 297 * @param expanded 298 * the Properties to perform the macro expansion upon 299 * @param optionals 300 * null or an optional set of Properties to use for dereferencing 301 * macro keys (foo.bar) 302 */ 303 public static void macroExpand( Properties expanded, Properties[] optionals ) 304 { 305 // Handle null optionals 306 if ( null == optionals ) 307 { 308 optionals = new Properties[0]; 309 } 310 311 Enumeration<?> list = expanded.propertyNames(); 312 313 while ( list.hasMoreElements() ) 314 { 315 String key = ( String ) list.nextElement(); 316 String macro = expanded.getProperty( key ); 317 318 int n = macro.indexOf( "${" ); 319 if ( n < 0 ) 320 { 321 continue; 322 } 323 324 int m = macro.indexOf( "}", n + 2 ); 325 if ( m < 0 ) 326 { 327 continue; 328 } 329 330 final String symbol = macro.substring( n + 2, m ); 331 332 if ( expanded.containsKey( symbol ) ) 333 { 334 final String value = expanded.getProperty( symbol ); 335 final String head = macro.substring( 0, n ); 336 final String tail = macro.substring( m + 1 ); 337 final String resolved = head + value + tail; 338 expanded.put( key, resolved ); 339 continue; 340 } 341 342 /* 343 * Check if the macro key exists within the array of optional 344 * Properties. Set expanded value to first Properties with the key 345 * and break out of the loop. 346 */ 347 for ( int ii = 0; ii < optionals.length; ii++ ) 348 { 349 if ( optionals[ii].containsKey( symbol ) ) 350 { 351 final String value = optionals[ii].getProperty( symbol ); 352 final String head = macro.substring( 0, n ); 353 final String tail = macro.substring( m + 1 ); 354 final String resolved = head + value + tail; 355 expanded.put( key, resolved ); 356 break; 357 } 358 } 359 } 360 } 361 362 363 /** 364 * Discovers a value within a set of Properties either halting on the first 365 * time the property is discovered or continuing on to take the last value 366 * found for the property key. 367 * 368 * @param key 369 * a property key 370 * @param sources 371 * a set of source Properties 372 * @param haltOnDiscovery 373 * true if we stop on finding a value, false otherwise 374 * @return the value found or null 375 */ 376 public static String discover( String key, Properties[] sources, boolean haltOnDiscovery ) 377 { 378 String retval = null; 379 380 for ( int ii = 0; ii < sources.length; ii++ ) 381 { 382 if ( sources[ii].containsKey( key ) ) 383 { 384 retval = sources[ii].getProperty( key ); 385 386 if ( haltOnDiscovery ) 387 { 388 break; 389 } 390 } 391 } 392 393 return retval; 394 } 395 396 397 /** 398 * Merges a set of properties from source Properties into a target 399 * properties instance containing keys. This method does not allow null 400 * overrides. 401 * 402 * @param keys 403 * the keys to discover values for 404 * @param sources 405 * the sources to search 406 * @param haltOnDiscovery 407 * true to halt on first find or false to continue to last find 408 */ 409 public static void discover( Properties keys, Properties[] sources, boolean haltOnDiscovery ) 410 { 411 if ( null == sources || null == keys ) 412 { 413 return; 414 } 415 416 /* 417 * H A N D L E S I N G L E V A L U E D K E Y S 418 */ 419 for ( Object key:keys.keySet() ) 420 { 421 String value = discover( (String)key, sources, haltOnDiscovery ); 422 423 if ( value != null ) 424 { 425 keys.setProperty( (String)key, value ); 426 } 427 } 428 } 429 430 431 // ------------------------------------------------------------------------ 432 // Various Property Accessors 433 // ------------------------------------------------------------------------ 434 435 /** 436 * Gets a String property as a boolean returning a defualt if the key is not 437 * present. In any case, true, on, 1 and yes strings return true and 438 * everything else returns 439 * 440 * @param props 441 * the properties to get the value from 442 * @param key 443 * the property key 444 * @param defaultValue 445 * the default value to return if key is not present 446 * @return true defaultValue if property does not exist, else return true if 447 * the String value is one of 'true', 'on', '1', 'yes', otherwise 448 * false is returned 449 */ 450 public static boolean get( Properties props, String key, boolean defaultValue ) 451 { 452 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null ) 453 { 454 return defaultValue; 455 } 456 457 String val = props.getProperty( key ).trim().toLowerCase(); 458 return val.equals( "true" ) || val.equals( "on" ) || val.equals( "1" ) || val.equals( "yes" ); 459 } 460 461 462 /** 463 * Gets a property or entry value from a hashtable and tries to transform 464 * whatever the value may be to an primitive integer. 465 * 466 * @param ht 467 * the hashtable to access for the value 468 * @param key 469 * the key to use when accessing the ht 470 * @param defval 471 * the default value to use if the key is not contained in ht or 472 * if the value cannot be represented as a primitive integer. 473 * @return the primitive integer representation of a hashtable value 474 */ 475 public static int get( Hashtable<String, Object> ht, Object key, int defval ) 476 { 477 if ( ht == null || !ht.containsKey( key ) || ht.get( key ) == null ) 478 { 479 return defval; 480 } 481 482 Object obj = ht.get( key ); 483 484 if ( obj instanceof Byte ) 485 { 486 return ( ( Byte ) obj ).intValue(); 487 } 488 if ( obj instanceof Short ) 489 { 490 return ( ( Short ) obj ).intValue(); 491 } 492 if ( obj instanceof Integer ) 493 { 494 return ( ( Integer ) obj ).intValue(); 495 } 496 if ( obj instanceof Long ) 497 { 498 return ( ( Long ) obj ).intValue(); 499 } 500 if ( obj instanceof String ) 501 { 502 try 503 { 504 return Integer.parseInt( ( String ) obj ); 505 } 506 catch ( NumberFormatException ne ) 507 { 508 ne.printStackTrace(); 509 return defval; 510 } 511 } 512 513 return defval; 514 } 515 516 517 public static long get( Properties props, String key, long defaultValue ) 518 { 519 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null ) 520 { 521 return defaultValue; 522 } 523 524 throw new NotImplementedException(); 525 } 526 527 528 public static byte get( Properties props, String key, byte defaultValue ) 529 { 530 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null ) 531 { 532 return defaultValue; 533 } 534 535 throw new NotImplementedException(); 536 } 537 538 539 public static char get( Properties props, String key, char defaultValue ) 540 { 541 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null ) 542 { 543 return defaultValue; 544 } 545 546 throw new NotImplementedException(); 547 } 548 }