001package org.jconfig;
002
003import java.io.*;
004import java.util.*;
005
006import org.jconfig.event.*;
007/**
008 *  The ConfigurationManager is the do-it-all-in-one utility. The
009 *  ConfigurationManager allows you to store properties inside an XML file or a
010 *  properties file. The properties can be retrieved later for use in your
011 *  applications. <P/>
012 *
013 *  Typical usage: <P/>
014 *
015 *  <CODE>
016 * // <BR />
017 *  // first we get an existing property from the category &quot;general&quot;
018 *  <BR />
019 *  //<BR />
020 *  String debug_file = cfgmgr.getProperty(&quot;debug_file&quot;);<BR />
021 *  System.out.println(&quot;The debug_file is:&quot; + debug_file);<BR />
022 *  //<BR />
023 *  // now we try to get a non-existing property from the category
024 *  &quot;general&quot;<BR />
025 *  //<BR />
026 *  String neProp = cfgmgr.getProperty(&quot;hello&quot;,&quot;my
027 *  default&quot;);<BR />
028 *  System.out.println(&quot;The value is:&quot; + neProp );<BR />
029 *  //<BR />
030 *  // last step is we get a property from the category &quot;JDBC&quot;<BR />
031 *  //<BR />
032 *  String driver = cfgmgr.getProperty(&quot;DRIVER&quot;,&quot;&quot;,&quot;JDBC&quot;);
033 *  <BR />
034 *  System.out.println(&quot;The driver is:&quot; + driver);<BR />
035 *  </CODE>
036 *
037 *@author     Andreas Mecky andreas.mecky@xcom.de
038 *@author     Terry Dye terry.dye@xcom.de
039 *@created    27. Januar 2002
040 *@version    $Id: ConfigurationManager.java,v 1.16 2002/08/06 15:08:59
041 *      Andreas.Mecky Exp $
042 */
043public class ConfigurationManager implements FileListener {
044
045    // separator for category and property for HashMap
046    private final static char C_SEP = 127;
047    // prefix for category names
048    private final static char C_PFIX = 64;
049    // all instances of the ConfigurationManager
050    private static HashMap configurationManagers = null;
051    // the parameters for one instance
052    private HashMap parameter;
053    // all categories for one instance
054    private Vector categories;
055    // flag if we should validate an XML input
056    private boolean validate = false;
057    // the name of the instance
058    private String name;
059    // file watcher
060    private FileWatcher watcher;
061    // the config file
062    private File configFile;
063
064
065    /**
066     *  Constructor for the ConfigurationManager object
067     *
068     *@param  name  Description of the Parameter
069     */
070    private ConfigurationManager(String name) {
071        parameter = new HashMap();
072        categories = new Vector();
073        addCategory("general");
074        this.name = name;
075    }
076
077
078    /**
079     *  This method returns the value for a particular key. The category is
080     *  "general".
081     *
082     *@param  key  the name of the property
083     *@return      the value as String or null
084     */
085    public String getProperty(String key) {
086        return getProperty(key, null, null);
087    }
088
089
090    /**
091     *  This method returns the value for a particular key. If this key does not
092     *  exist then the method returns the defaultValue. The category is
093     *  "general".
094     *
095     *@param  key           the name of the property
096     *@param  defaultValue  the value that will be returned if the property
097     *      cannot be found
098     *@return               the value as String or the defaultValue
099     */
100    public String getProperty(String key, String defaultValue) {
101        return getProperty(key, defaultValue, null);
102    }
103
104
105    /**
106     *  This method returns the value for a particular key in the certain
107     *  category. If this key does not exist then the method returns the
108     *  defaultValue
109     *
110     *@param  key           the name of the property
111     *@param  defaultValue  the value that will be returned if the property
112     *      cannot be found
113     *@param  category      the name of the category
114     *@return               the value as String or the defaultValue
115     */
116    public String getProperty(String key, String defaultValue, String category) {
117        if (key == null) {
118            return defaultValue;
119        }
120        String mykey;        
121        String generalKey = C_PFIX + "general" + C_SEP + key;
122        if (category != null) {            
123            mykey = C_PFIX + category + C_SEP + key;
124        } else {
125            mykey = generalKey;
126        }
127        if (parameter.containsKey((String) mykey)) {
128            return (String) parameter.get((String) mykey);
129        } else if (parameter.containsKey((String) generalKey)) {
130            return (String) parameter.get((String) generalKey);
131        } else {
132            return defaultValue;
133        }
134    }
135
136
137    /**
138     *  This method returns the default instance of the ConfigurationManager. If
139     *  no instance is found it will create a new instance and will try to load
140     *  the config.xml. This file must be in the classpath.
141     *
142     *@return
143     */
144    public static ConfigurationManager getInstance() {
145        return getInstance("default", true);
146    }
147
148
149    /**
150     *  This method returns an instance of the ConfigurationManager with the
151     *  given name<BR />
152     *  If no instance is found it will create a new instance and will try to
153     *  load the config.xml. This file must be in the classpath.
154     *
155     *@param  instanceName
156     *@return
157     */
158    public static ConfigurationManager getInstance(String instanceName) {
159        return getInstance(instanceName, true);
160    }
161
162
163    /**
164     *  This method returns an instance of the ConfigurationManager with this
165     *  name. If there is no instance then it will create a new one. If the
166     *  autoLoad flag is set to true then it will try to load the config.xml. In
167     *  order to use the ConfigurationManager do the following: <BR />
168     *  private ConfigurationManager<BR />
169     *  cfgmgr = ConfigurationManager.getInstance("myConfiguration",true);
170     *
171     *@param  instanceName
172     *@param  autoLoad      if true then the method wil try to load the
173     *      config.xml if it can find it in the classpath
174     *@return               an instance of the ConfigurationManager
175     */
176    public static ConfigurationManager getInstance(String instanceName, boolean autoLoad) {
177        if (configurationManagers == null) {
178            configurationManagers = new HashMap();
179        }
180        if (configurationManagers.containsKey(instanceName)) {
181            return (ConfigurationManager) configurationManagers.get(instanceName);
182        }
183        ConfigurationManager cfgmgr = new ConfigurationManager(instanceName);
184        InputStream is = null;
185        try {
186            /*
187             *  get a default config.xml, means no need to call method
188             *  load ( filename )
189             */
190            if (autoLoad) {
191                ClassLoader cl = cfgmgr.getClass().getClassLoader();
192                is = cl.getResourceAsStream("config.xml");
193                if (is != null) {
194                    cfgmgr.load(is);
195                    cfgmgr.setAutoReload(true);
196                }
197                /*
198                 *  we now try register the watchers
199                 */
200                java.net.URL url = cl.getResource("config.xml");
201                if (url != null) {
202                    File file = new File(url.getFile());
203                    cfgmgr.configFile = file;
204                    cfgmgr.setWatcher(new FileWatcher(file));
205                    cfgmgr.watcher = cfgmgr.getWatcher();
206                    cfgmgr.watcher.addFileListener(cfgmgr);
207                    cfgmgr.watcher.start();
208                }
209            }
210            configurationManagers.put(instanceName, cfgmgr);
211        } catch (Exception e) {
212            e.printStackTrace();
213            return null;
214        }
215        return cfgmgr;
216    }
217
218
219    /**
220     *  This method returns an Enumeration of all categories. The names are
221     *  stored as Strings inside the Enumeration.
222     *
223     *@return        an enumeratiuon containing all names of all categories
224     *@deprecated    use getAllCategoryNames instead
225     */
226    public Enumeration getCategoryNames() {
227        return categories.elements();
228    }
229
230
231    /**
232     *  This method returns an <code>String</code> array of the names of all
233     *  categories.
234     *
235     *@return    an <code>String</code> containing all names of all categories
236     */
237    public String[] getAllCategoryNames() {
238        return (String[]) categories.toArray(new String[0]);
239    }
240
241
242    /**
243     *  This method will return a Hashtable that contains all properties for the
244     *  category "general". The properties are stored as key and value both as
245     *  strings.
246     *
247     *@return    a Hashtable that contains all properties for the category
248     *      "general"
249     */
250    public Hashtable getProperties() {
251        return getProperties(null);
252    }
253
254
255    /**
256     *  This method will return a Hashtable that contains all properties for the
257     *  selected category. The properties are stored as key and value both as
258     *  strings.
259     *
260     *@param  category  the name of the category
261     *@return           a Hashtable that contains all properties for this
262     *      category
263     */
264    public Hashtable getProperties(String category) {
265        Hashtable retValues = new Hashtable();
266        // if no category was passed to us then we take "general"
267        if (category == null) {
268            category = C_PFIX + "general";
269        } else {
270            category = C_PFIX + category;
271        }
272        String currentKey = null;
273        // get all the keys from our Hashtable
274        Iterator it = parameter.keySet().iterator();
275        while (it.hasNext()) {
276            currentKey = (String) it.next();
277            // let's see if the category name is inside the key-string            
278            if (currentKey.indexOf(category + C_SEP) != -1) {
279                // now extract the category name from the key and
280                // store the name and the value                
281                String name = currentKey.substring(currentKey.indexOf(C_SEP) + 1);
282                String value = (String) parameter.get((String) currentKey);
283                retValues.put(name, value);
284            }
285        }
286        return retValues;
287    }
288
289
290    /**
291     *  This method will set the value for a specific property in the category
292     *  "general". If this property does not exist then it will be created
293     *
294     *@param  name   the name of the property
295     *@param  value  the value for this property
296     */
297    public void setProperty(String name, String value) {
298        setProperty(name, value, null);
299    }
300
301
302    /**
303     *  This method will set the value for a specific property in the selected
304     *  category. If this property does not exist then it will be created. If
305     *  the category does not exist it will be created.
306     *
307     *@param  name      the name of the property
308     *@param  value     the value for this property
309     *@param  category  the name of the category
310     */
311    public void setProperty(String name, String value, String category) {
312        // we use the default one if it is not specified
313        String tmpCategory = null;
314        if (category == null) {
315            tmpCategory = C_PFIX + "general";
316        } else {
317            tmpCategory = C_PFIX + category;
318        }
319        // it is safe to use addCategory since it will check
320        // if this category already exists and return quitely
321        addCategory(category);
322        // now we save the property
323        String mykey = tmpCategory + C_SEP + name;
324        parameter.put(mykey, value);
325    }
326
327
328    /**
329     *  This method removes the property with the given name from the category
330     *  general. It calls <code>removeProperty(name,"general")</code>.
331     *
332     *@param  name  the name of the property that will be removed
333     */
334    public void removeProperty(String name) {
335        if (name != null) {
336            removeProperty(name, "general");
337        }
338    }
339
340
341    /**
342     *  This method will remove a property with the given name from the specific
343     *  category.
344     *
345     *@param  name      the name of the property
346     *@param  category  the name of the category
347     */
348    public void removeProperty(String name, String category) {
349        if (name != null && category != null) {            
350            String mykey = C_PFIX + category + C_SEP + name; 
351            if( parameter.get(mykey) != null ) {
352                parameter.remove(mykey);
353            }
354        }
355    }
356
357
358    /**
359     *  Adds a feature to the Category attribute of the ConfigurationManager
360     *  object
361     *
362     *@param  category  The feature to be added to the Category attribute
363     */
364    public void addCategory(String category) {
365        if (categories.indexOf(category) == -1) {
366            categories.add(category);
367        }
368    }
369
370
371    /**
372     *  This method removes the specific category.
373     *
374     *@param  category  the name of the category that will be removed
375     */
376    public void removeCategory(String category) {
377        String tmpCategory = C_PFIX + category;
378        if (categories.indexOf(tmpCategory) != -1) {
379            String[] propertyNames = getAllPropertyNamesFromCategory(category);
380            for (int i = 0; i < propertyNames.length; i++) {
381                removeProperty(propertyNames[i], category);
382            }
383            categories.remove(tmpCategory);
384        }
385    }
386
387    /**
388     *  Gets all property names for a given category. If the category
389     * is <code>null</code> then the category is "general".     
390     *
391     *@param  category  the name of the category
392     *@return           a string array with the names
393     */
394    public String[] getAllPropertyNamesFromCategory(String category) {
395        Hashtable allProps = getProperties(category);
396        Set myKeys = allProps.keySet();
397        return (String[]) myKeys.toArray(new String[0]);
398    }
399
400
401    /**
402     *  This method will save the current categories and their properties to a
403     *  file
404     *
405     *@param  file                               the file that will be generated
406     *@exception  ConfigurationManagerException  Description of the Exception
407     */
408    public void store(File file) throws ConfigurationManagerException {
409        ConfigurationFileHandler cph = new ConfigurationFileHandler();
410        HashMap props = new HashMap();
411        for (int i = 0; i < categories.size(); i++) {
412            String currentCategory = (String) categories.get(i);
413            Hashtable myProperties = getProperties(currentCategory);
414            props.put(currentCategory, myProperties);
415        }
416        HashMap params = new HashMap();
417        params.put("File", file);
418        params.put("Data", props);
419        cph.initialize(params);
420        cph.store();
421    }
422
423
424    /**
425     *  This method will store all categories and properties to a
426     *  Java-properties file. All properties will be in the category "general".
427     *  It will override an existing configuration.
428     *
429     *@param  file                            the file
430     *@throws  ConfigurationManagerException  if the file cannot be processed
431     */
432    public void loadProperties(File file) throws ConfigurationManagerException {
433        ConfigurationPropertiesHandler cph = new ConfigurationPropertiesHandler();
434        HashMap params = new HashMap();
435        params.put("File", file);
436        cph.initialize(params);
437        cph.load();
438        categories = cph.getCategories();
439        parameter = cph.getProperties();
440    }
441
442
443    /**
444     *  This method will generate a Java-properties file. All categories will be
445     *  saved as comments and only the properties are written to the file. The
446     *  output is category.property=value
447     *
448     *@param  file                            the file
449     *@throws  ConfigurationManagerException  if the file cannot be processed
450     */
451    public void storeProperties(File file) throws ConfigurationManagerException {
452        ConfigurationPropertiesHandler cph = new ConfigurationPropertiesHandler();
453        HashMap props = new HashMap();
454        for (int i = 0; i < categories.size(); i++) {
455            String currentCategory = (String) categories.get(i);
456            Hashtable myProperties = getProperties(currentCategory);
457            props.put(currentCategory, myProperties);
458        }
459        HashMap params = new HashMap();
460        params.put("File", file);
461        params.put("Data", props);
462        // flag to show that only a single category will be saved
463        params.put("Type", "Multi");
464        cph.initialize(params);
465        cph.store();
466    }
467
468
469    /**
470     *  This method will write a Java-properties file and fill in the properties
471     *  for the particular category. The category will be saved as comment and
472     *  only the properties are written to the file. The output is
473     *  property=value. The category is not included in the property name.
474     *
475     *@param  file                            the file
476     *@param  category                        Description of the Parameter
477     *@throws  ConfigurationManagerException  if the file cannot be processed
478     */
479    public void storeProperties(File file, String category) throws ConfigurationManagerException {
480        ConfigurationPropertiesHandler cph = new ConfigurationPropertiesHandler();
481        HashMap params = new HashMap();
482        params.put("File", file);
483        params.put("Properties", getProperties(category));
484        params.put("Category", category);
485        // flag to show that only a single category will be saved
486        params.put("Type", "Single");
487        cph.initialize(params);
488        cph.store();
489    }
490
491
492    /**
493     *  This method reads the configuration from an URL. It must be a XML file.
494     *
495     *@param  theURL                          the URL
496     *@throws  ConfigurationManagerException  if an error occurs
497     */
498    public synchronized void load(String theURL) throws ConfigurationManagerException {
499        HashMap params = new HashMap();
500        params.put("URL", theURL);
501        load(params);
502    }
503
504
505    /**
506     *  This method will read an <code>InputStream</code>.<BR />
507     *
508     *
509     *@param  is                              the <code>InputStream</code>
510     *@throws  ConfigurationManagerException  if an error occurs
511     */
512    public synchronized void load(InputStream is) throws ConfigurationManagerException {
513        HashMap params = new HashMap();
514        params.put("InputStream", is);
515        load(params);
516    }
517
518
519    /**
520     *  This method reads a XML file.
521     *
522     *@param  file                            the file that will be read
523     *@throws  ConfigurationManagerException  if the file cannot be read
524     */
525    public synchronized void load(File file) throws ConfigurationManagerException {
526        HashMap params = new HashMap();
527        params.put("File", file);
528
529        configFile = file;
530        load(params);
531    }
532
533
534    /**
535     *  Description of the Method
536     *
537     *@param  params                             Description of the Parameter
538     *@exception  ConfigurationManagerException  Description of the Exception
539     */
540    private void load(HashMap params) throws ConfigurationManagerException {
541        ConfigurationFileHandler cfh = new ConfigurationFileHandler();
542        cfh.initialize(params);
543        cfh.load();
544        cfh.setValidation(validate);
545        categories = cfh.getCategories();
546        parameter = cfh.getProperties();
547        reloadWatcher();
548    }
549
550
551    /**
552     *  Description of the Method
553     */
554    private void reloadWatcher() {
555        // how do I get the File information that was loaded?
556    }
557
558
559    /**
560     *  This method creates a string representation of this configuration. It
561     *  can be used for debugging.
562     *
563     *@return    the configuration as <code>String</code>
564     */
565    public String toString() {
566        StringBuffer sb = new StringBuffer();
567        sb.append("Configuration\n");
568        sb.append("Name:" + name + "\n");
569        for (int i = 0; i < categories.size(); i++) {
570            String currentCategory = (String) categories.get(i);
571            sb.append("  Category:" + currentCategory + "\n");
572            Hashtable myProperties = getProperties(currentCategory);
573            Enumeration lprops = myProperties.keys();
574            // here we walk through the properties
575            while (lprops.hasMoreElements()) {
576                String currentKey = (String) lprops.nextElement();
577                sb.append("    " + currentKey + "=" + (String) myProperties.get(currentKey) + "\n");
578            }
579        }
580        return sb.toString();
581    }
582
583
584    /**
585     *  This method, once implemented, will be called when the File object
586     *  itself changes.
587     *
588     *@param  evt  Description of the Parameter
589     */
590    public void fileChanged(FileListenerEvent evt) {
591        try {
592            load(evt.getFile());
593        } catch (Exception e) {
594            e.printStackTrace();
595        }
596    }
597
598
599    /**
600     *  Sets the validation attribute of the ConfigurationManager object
601     *
602     *@param  validate  The new validation value
603     */
604    public void setValidation(boolean validate) {
605        this.validate = validate;
606    }
607
608
609    /**
610     *  Sets the reloadIntervall attribute of the ConfigurationManager object
611     *
612     *@param  seconds  The new reloadIntervall value
613     */
614    public void setReloadIntervall(int seconds) {
615        if (getWatcher() != null) {
616            getWatcher().setInterval(seconds);
617        }
618    }
619
620
621    /**
622     * Sets the reload flag. If it is set to true then jConfig will watch the
623     * correspondig resource for this configuration and reload it if it has
624     * changed.
625     *
626     *@param  reload  The new autoReload value
627     */
628    public void setAutoReload(boolean reload) {
629        if (reload) {
630            if (getWatcher() != null) {
631                getWatcher().start();
632            } else {
633                if (configFile != null) {
634                    setWatcher(new FileWatcher(configFile));
635                    getWatcher().addFileListener(this);
636                    getWatcher().start();
637                }
638            }
639        } else {
640            if (getWatcher() != null) {
641                getWatcher().stopWatching();
642            }
643        }
644    }
645
646
647    /**
648     *  Getter for property FileWatcher
649     *
650     *@return    The FileWatcher
651     */
652    public FileWatcher getWatcher() {
653        return watcher;
654    }
655
656
657    /**
658     *  Setter for FileWatcher .
659     *
660     *@param  watcher  New value of property watcher.
661     */
662    public void setWatcher(org.jconfig.FileWatcher watcher) {
663        this.watcher = watcher;
664    }
665
666
667    /**
668     *  Getter for property Configuration Name.
669     *
670     *@return    Configuration Name.
671     */
672    public java.lang.String getConfigurationName() {
673        return name;
674    }
675
676
677    /**
678     *  Adds a FileListener to the current configuration
679     *
680     *@param  fileListener  The FileListener
681     */
682    public void addFileListener(FileListener fileListener) {
683        getWatcher().addFileListener(fileListener);
684    }
685    
686    /**
687     * Getter for the File Object of the configuration file used.
688     *
689     * @return The File object
690     */
691    public File getConfigFile() {
692        return configFile;
693    }
694}