Greetings, I'd like to contribute a slight modification to reload automatically a PropertiesConfiguration if the underlying .properties file has been changed. I had to implement this to prevent restarting my web application every time a configuration file is modified.

The auto reload mode is enabled by calling setAutoReload(true) on the PropertiesConfiguration instance. It's disabled by default. Every time a property is accessed with getXXX(key) the date of the file is checked and the configuration is reloaded if the previous date known is older. This check is executed only once every 5 seconds to prevent hammering the file system.

I'm attaching the patch and the modified files to this mail, all test cases passed successfully. I've also added a specific test case for the automatic reloading scenario.

Emmanuel Bourg
Index: src/java/org/apache/commons/configuration/BaseConfiguration.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/configuration/src/java/org/apache/commons/configuration/BaseConfiguration.java,v
retrieving revision 1.16
diff -u -r1.16 BaseConfiguration.java
--- src/java/org/apache/commons/configuration/BaseConfiguration.java    17 Oct 2003 
08:11:52 -0000      1.16
+++ src/java/org/apache/commons/configuration/BaseConfiguration.java    11 Dec 2003 
15:24:09 -0000
@@ -55,6 +55,7 @@
  */
 
 import java.util.Iterator;
+import java.util.Map;
 
 import org.apache.commons.collections.SequencedHashMap;
 
@@ -86,7 +87,7 @@
 public class BaseConfiguration extends AbstractConfiguration
 {
     /** stores the configuration key-value pairs */
-    private SequencedHashMap store = new SequencedHashMap();
+    protected Map store = new SequencedHashMap();
     
     /**
      * Empty constructor.  You must add all the values to this configuration.
@@ -208,6 +209,6 @@
      */
     public Iterator getKeys()
     {
-        return store.iterator();
+        return store.keySet().iterator();
     }
 }
Index: src/java/org/apache/commons/configuration/PropertiesConfiguration.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/configuration/src/java/org/apache/commons/configuration/PropertiesConfiguration.java,v
retrieving revision 1.12
diff -u -r1.12 PropertiesConfiguration.java
--- src/java/org/apache/commons/configuration/PropertiesConfiguration.java      11 Nov 
2003 15:02:07 -0000      1.12
+++ src/java/org/apache/commons/configuration/PropertiesConfiguration.java      11 Dec 
2003 15:24:15 -0000
@@ -56,8 +56,12 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.File;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Vector;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -108,6 +112,21 @@
      * the load method. */
     protected String fileName = null;
 
+    /** Should the configuration be reloaded on file changes ? */
+    protected boolean autoReload = false;
+
+    /** The last time the configuration file was modified. */
+    protected long lastModified;
+
+    /** The last time the file was checked for changes. */
+    protected long lastChecked;
+
+    /** The minimum delay in milliseconds between checks. */
+    protected long refreshDelay = 5000;
+
+    /** The lock to block read access while reloading */
+    private Object reloadLock = new Object();
+
     /**
      * Creates an empty PropertyConfiguration object which can be
      * used to synthesize a new Properties file by adding values and
@@ -143,7 +162,6 @@
      */
     public PropertiesConfiguration(String fileName) throws IOException
     {
-
         load(fileName);
     }
 
