| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: Property.java,v 1.1.1.1.2.4 2004/07/16 23:32:04 vlad_r Exp $ |
| */ |
| package com.vladium.util; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| /* |
| * NOTE: to avoid certain build problems, this class should use only |
| * core Java APIs and not any app infrastructure. |
| */ |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| abstract class Property |
| { |
| // public: ................................................................ |
| |
| |
| public static boolean toBoolean (final String value) |
| { |
| if (value == null) |
| return false; |
| else |
| return value.startsWith ("t") || value.startsWith ("y"); |
| } |
| |
| |
| /** |
| * NOTE: this does not guarantee that the result will be mutatable |
| * independently from 'overrides' or 'base', so this method |
| * should be used for read-only property only |
| * |
| * @param overrides [null is equivalent to empty] |
| * @param base [null is equivalent to empty] |
| * |
| * @return [never null, could be empty] |
| */ |
| public static Properties combine (final Properties overrides, final Properties base) |
| { |
| // note: no defensive copies here |
| |
| if (base == null) |
| { |
| if (overrides == null) |
| return new XProperties (); |
| else |
| return overrides; |
| } |
| |
| // [assertion: base != null] |
| |
| if (overrides == null) return base; |
| |
| // [assertion: both 'overrides' and 'base' are not null] |
| |
| final Properties result = new XProperties (base); |
| |
| // note: must use propertyNames() because that is the only method that recurses |
| // into possible bases inside 'overrides' |
| |
| for (Enumeration overrideNames = overrides.propertyNames (); overrideNames.hasMoreElements (); ) |
| { |
| final String n = (String) overrideNames.nextElement (); |
| final String v = overrides.getProperty (n); |
| |
| result.setProperty (n, v); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Creates a set of properties for an application with a given namespace. |
| * This method is not property aliasing-aware. |
| * |
| * @param namespace application namespace [may not be null] |
| * @param loader classloader to use for any classloader resource lookups |
| * [null is equivalent to the applicaton classloader] |
| * @return application properties [never null, a new instance is created |
| * on each invocation] |
| */ |
| public static Properties getAppProperties (final String namespace, final ClassLoader loader) |
| { |
| if (namespace == null) |
| throw new IllegalArgumentException ("null properties: appNameLC"); |
| |
| final Properties appDefaults = Property.getProperties (namespace + "_default.properties", loader); |
| final Properties systemFileOverrides; |
| { |
| final String fileName = Property.getSystemProperty (namespace + ".properties"); |
| final File file = fileName != null |
| ? new File (fileName) |
| : null; |
| |
| systemFileOverrides = Property.getLazyPropertiesFromFile (file); |
| } |
| final Properties systemOverrides = Property.getSystemProperties (namespace); |
| final Properties resOverrides = Property.getProperties (namespace + ".properties", loader); |
| |
| return combine (resOverrides, |
| combine (systemOverrides, |
| combine (systemFileOverrides, |
| appDefaults))); |
| } |
| |
| public static Properties getSystemProperties (final String systemPrefix) |
| { |
| // note: this method is not synchronized on purpose |
| |
| Properties result = s_systemProperties; |
| if (result == null) |
| { |
| result = new SystemPropertyLookup (systemPrefix); |
| |
| s_systemProperties = result; |
| return result; |
| } |
| |
| return result; |
| } |
| |
| public static Properties getSystemPropertyRedirects (final Map systemRedirects) |
| { |
| // note: this method is not synchronized on purpose |
| |
| Properties result = s_systemRedirects; |
| if (result == null) |
| { |
| result = new SystemRedirectsLookup (systemRedirects); |
| |
| s_systemRedirects = result; |
| return result; |
| } |
| |
| return result; |
| } |
| |
| |
| public static String getSystemFingerprint () |
| { |
| // [not synchronized intentionally] |
| |
| if (s_systemFingerprint != null) |
| return s_systemFingerprint; |
| else |
| { |
| final StringBuffer s = new StringBuffer (); |
| final char delimiter = ':'; |
| |
| s.append (getSystemProperty ("java.vm.name", "")); |
| s.append (delimiter); |
| s.append (getSystemProperty ("java.vm.version", "")); |
| s.append (delimiter); |
| s.append (getSystemProperty ("java.vm.vendor", "")); |
| s.append (delimiter); |
| s.append (getSystemProperty ("os.name", "")); |
| s.append (delimiter); |
| s.append (getSystemProperty ("os.version", "")); |
| s.append (delimiter); |
| s.append (getSystemProperty ("os.arch", "")); |
| |
| s_systemFingerprint = s.toString (); |
| return s_systemFingerprint; |
| } |
| } |
| |
| public static String getSystemProperty (final String key) |
| { |
| try |
| { |
| return System.getProperty (key); |
| } |
| catch (SecurityException se) |
| { |
| return null; |
| } |
| } |
| |
| public static String getSystemProperty (final String key, final String def) |
| { |
| try |
| { |
| return System.getProperty (key, def); |
| } |
| catch (SecurityException se) |
| { |
| return def; |
| } |
| } |
| |
| /** |
| * does not throw |
| * |
| * @param name |
| * @return |
| */ |
| public static Properties getProperties (final String name) |
| { |
| Properties result = null; |
| |
| InputStream in = null; |
| try |
| { |
| in = ResourceLoader.getResourceAsStream (name); |
| if (in != null) |
| { |
| result = new XProperties (); |
| result.load (in); |
| } |
| } |
| catch (Throwable t) |
| { |
| result = null; |
| } |
| finally |
| { |
| if (in != null) try { in.close (); } catch (Throwable ignore) {} |
| in = null; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * does not throw |
| * |
| * @param name |
| * @param loader |
| * @return |
| */ |
| public static Properties getProperties (final String name, final ClassLoader loader) |
| { |
| Properties result = null; |
| |
| InputStream in = null; |
| try |
| { |
| in = ResourceLoader.getResourceAsStream (name, loader); |
| if (in != null) |
| { |
| result = new XProperties (); |
| result.load (in); |
| } |
| } |
| catch (Throwable t) |
| { |
| result = null; |
| } |
| finally |
| { |
| if (in != null) try { in.close (); } catch (Throwable ignore) {} |
| in = null; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Loads 'file' as a .properties file. |
| * |
| * @param file [may not be null] |
| * @return read properties [never null] |
| * @throws IOException on any file I/O errors |
| */ |
| public static Properties getPropertiesFromFile (final File file) |
| throws IOException |
| { |
| if (file == null) |
| throw new IllegalArgumentException ("null input: file"); |
| |
| Properties result = null; |
| |
| InputStream in = null; |
| try |
| { |
| in = new BufferedInputStream (new FileInputStream (file), 8 * 1024); |
| |
| result = new XProperties (); |
| result.load (in); |
| } |
| finally |
| { |
| if (in != null) try { in.close (); } catch (Throwable ignore) {} |
| in = null; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns a lazy property implementation that will read 'load' as a .properties |
| * file on first use. If there are any file I/O errors when reading the file, |
| * they will be thrown as runtime exceptions (also on first use). |
| * |
| * @param file [can be null, which results in an empty property set returned] |
| * @return [never null] |
| */ |
| public static Properties getLazyPropertiesFromFile (final File file) |
| { |
| return new FilePropertyLookup (file); |
| } |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private static final class FilePropertyLookup extends XProperties |
| { |
| // note: due to incredibly stupid coding in java.util.Properties |
| // (getProperty() uses a non-virtual call to get(), while propertyNames() |
| // uses a virtual call to the same instead of delegating to getProperty()) |
| // I must override both methods below |
| |
| public String getProperty (final String key) |
| { |
| faultContents (); |
| |
| return m_contents.getProperty (key); |
| } |
| |
| public Object get (final Object key) |
| { |
| faultContents (); |
| |
| return m_contents.get (key); |
| } |
| |
| /* |
| * Overrides Properties.keys () [this is used for debug logging only] |
| */ |
| public Enumeration keys () |
| { |
| faultContents (); |
| |
| return m_contents.keys (); |
| } |
| |
| |
| /** |
| * Creates a lazy property lookup based on 'src' contents. |
| * |
| * @param src [null will result in empty property set created] |
| */ |
| FilePropertyLookup (final File src) |
| { |
| m_src = src; |
| } |
| |
| /* |
| * @throws RuntimeException on file I/O failures. |
| */ |
| private synchronized void faultContents () |
| { |
| Properties contents = m_contents; |
| if ((contents == null) && (m_src != null)) |
| { |
| try |
| { |
| contents = getPropertiesFromFile (m_src); |
| } |
| catch (RuntimeException re) |
| { |
| throw re; // re-throw; |
| } |
| catch (Exception e) |
| { |
| throw new RuntimeException ("exception while processing properties file [" + m_src.getAbsolutePath () + "]: " + e); |
| } |
| } |
| |
| if (contents == null) |
| { |
| contents = new XProperties (); // non-null marker |
| } |
| |
| m_contents = contents; |
| } |
| |
| |
| private final File m_src; // can be null |
| private Properties m_contents; // non-null after faultContents() |
| |
| } // end of nested class |
| |
| |
| private static final class SystemPropertyLookup extends XProperties |
| { |
| // note: due to incredibly stupid coding in java.util.Properties |
| // (getProperty() uses a non-virtual call to get(), while propertyNames() |
| // uses a virtual call to the same instead of delegating to getProperty()) |
| // I must override both methods below |
| |
| public String getProperty (final String key) |
| { |
| return (String) get (key); |
| } |
| |
| public Object get (final Object key) |
| { |
| if (! (key instanceof String)) return null; |
| |
| String result = (String) super.get (key); |
| if (result != null) return result; |
| |
| if (m_systemPrefix != null) |
| { |
| result = getSystemProperty (m_systemPrefix.concat ((String) key), null); |
| |
| if (result != null) return result; |
| } |
| |
| return result; |
| } |
| |
| /* |
| * Overrides Properties.keys () [this is used for debug logging only] |
| */ |
| public synchronized Enumeration keys () |
| { |
| final Hashtable _propertyNames = new Hashtable (); |
| |
| if (m_systemPrefix != null) |
| { |
| try |
| { |
| final int systemPrefixLength = m_systemPrefix.length (); |
| |
| for (Enumeration e = System.getProperties ().propertyNames (); |
| e.hasMoreElements (); ) |
| { |
| final String n = (String) e.nextElement (); |
| |
| if (n.startsWith (m_systemPrefix)) |
| { |
| final String yn = n.substring (systemPrefixLength); |
| |
| _propertyNames.put (yn, yn); |
| } |
| } |
| } |
| catch (SecurityException ignore) |
| { |
| ignore.printStackTrace (System.out); |
| |
| // continue |
| } |
| } |
| |
| return _propertyNames.keys (); |
| } |
| |
| |
| SystemPropertyLookup (String systemPrefix) |
| { |
| if ((systemPrefix != null) && ! systemPrefix.endsWith (".")) |
| systemPrefix = systemPrefix.concat ("."); |
| |
| m_systemPrefix = systemPrefix; |
| } |
| |
| |
| private final String m_systemPrefix; // can be null [if not null, normalized to end with "."] |
| |
| } // end of nested class |
| |
| |
| private static final class SystemRedirectsLookup extends XProperties |
| { |
| // note: due to incredibly stupid coding in java.util.Properties |
| // (getProperty() uses a non-virtual call to get(), while propertyNames() |
| // uses a virtual call to the same instead of delegating to getProperty()) |
| // I must override both methods below |
| |
| public String getProperty (final String key) |
| { |
| return (String) get (key); |
| } |
| |
| public Object get (final Object key) |
| { |
| if (! (key instanceof String)) return null; |
| |
| String result = (String) super.get (key); |
| if (result != null) return result; |
| |
| if (m_systemRedirects != null) |
| { |
| final String redirect = (String) m_systemRedirects.get (key); |
| |
| if (redirect != null) |
| { |
| result = getSystemProperty (redirect, null); |
| if (result != null) return result; |
| } |
| } |
| |
| return result; |
| } |
| |
| /* |
| * Overrides Properties.keys () [this is used for debug logging only] |
| */ |
| public synchronized Enumeration keys () |
| { |
| final Hashtable _propertyNames = new Hashtable (); |
| |
| if (m_systemRedirects != null) |
| { |
| for (Iterator i = m_systemRedirects.keySet ().iterator (); |
| i.hasNext (); ) |
| { |
| final Object key = i.next (); |
| if (key != null) _propertyNames.put (key , key); |
| } |
| } |
| |
| return _propertyNames.keys (); |
| } |
| |
| |
| SystemRedirectsLookup (final Map systemRedirects) |
| { |
| m_systemRedirects = systemRedirects; // note: no defensive copy |
| } |
| |
| |
| private final Map m_systemRedirects; // can be null |
| |
| } // end of nested class |
| |
| |
| private static String s_systemFingerprint; |
| private static Properties s_systemProperties, s_systemRedirects; |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |