1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: Property.java,v 1.1.1.1.2.4 2004/07/16 23:32:04 vlad_r Exp $ 8 */ 9 package com.vladium.util; 10 11 import java.io.BufferedInputStream; 12 import java.io.File; 13 import java.io.FileInputStream; 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.util.Enumeration; 17 import java.util.Hashtable; 18 import java.util.Iterator; 19 import java.util.Map; 20 import java.util.Properties; 21 22 /* 23 * NOTE: to avoid certain build problems, this class should use only 24 * core Java APIs and not any app infrastructure. 25 */ 26 27 // ---------------------------------------------------------------------------- 28 /** 29 * @author Vlad Roubtsov, (C) 2003 30 */ 31 public 32 abstract class Property 33 { 34 // public: ................................................................ 35 36 37 public static boolean toBoolean (final String value) 38 { 39 if (value == null) 40 return false; 41 else 42 return value.startsWith ("t") || value.startsWith ("y"); 43 } 44 45 46 /** 47 * NOTE: this does not guarantee that the result will be mutatable 48 * independently from 'overrides' or 'base', so this method 49 * should be used for read-only property only 50 * 51 * @param overrides [null is equivalent to empty] 52 * @param base [null is equivalent to empty] 53 * 54 * @return [never null, could be empty] 55 */ 56 public static Properties combine (final Properties overrides, final Properties base) 57 { 58 // note: no defensive copies here 59 60 if (base == null) 61 { 62 if (overrides == null) 63 return new XProperties (); 64 else 65 return overrides; 66 } 67 68 // [assertion: base != null] 69 70 if (overrides == null) return base; 71 72 // [assertion: both 'overrides' and 'base' are not null] 73 74 final Properties result = new XProperties (base); 75 76 // note: must use propertyNames() because that is the only method that recurses 77 // into possible bases inside 'overrides' 78 79 for (Enumeration overrideNames = overrides.propertyNames (); overrideNames.hasMoreElements (); ) 80 { 81 final String n = (String) overrideNames.nextElement (); 82 final String v = overrides.getProperty (n); 83 84 result.setProperty (n, v); 85 } 86 87 return result; 88 } 89 90 /** 91 * Creates a set of properties for an application with a given namespace. 92 * This method is not property aliasing-aware. 93 * 94 * @param namespace application namespace [may not be null] 95 * @param loader classloader to use for any classloader resource lookups 96 * [null is equivalent to the applicaton classloader] 97 * @return application properties [never null, a new instance is created 98 * on each invocation] 99 */ 100 public static Properties getAppProperties (final String namespace, final ClassLoader loader) 101 { 102 if (namespace == null) 103 throw new IllegalArgumentException ("null properties: appNameLC"); 104 105 final Properties appDefaults = Property.getProperties (namespace + "_default.properties", loader); 106 final Properties systemFileOverrides; 107 { 108 final String fileName = Property.getSystemProperty (namespace + ".properties"); 109 final File file = fileName != null 110 ? new File (fileName) 111 : null; 112 113 systemFileOverrides = Property.getLazyPropertiesFromFile (file); 114 } 115 final Properties systemOverrides = Property.getSystemProperties (namespace); 116 final Properties resOverrides = Property.getProperties (namespace + ".properties", loader); 117 118 return combine (resOverrides, 119 combine (systemOverrides, 120 combine (systemFileOverrides, 121 appDefaults))); 122 } 123 124 public static Properties getSystemProperties (final String systemPrefix) 125 { 126 // note: this method is not synchronized on purpose 127 128 Properties result = s_systemProperties; 129 if (result == null) 130 { 131 result = new SystemPropertyLookup (systemPrefix); 132 133 s_systemProperties = result; 134 return result; 135 } 136 137 return result; 138 } 139 140 public static Properties getSystemPropertyRedirects (final Map systemRedirects) 141 { 142 // note: this method is not synchronized on purpose 143 144 Properties result = s_systemRedirects; 145 if (result == null) 146 { 147 result = new SystemRedirectsLookup (systemRedirects); 148 149 s_systemRedirects = result; 150 return result; 151 } 152 153 return result; 154 } 155 156 157 public static String getSystemFingerprint () 158 { 159 // [not synchronized intentionally] 160 161 if (s_systemFingerprint != null) 162 return s_systemFingerprint; 163 else 164 { 165 final StringBuffer s = new StringBuffer (); 166 final char delimiter = ':'; 167 168 s.append (getSystemProperty ("java.vm.name", "")); 169 s.append (delimiter); 170 s.append (getSystemProperty ("java.vm.version", "")); 171 s.append (delimiter); 172 s.append (getSystemProperty ("java.vm.vendor", "")); 173 s.append (delimiter); 174 s.append (getSystemProperty ("os.name", "")); 175 s.append (delimiter); 176 s.append (getSystemProperty ("os.version", "")); 177 s.append (delimiter); 178 s.append (getSystemProperty ("os.arch", "")); 179 180 s_systemFingerprint = s.toString (); 181 return s_systemFingerprint; 182 } 183 } 184 185 public static String getSystemProperty (final String key) 186 { 187 try 188 { 189 return System.getProperty (key); 190 } 191 catch (SecurityException se) 192 { 193 return null; 194 } 195 } 196 197 public static String getSystemProperty (final String key, final String def) 198 { 199 try 200 { 201 return System.getProperty (key, def); 202 } 203 catch (SecurityException se) 204 { 205 return def; 206 } 207 } 208 209 /** 210 * does not throw 211 * 212 * @param name 213 * @return 214 */ 215 public static Properties getProperties (final String name) 216 { 217 Properties result = null; 218 219 InputStream in = null; 220 try 221 { 222 in = ResourceLoader.getResourceAsStream (name); 223 if (in != null) 224 { 225 result = new XProperties (); 226 result.load (in); 227 } 228 } 229 catch (Throwable t) 230 { 231 result = null; 232 } 233 finally 234 { 235 if (in != null) try { in.close (); } catch (Throwable ignore) {} 236 in = null; 237 } 238 239 return result; 240 } 241 242 /** 243 * does not throw 244 * 245 * @param name 246 * @param loader 247 * @return 248 */ 249 public static Properties getProperties (final String name, final ClassLoader loader) 250 { 251 Properties result = null; 252 253 InputStream in = null; 254 try 255 { 256 in = ResourceLoader.getResourceAsStream (name, loader); 257 if (in != null) 258 { 259 result = new XProperties (); 260 result.load (in); 261 } 262 } 263 catch (Throwable t) 264 { 265 result = null; 266 } 267 finally 268 { 269 if (in != null) try { in.close (); } catch (Throwable ignore) {} 270 in = null; 271 } 272 273 return result; 274 } 275 276 /** 277 * Loads 'file' as a .properties file. 278 * 279 * @param file [may not be null] 280 * @return read properties [never null] 281 * @throws IOException on any file I/O errors 282 */ 283 public static Properties getPropertiesFromFile (final File file) 284 throws IOException 285 { 286 if (file == null) 287 throw new IllegalArgumentException ("null input: file"); 288 289 Properties result = null; 290 291 InputStream in = null; 292 try 293 { 294 in = new BufferedInputStream (new FileInputStream (file), 8 * 1024); 295 296 result = new XProperties (); 297 result.load (in); 298 } 299 finally 300 { 301 if (in != null) try { in.close (); } catch (Throwable ignore) {} 302 in = null; 303 } 304 305 return result; 306 } 307 308 /** 309 * Returns a lazy property implementation that will read 'load' as a .properties 310 * file on first use. If there are any file I/O errors when reading the file, 311 * they will be thrown as runtime exceptions (also on first use). 312 * 313 * @param file [can be null, which results in an empty property set returned] 314 * @return [never null] 315 */ 316 public static Properties getLazyPropertiesFromFile (final File file) 317 { 318 return new FilePropertyLookup (file); 319 } 320 321 // protected: ............................................................. 322 323 // package: ............................................................... 324 325 // private: ............................................................... 326 327 328 private static final class FilePropertyLookup extends XProperties 329 { 330 // note: due to incredibly stupid coding in java.util.Properties 331 // (getProperty() uses a non-virtual call to get(), while propertyNames() 332 // uses a virtual call to the same instead of delegating to getProperty()) 333 // I must override both methods below 334 335 public String getProperty (final String key) 336 { 337 faultContents (); 338 339 return m_contents.getProperty (key); 340 } 341 342 public Object get (final Object key) 343 { 344 faultContents (); 345 346 return m_contents.get (key); 347 } 348 349 /* 350 * Overrides Properties.keys () [this is used for debug logging only] 351 */ 352 public Enumeration keys () 353 { 354 faultContents (); 355 356 return m_contents.keys (); 357 } 358 359 360 /** 361 * Creates a lazy property lookup based on 'src' contents. 362 * 363 * @param src [null will result in empty property set created] 364 */ 365 FilePropertyLookup (final File src) 366 { 367 m_src = src; 368 } 369 370 /* 371 * @throws RuntimeException on file I/O failures. 372 */ 373 private synchronized void faultContents () 374 { 375 Properties contents = m_contents; 376 if ((contents == null) && (m_src != null)) 377 { 378 try 379 { 380 contents = getPropertiesFromFile (m_src); 381 } 382 catch (RuntimeException re) 383 { 384 throw re; // re-throw; 385 } 386 catch (Exception e) 387 { 388 throw new RuntimeException ("exception while processing properties file [" + m_src.getAbsolutePath () + "]: " + e); 389 } 390 } 391 392 if (contents == null) 393 { 394 contents = new XProperties (); // non-null marker 395 } 396 397 m_contents = contents; 398 } 399 400 401 private final File m_src; // can be null 402 private Properties m_contents; // non-null after faultContents() 403 404 } // end of nested class 405 406 407 private static final class SystemPropertyLookup extends XProperties 408 { 409 // note: due to incredibly stupid coding in java.util.Properties 410 // (getProperty() uses a non-virtual call to get(), while propertyNames() 411 // uses a virtual call to the same instead of delegating to getProperty()) 412 // I must override both methods below 413 414 public String getProperty (final String key) 415 { 416 return (String) get (key); 417 } 418 419 public Object get (final Object key) 420 { 421 if (! (key instanceof String)) return null; 422 423 String result = (String) super.get (key); 424 if (result != null) return result; 425 426 if (m_systemPrefix != null) 427 { 428 result = getSystemProperty (m_systemPrefix.concat ((String) key), null); 429 430 if (result != null) return result; 431 } 432 433 return result; 434 } 435 436 /* 437 * Overrides Properties.keys () [this is used for debug logging only] 438 */ 439 public synchronized Enumeration keys () 440 { 441 final Hashtable _propertyNames = new Hashtable (); 442 443 if (m_systemPrefix != null) 444 { 445 try 446 { 447 final int systemPrefixLength = m_systemPrefix.length (); 448 449 for (Enumeration e = System.getProperties ().propertyNames (); 450 e.hasMoreElements (); ) 451 { 452 final String n = (String) e.nextElement (); 453 454 if (n.startsWith (m_systemPrefix)) 455 { 456 final String yn = n.substring (systemPrefixLength); 457 458 _propertyNames.put (yn, yn); 459 } 460 } 461 } 462 catch (SecurityException ignore) 463 { 464 ignore.printStackTrace (System.out); 465 466 // continue 467 } 468 } 469 470 return _propertyNames.keys (); 471 } 472 473 474 SystemPropertyLookup (String systemPrefix) 475 { 476 if ((systemPrefix != null) && ! systemPrefix.endsWith (".")) 477 systemPrefix = systemPrefix.concat ("."); 478 479 m_systemPrefix = systemPrefix; 480 } 481 482 483 private final String m_systemPrefix; // can be null [if not null, normalized to end with "."] 484 485 } // end of nested class 486 487 488 private static final class SystemRedirectsLookup extends XProperties 489 { 490 // note: due to incredibly stupid coding in java.util.Properties 491 // (getProperty() uses a non-virtual call to get(), while propertyNames() 492 // uses a virtual call to the same instead of delegating to getProperty()) 493 // I must override both methods below 494 495 public String getProperty (final String key) 496 { 497 return (String) get (key); 498 } 499 500 public Object get (final Object key) 501 { 502 if (! (key instanceof String)) return null; 503 504 String result = (String) super.get (key); 505 if (result != null) return result; 506 507 if (m_systemRedirects != null) 508 { 509 final String redirect = (String) m_systemRedirects.get (key); 510 511 if (redirect != null) 512 { 513 result = getSystemProperty (redirect, null); 514 if (result != null) return result; 515 } 516 } 517 518 return result; 519 } 520 521 /* 522 * Overrides Properties.keys () [this is used for debug logging only] 523 */ 524 public synchronized Enumeration keys () 525 { 526 final Hashtable _propertyNames = new Hashtable (); 527 528 if (m_systemRedirects != null) 529 { 530 for (Iterator i = m_systemRedirects.keySet ().iterator (); 531 i.hasNext (); ) 532 { 533 final Object key = i.next (); 534 if (key != null) _propertyNames.put (key , key); 535 } 536 } 537 538 return _propertyNames.keys (); 539 } 540 541 542 SystemRedirectsLookup (final Map systemRedirects) 543 { 544 m_systemRedirects = systemRedirects; // note: no defensive copy 545 } 546 547 548 private final Map m_systemRedirects; // can be null 549 550 } // end of nested class 551 552 553 private static String s_systemFingerprint; 554 private static Properties s_systemProperties, s_systemRedirects; 555 556 } // end of class 557 // ----------------------------------------------------------------------------