@@ -166,6 +184,9 @@
     public void load(String fileName) throws IOException
     {
         load(getPropertyStream(fileName));
+
+        File file = new File(fileName);
+        lastModified = file.lastModified();
     }
 
     /**
@@ -258,4 +279,219 @@
         super.setBasePath(basePath);
         setIncludesAllowed(StringUtils.isNotEmpty(basePath));
     }
+
+    /**
+     * Tell if the auto reload mode is enabled or disabled.
+     */
+    public boolean isAutoReload() {
+        return autoReload;
+    }
+
+    /**
+     * Enable or disable the automatic reloading of the configuration
+     * when the file is changed.
+     */
+    public void setAutoReload(boolean autoReload) {
+        this.autoReload = autoReload;
+    }
+
+    /**
+     * Check if the configuration has changed since the last
+     * time it was loaded.
+     */
+    protected boolean hasChanged() {
+        if (fileName == null) {
+            return false;
+        }
+
+        File file = new File(fileName);
+        return (file.lastModified() > lastModified);
+    }
+
+    /**
+     * Reload the configuration if the file has been modified. The
+     * configuration will not be reloaded more than once every 5 seconds.
+     */
+    protected void reload() {
+        long now = System.currentTimeMillis();
+        if (autoReload) {
+            synchronized (reloadLock) {
+                if ((now > lastChecked + refreshDelay) && hasChanged()) {
+
+                    lastChecked = now;
+
+                    log.debug("Reloading configuration " + getFileName());
+                    System.out.println("Reloading configuration " + getFileName());
+
+                    try {
+                        store.clear();
+                        load();
+                    } catch (IOException e) {
+                        log.warn("Unable to reload configuration", e);
+                    }
+                }
+            }
+        }
+    }
+
+    public void save(String filename) throws IOException {
+        super.save(filename);
+        lastModified = System.currentTimeMillis();
+    }
+
+    public Iterator getKeys(String prefix) {
+        reload();
+        return super.getKeys(prefix);
+    }
+
+    public Properties getProperties(String key) {
+        reload();
+        return super.getProperties(key);
+    }
+
+    public Properties getProperties(String key, Properties defaults) {
+        reload();
+        return super.getProperties(key, defaults);
+    }
+
+    public Object getProperty(String key) {
+        reload();
+        return super.getProperty(key);
+    }
+
+    public boolean getBoolean(String key) {
+        reload();
+        return super.getBoolean(key);
+    }
+
+    public boolean getBoolean(String key, boolean defaultValue) {
+        reload();
+        return super.getBoolean(key, defaultValue);
+    }
+
+    public Boolean getBoolean(String key, Boolean defaultValue) {
+        reload();
+        return super.getBoolean(key, defaultValue);
+    }
+
+    public byte getByte(String key) {
+        reload();
+        return super.getByte(key);
+    }
+
+    public byte getByte(String key, byte defaultValue) {
+        reload();
+        return super.getByte(key, defaultValue);
+    }
+
+    public Byte getByte(String key, Byte defaultValue) {
+        reload();
+        return super.getByte(key, defaultValue);
+    }
+
+    public double getDouble(String key) {
+        reload();
+        return super.getDouble(key);
+    }
+
+    public double getDouble(String key, double defaultValue) {
+        reload();
+        return super.getDouble(key, defaultValue);
+    }
+
+    public Double getDouble(String key, Double defaultValue) {
+        reload();
+        return super.getDouble(key, defaultValue);
+    }
+
+    public float getFloat(String key) {
+        reload();
+        return super.getFloat(key);
+    }
+
+    public float getFloat(String key, float defaultValue) {
+        reload();
+        return super.getFloat(key, defaultValue);
+    }
+
+    public Float getFloat(String key, Float defaultValue) {
+        reload();
+        return super.getFloat(key, defaultValue);
+    }
+
+    public int getInt(String key) {
+        reload();
+        return super.getInt(key);
+    }
+
+    public int getInt(String key, int defaultValue) {
+        reload();
+        return super.getInt(key, defaultValue);
+    }
+
+    public Integer getInteger(String key, Integer defaultValue) {
+        reload();
+        return super.getInteger(key, defaultValue);
+    }
+
+    public long getLong(String key) {
+        reload();
+        return super.getLong(key);
+    }
+
+    public long getLong(String key, long defaultValue) {
+        reload();
+        return super.getLong(key, defaultValue);
+    }
+
+    public Long getLong(String key, Long defaultValue) {
+        reload();
+        return super.getLong(key, defaultValue);
+    }
+
+    public short getShort(String key) {
+        reload();
+        return super.getShort(key);
+    }
+
+    public short getShort(String key, short defaultValue) {
+        reload();
+        return super.getShort(key, defaultValue);
+    }
+
+    public Short getShort(String key, Short defaultValue) {
+        reload();
+        return super.getShort(key, defaultValue);
+    }
+
+    public String getString(String key) {
+        reload();
+        return super.getString(key);
+    }
+
+    public String getString(String key, String defaultValue) {
+        reload();
+        return super.getString(key, defaultValue);
+    }
+
+    public String[] getStringArray(String key) {
+        reload();
+        return super.getStringArray(key);
+    }
+
+    public Vector getVector(String key) {
+        reload();
+        return super.getVector(key);
+    }
+
+    public Vector getVector(String key, Vector defaultValue) {
+        reload();
+        return super.getVector(key, defaultValue);
+    }
+
+    public Configuration subset(String prefix) {
+        reload();
+        return super.subset(prefix);
+    }
+
 }
Index: src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/configuration/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java,v
retrieving revision 1.12
diff -u -r1.12 TestPropertiesConfiguration.java
--- src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java  12 Oct 
2003 09:31:09 -0000      1.12
+++ src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java  11 Dec 
2003 15:24:24 -0000
@@ -55,6 +55,7 @@
  */
 
 import java.io.File;
