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 */
9package com.vladium.util;
10
11import java.io.BufferedInputStream;
12import java.io.File;
13import java.io.FileInputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.util.Enumeration;
17import java.util.Hashtable;
18import java.util.Iterator;
19import java.util.Map;
20import 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 */
31public
32abstract 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// ----------------------------------------------------------------------------