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 
toBoolean(final String value)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      */
combine(final Properties overrides, final Properties base)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      */
getAppProperties(final String namespace, final ClassLoader loader)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 
getSystemProperties(final String systemPrefix)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 
getSystemPropertyRedirects(final Map systemRedirects)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 
getSystemFingerprint()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 
getSystemProperty(final String key)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 
getSystemProperty(final String key, final String def)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      */
getProperties(final String name)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      */
getProperties(final String name, final ClassLoader loader)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      */
getPropertiesFromFile(final File file)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      */
getLazyPropertiesFromFile(final File file)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 
getProperty(final String key)335         public String getProperty (final String key)
336         {
337             faultContents ();
338 
339             return m_contents.getProperty (key);
340         }
341 
get(final Object key)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          */
keys()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          */
FilePropertyLookup(final File src)365         FilePropertyLookup (final File src)
366         {
367             m_src = src;
368         }
369 
370         /*
371          * @throws RuntimeException on file I/O failures.
372          */
faultContents()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 
getProperty(final String key)414         public String getProperty (final String key)
415         {
416             return (String) get (key);
417         }
418 
get(final Object key)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          */
keys()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 
SystemPropertyLookup(String systemPrefix)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 
getProperty(final String key)495         public String getProperty (final String key)
496         {
497             return (String) get (key);
498         }
499 
get(final Object key)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          */
keys()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 
SystemRedirectsLookup(final Map systemRedirects)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 // ----------------------------------------------------------------------------