+import java.io.FileWriter;
 import java.util.Vector;
 
 /**
@@ -134,5 +135,40 @@
         pc.load();
 
         assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
+    }
+
+    public void testAutomaticReloading() throws Exception {
+
+        // create a new configuration
+        File file = new File("testReload.properties");
+
+        try {
+            FileWriter out = new FileWriter(file);
+            out.write("string=value1");
+            out.flush();
+            out.close();
+
+            // load the configuration
+            PropertiesConfiguration pc = new 
PropertiesConfiguration("testReload.properties");
+            pc.setFileName("testReload.properties");
+            pc.setAutoReload(false);
+            assertEquals("Initial value", "value1", pc.getString("string"));
+
+            Thread.sleep(500);
+
+            // change the file
+            out = new FileWriter(file);
+            out.write("string=value2");
+            out.flush();
+            out.close();
+
+            // test the automatic reloading
+            assertEquals("Modified value with disabled reloading", "value1", 
pc.getString("string"));
+            pc.setAutoReload(true);
+            assertEquals("Modified value with enabled reloading", "value2", 
pc.getString("string"));
+        } finally {
+            // delete the test file
+            file.delete();
+        }
     }
 }
package org.apache.commons.configuration;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.commons.lang.StringUtils;

/**
 * This is the "classic" Properties loader which loads the values from
 * a single or multiple files (which can be chained with "include =".
 * All given path references are either absolute or relative to the 
 * file name supplied in the Constructor.
 * <p>
 * In this class, empty PropertyConfigurations can be built, properties
 * added and later saved. include statements are (obviously) not supported
 * if you don't construct a PropertyConfiguration from a file.
 * <p>
 * If you want to use the getResourceAsStream() trick to load your 
 * resources without an absolute path, please take a look at the
 * ClassPropertiesConfiguration which is intended to be used for this.
 * 
 * @author <a href="mailto:[EMAIL PROTECTED]">Stefano Mazzocchi</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Dave Bryson</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Leon Messerschmidt</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Kent Johnson</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Daniel Rall</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Ilkka Priha</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Martin Poeschl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Henning P. Schmiedehausen</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Eric Pugh</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Oliver Heger</a>
 * @version $Id: PropertiesConfiguration.java,v 1.12 2003/11/11 15:02:07 epugh Exp $
 */
