Home | History | Annotate | Download | only in util
      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 // ----------------------------------------------------------------------------