public class PropertiesConfiguration
        extends BasePropertiesConfiguration
        implements Configuration
{
    /** Static logger */
    Log log = LogFactory.getLog(PropertiesConfiguration.class);

    /** File separator. */
    protected String fileSeparator = System.getProperty("file.separator");

    /** 
     * The name of the file to be loaded.  This is used in conjuction with
     * the load method. */
    protected String fileName = null;

    /** Should the configuration be reloaded on file changes ? */
    protected boolean autoReload = false;

    /** The last time the configuration file was modified. */
    protected long lastModified;

    /** The last time the file was checked for changes. */
    protected long lastChecked;

    /** The minimum delay in milliseconds between checks. */
    protected long refreshDelay = 5000;

    /** The lock to block read access while reloading */
    private Object reloadLock = new Object();

    /**
     * Creates an empty PropertyConfiguration object which can be
     * used to synthesize a new Properties file by adding values and
     * then saving(). An object constructed by this C'tor can not be 
     * tickled into loading included files because it cannot supply a
     * base for relative includes.
     */
    public PropertiesConfiguration()
    {
        setIncludesAllowed(false);
    }

    /**
     * Creates an empty PropertyConfiguration object with
     * a Super-Object which is queries for every key.
     *
     * @param defaults Configuration defaults to use if key not in file
     * @throws IOException Error while loading the properties file
     */
    public PropertiesConfiguration(Configuration defaults) throws IOException
    {
        this();
        this.defaults = defaults;
    }

    /**
     * Creates and loads the extended properties from the specified file.
     * The specified file can contain "include = " properties which then
     * are loaded and merged into the properties.
     *
     * @param fileName The name of the Properties File to load.
     * @throws IOException Error while loading the properties file
     */
    public PropertiesConfiguration(String fileName) throws IOException
    {
        load(fileName);
    }

    /**
     * Load the properties from the fileName set by setFileName 
     *
     * @throws IOException
     */
    public void load() throws IOException
    {
        load(getFileName());
    }

    /**
     * Load the properties from the given fileName
     *
     * @param fileName A properties file to load
     * @throws IOException
     */
    public void load(String fileName) throws IOException
    {
        load(getPropertyStream(fileName));

        File file = new File(fileName);
        lastModified = file.lastModified();
    }

    /**
     * Creates and loads the extended properties from the specified file.
     *
     * @param file The name of the Properties File to load.
     * @param defaults Configuration defaults to use if key not in file
     * @throws IOException Error while loading the properties file
     */
    public PropertiesConfiguration(String file, Configuration defaults) throws 
IOException
    {
        this(file);
        this.defaults = defaults;
    }

    /**
     * Creates and loads the extended properties from the specified file.
     *
     * @param file The name of the Properties File to load.
     * @param defaultFile The name of a properties file whose values
     *                    should be used if a key is not in the file.
     * @throws IOException Error while loading the properties file
     */
    public PropertiesConfiguration(String file, String defaultFile) throws IOException
    {
        this(file);
        if (StringUtils.isNotEmpty(defaultFile))
        {
            this.defaults = new PropertiesConfiguration(defaultFile);
        }
    }

    /**
     * Gets a resource relative to the supplied base path. If the passed in
     * resource name is absolute, it is used directly.
     *
     * @param resourceName The resource Name
     * @return An Input Stream
     * @throws IOException Error while loading the properties file
     */
    public InputStream getPropertyStream(String resourceName) throws IOException
    {
        InputStream resource = null;
        URL url = null;
        
        try
        {
            url = ConfigurationUtils.getURL(getBasePath(), resourceName);
        }  /* try */
        catch(MalformedURLException uex)
        {
            throw new IOException("Cannot obtain URL for resource "
            + resourceName);
        }  /* catch */
        
        resource = url.openStream();

        setBasePath(url.toString());
        setIncludesAllowed(true);

        return resource;
    }

    /**
     * Returns the fileName.
     * @return String
     */
    public String getFileName()
    {
        return fileName;
    }

    /**
     * Sets the fileName.
     * @param fileName The fileName to set
     */
    public void setFileName(String fileName)
    {
        this.fileName = fileName;
    }

    /**
     * Extend the setBasePath method to turn includes
     * on and off based on the existence of a base path.
     *
     * @param basePath The new basePath to set.
     */
    public void setBasePath(String basePath)
    {
        super.setBasePath(basePath);
        setIncludesAllowed(StringUtils.isNotEmpty(basePath));
    }

    /**
     * Tell if the auto reload mode is enabled or disabled.
     */
    public boolean isAutoReload() {
        return autoReload;
    }

    /**
     * Enable or disable the automatic reloading of the configuration
     * when the file is changed.
     */
    public void setAutoReload(boolean autoReload) {
        this.autoReload = autoReload;
    }

    /**
     * Check if the configuration has changed since the last
     * time it was loaded.
     */
    protected boolean hasChanged() {
        if (fileName == null) {
            return false;
        }

        File file = new File(fileName);
        return (file.lastModified() > lastModified);
    }

    /**
     * Reload the configuration if the file has been modified. The
     * configuration will not be reloaded more than once every 5 seconds.
     */
    protected void reload() {
        long now = System.currentTimeMillis();
        if (autoReload) {
            synchronized (reloadLock) {
                if ((now > lastChecked + refreshDelay) && hasChanged()) {

                    lastChecked = now;

                    log.debug("Reloading configuration " + getFileName());
                    System.out.println("Reloading configuration " + getFileName());

                    try {
                        store.clear();
                        load();
                    } catch (IOException e) {
                        log.warn("Unable to reload configuration", e);
                    }
                }
            }
        }
    }

    public void save(String filename) throws IOException {
        super.save(filename);
        lastModified = System.currentTimeMillis();
    }

    public Iterator getKeys(String prefix) {
        reload();
        return super.getKeys(prefix);
    }

    public Properties getProperties(String key) {
        reload();
        return super.getProperties(key);
    }

    public Properties getProperties(String key, Properties defaults) {
        reload();
        return super.getProperties(key, defaults);
    }

    public Object getProperty(String key) {
        reload();
        return super.getProperty(key);
    }

    public boolean getBoolean(String key) {
        reload();
        return super.getBoolean(key);
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        reload();
        return super.getBoolean(key, defaultValue);
    }

    public Boolean getBoolean(String key, Boolean defaultValue) {
        reload();
        return super.getBoolean(key, defaultValue);
    }

    public byte getByte(String key) {
        reload();
        return super.getByte(key);
    }

    public byte getByte(String key, byte defaultValue) {
        reload();
        return super.getByte(key, defaultValue);
    }

    public Byte getByte(String key, Byte defaultValue) {
        reload();
        return super.getByte(key, defaultValue);
    }

    public double getDouble(String key) {
        reload();
        return super.getDouble(key);
    }

    public double getDouble(String key, double defaultValue) {
        reload();
        return super.getDouble(key, defaultValue);
    }

    public Double getDouble(String key, Double defaultValue) {
        reload();
        return super.getDouble(key, defaultValue);
    }

    public float getFloat(String key) {
        reload();
        return super.getFloat(key);
    }

    public float getFloat(String key, float defaultValue) {
        reload();
        return super.getFloat(key, defaultValue);
    }

    public Float getFloat(String key, Float defaultValue) {
        reload();
        return super.getFloat(key, defaultValue);
    }

    public int getInt(String key) {
        reload();
        return super.getInt(key);
    }

    public int getInt(String key, int defaultValue) {
        reload();
        return super.getInt(key, defaultValue);
    }

    public Integer getInteger(String key, Integer defaultValue) {
        reload();
        return super.getInteger(key, defaultValue);
    }

    public long getLong(String key) {
        reload();
        return super.getLong(key);
    }

    public long getLong(String key, long defaultValue) {
        reload();
        return super.getLong(key, defaultValue);
    }

    public Long getLong(String key, Long defaultValue) {
        reload();
        return super.getLong(key, defaultValue);
    }

    public short getShort(String key) {
        reload();
        return super.getShort(key);
    }

    public short getShort(String key, short defaultValue) {
        reload();
        return super.getShort(key, defaultValue);
    }

    public Short getShort(String key, Short defaultValue) {
        reload();
        return super.getShort(key, defaultValue);
    }

    public String getString(String key) {
        reload();
        return super.getString(key);
    }

    public String getString(String key, String defaultValue) {
        reload();
        return super.getString(key, defaultValue);
    }

    public String[] getStringArray(String key) {
        reload();
        return super.getStringArray(key);
    }

    public Vector getVector(String key) {
        reload();
        return super.getVector(key);
    }

    public Vector getVector(String key, Vector defaultValue) {
        reload();
        return super.getVector(key, defaultValue);
    }

    public Configuration subset(String prefix) {
        reload();
        return super.subset(prefix);
    }

}
package org.apache.commons.configuration;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.util.Iterator;
import java.util.Map;

import org.apache.commons.collections.SequencedHashMap;

/**
 * Basic configuration classe. Stores the configuration data but does not
 * provide any load or save functions. If you want to load your Configuration
 * from a file use PropertiesConfiguration or XmlConfiguration.
 *
 * This class extends normal Java properties by adding the possibility
 * to use the same key many times concatenating the value strings
 * instead of overwriting them.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Stefano Mazzocchi</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Dave Bryson</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Leon Messerschmidt</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Kent Johnson</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Daniel Rall</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Ilkka Priha</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Martin Poeschl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Henning P. Schmiedehausen</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Konstantin Shaposhnikov</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Oliver Heger</a>
 *
 * @version $Id: BaseConfiguration.java,v 1.16 2003/10/17 08:11:52 epugh Exp $
 */
public class BaseConfiguration extends AbstractConfiguration
{
    /** stores the configuration key-value pairs */
    protected Map store = new SequencedHashMap();
    
    /**
     * Empty constructor.  You must add all the values to this configuration.
     */
    public BaseConfiguration()
    {
        super();
    }
    
    /**
     * Creates an empty BaseConfiguration object with
     * a Super-Object which is queries for every key.
     *
     * @param defaults Configuration defaults to use if key not in file
     */
    public BaseConfiguration(Configuration defaults) 
    {
        super(defaults);
    }

    /**
     * Adds a key/value pair to the map.  This routine does no magic morphing.
     * It ensures the keylist is maintained
     *
     * @param key key to use for mapping
     * @param obj object to store
     */
    protected void addPropertyDirect(String key, Object obj)
    {
        Object o = getPropertyDirect(key);
        Object objAdd = null;
        
        if(o == null)
        {
            objAdd = obj;
        }
        else
        {
            if (o instanceof Container)
            {
                ((Container) o).add(obj);
            }
            else
            {
                // The token key is not a container.
                Container c = new Container();

                // There is an element. Put it into the container
                // at the first position
                c.add(o);

                // Now gobble up the supplied object
                c.add(obj);

                objAdd = c;
            }
        }
        
        if(objAdd != null)
        {
            store.put(key, objAdd);
        }
    }

    /**
     * Read property from underlying map.
     *
     * @param key key to use for mapping
     *
     * @return object associated with the given configuration key.
     */
    protected Object getPropertyDirect(String key) 
    {
        return store.get(key);
    }

    /**
     * Check if the configuration is empty
     *
     * @return <code>true</code> if Configuration is empty,
     * <code>false</code> otherwise.
     */
    public boolean isEmpty()
    {
        return store.isEmpty();
    }

    /**
     * check if the configuration contains the key
     *
     * @param key the configuration key
     * 
     * @return <code>true</code> if Configuration contain given key,
     * <code>false</code> otherwise.
     */
    public boolean containsKey(String key)
    {
        return store.containsKey(key);
    }

    /**
     * Clear a property in the configuration.
     *
     * @param key the key to remove along with corresponding value.
     */
    public void clearProperty(String key)
    {
        if (containsKey(key))
        {
            store.remove(key);
        }
    }

    /**
     * Get the list of the keys contained in the configuration
     * repository.
     *
     * @return An Iterator.
     */
    public Iterator getKeys()
    {
        return store.keySet().iterator();
    }
}
package org.apache.commons.configuration;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.io.File;
import java.io.FileWriter;
import java.util.Vector;

/**
 * test for loading and saving properties files
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Martin Poeschl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Henning P. Schmiedehausen</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Daniel Rall</a>
 * @version $Id: TestPropertiesConfiguration.java,v 1.12 2003/10/12 09:31:09 rdonkin 
Exp $
 */
public class TestPropertiesConfiguration extends TestBasePropertiesConfiguration
{
    /** The File that we test with */
    private String testProperties = new File("conf/test.properties").getAbsolutePath();

    private String testBasePath = new File("conf").getAbsolutePath();
    private String testBasePath2 = new 
File("conf").getAbsoluteFile().getParentFile().getAbsolutePath();

    public TestPropertiesConfiguration(String s)
    {
        super(s);
    }

    protected void setUp() throws Exception
    {
        conf = new PropertiesConfiguration(testProperties);
    }

    public void atestSave() throws Exception
    {
        PropertiesConfiguration toSave = new PropertiesConfiguration();
        toSave.addProperty("string", "value1");
        Vector vec = new Vector();
        for (int i = 1; i < 5; i++)
        {
            vec.add("value" + i);
        }
        toSave.addProperty("array", vec);
        String filename = "STRING0";

        toSave.save(filename);
    }

    public void atestLoadViaProperty() throws Exception
    {
        PropertiesConfiguration pc = new PropertiesConfiguration();
        pc.setFileName(testProperties);
        pc.load();

        assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    }

    public void testLoadViaPropertyWithBasePath() throws Exception
    {

        PropertiesConfiguration pc = new PropertiesConfiguration();
        pc.setBasePath(testBasePath);
        pc.setFileName("test.properties");
        pc.load();

        assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    }

    public void testLoadViaPropertyWithBasePath2() throws Exception
    {

        PropertiesConfiguration pc = new PropertiesConfiguration();
        pc.setBasePath(testBasePath2);
        pc.setFileName("conf/test.properties");
        pc.load();

        assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));

        pc = new PropertiesConfiguration();
        pc.setBasePath(testBasePath2);
        pc.setFileName("conf/test.properties");
        pc.load();

        assertTrue("Make sure we have multiple keys", pc.getBoolean("test.boolean"));
    }

    public void testAutomaticReloading() throws Exception {

        // create a new configuration
        File file = new File("testReload.properties");

        try {
            FileWriter out = new FileWriter(file);
            out.write("string=value1");
            out.flush();
            out.close();

            // load the configuration
            PropertiesConfiguration pc = new 
PropertiesConfiguration("testReload.properties");
            pc.setFileName("testReload.properties");
            pc.setAutoReload(false);
            assertEquals("Initial value", "value1", pc.getString("string"));

            Thread.sleep(500);

            // change the file
            out = new FileWriter(file);
            out.write("string=value2");
            out.flush();
            out.close();

            // test the automatic reloading
            assertEquals("Modified value with disabled reloading", "value1", 
pc.getString("string"));
            pc.setAutoReload(true);
            assertEquals("Modified value with enabled reloading", "value2", 
pc.getString("string"));
        } finally {
            // delete the test file
            file.delete();
        }
    }
}

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to