You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by oh...@apache.org on 2007/04/22 20:54:58 UTC

svn commit: r531254 - in /jakarta/commons/proper/configuration/trunk: src/java/org/apache/commons/configuration/ src/test/org/apache/commons/configuration/ src/test/org/apache/commons/configuration/event/ xdocs/ xdocs/userguide/

Author: oheger
Date: Sun Apr 22 11:54:57 2007
New Revision: 531254

URL: http://svn.apache.org/viewvc?view=rev&rev=531254
Log:
CONFIGURATION-265: Auto-save of hierarchical file-based configurations is now also triggered by changes at a SubnodeConfiguration. A new event type EVENT_SUBNODE_CHANGED was introduced to report such changes to registered event listeners. Improvements of JavaDoc for HierarchicalConfiguration.

Modified:
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractHierarchicalFileConfiguration.java
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
    jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
    jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/AbstractTestConfigurationEvents.java
    jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/TestHierarchicalConfigurationEvents.java
    jakarta/commons/proper/configuration/trunk/xdocs/changes.xml
    jakarta/commons/proper/configuration/trunk/xdocs/userguide/howto_events.xml

Modified: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractHierarchicalFileConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractHierarchicalFileConfiguration.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractHierarchicalFileConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractHierarchicalFileConfiguration.java Sun Apr 22 11:54:57 2007
@@ -339,6 +339,19 @@
     }
 
     /**
+     * Reacts on changes of an associated subnode configuration. If the auto
+     * save mechanism is active, the configuration must be saved.
+     *
+     * @param event the event describing the change
+     * @since 1.5
+     */
+    protected void subnodeConfigurationChanged(ConfigurationEvent event)
+    {
+        delegate.possiblySave();
+        super.subnodeConfigurationChanged(event);
+    }
+
+    /**
      * Creates the file configuration delegate, i.e. the object that implements
      * functionality required by the <code>FileConfiguration</code> interface.
      * This base implementation will return an instance of the

Modified: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java Sun Apr 22 11:54:57 2007
@@ -28,12 +28,15 @@
 
 import org.apache.commons.collections.set.ListOrderedSet;
 import org.apache.commons.collections.iterators.SingletonIterator;
+import org.apache.commons.configuration.event.ConfigurationEvent;
+import org.apache.commons.configuration.event.ConfigurationListener;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
 import org.apache.commons.configuration.tree.DefaultConfigurationNode;
 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
 import org.apache.commons.configuration.tree.ExpressionEngine;
 import org.apache.commons.configuration.tree.NodeAddData;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
 import org.apache.commons.lang.StringUtils;
 
 /**
@@ -94,20 +97,52 @@
  * <code>getMaxIndex()</code> method that returns the maximum allowed index
  * that can be added to a given property key. This method can be used to iterate
  * over all values defined for a certain property.</p>
+ * <p>Since the 1.3 release of <em>Commons Configuration</em> hierarchical
+ * configurations support an <em>expression engine</em>. This expression engine
+ * is responsible for evaluating the passed in configuration keys and map them
+ * to the stored properties. The examples above are valid for the default
+ * expression engine, which is used when a new <code>HierarchicalConfiguration</code>
+ * instance is created. With the <code>setExpressionEngine()</code> method a
+ * different expression engine can be set. For instance with
+ * <code>{@link XPathExpressionEngine}</code> there is an expression engine
+ * available that supports configuration keys in XPATH syntax.</p>
+ * <p>In addition to the events common for all configuration classes hierarchical
+ * configurations support some more events that correspond to some specific
+ * methods and features:
+ * <dl><dt><em>EVENT_ADD_NODES</em></dt><dd>The <code>addNodes()</code> method
+ * was called; the event object contains the key, to which the nodes were added,
+ * and a collection with the new nodes as value.</dd>
+ * <dt><em>EVENT_CLEAR_TREE</em></dt><dd>The <code>clearTree()</code> method was
+ * called; the event object stores the key of the removed sub tree.</dd>
+ * <dt><em>EVENT_SUBNODE_CHANGED</em></dt><dd>A <code>SubnodeConfiguration</code>
+ * that was created from this configuration has been changed. The value property
+ * of the event object contains the original event object as it was sent by the
+ * subnode configuration.</dd></dl></p> 
  *
- * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
- * @version $Id: HierarchicalConfiguration.java,v 1.14 2004/12/02 22:05:52
- * ebourg Exp $
+ * @author Oliver Heger
+ * @version $Id$
  */
 public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
 {
-    /** Constant for the clear tree event.*/
+    /**
+     * Constant for the clear tree event.
+     * @since 1.3
+     */
     public static final int EVENT_CLEAR_TREE = 10;
 
-    /** Constant for the add nodes event.*/
+    /**
+     * Constant for the add nodes event.
+     * @since 1.3
+     */
     public static final int EVENT_ADD_NODES = 11;
 
     /**
+     * Constant for the subnode configuration modified event.
+     * @since 1.5
+     */
+    public static final int EVENT_SUBNODE_CHANGED = 12;
+
+    /**
      * The serial version UID.
      */
     private static final long serialVersionUID = 3373812230395363192L;
@@ -564,7 +599,9 @@
      */
     protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
     {
-        return new SubnodeConfiguration(this, node);
+        SubnodeConfiguration result = new SubnodeConfiguration(this, node);
+        registerSubnodeConfiguration(result);
+        return result;
     }
 
     /**
@@ -583,6 +620,39 @@
         SubnodeConfiguration result = createSubnodeConfiguration(node);
         result.setSubnodeKey(subnodeKey);
         return result;
+    }
+
+    /**
+     * This method is always called when a subnode configuration created from
+     * this configuration has been modified. This implementation transforms the
+     * received event into an event of type <code>EVENT_SUBNODE_CHANGED</code>
+     * and notifies the registered listeners.
+     *
+     * @param event the event describing the change
+     * @since 1.5
+     */
+    protected void subnodeConfigurationChanged(ConfigurationEvent event)
+    {
+        fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
+    }
+
+    /**
+     * Registers this instance at the given subnode configuration. This
+     * implementation will register a change listener, so that modifications of
+     * the subnode configuration can be tracked.
+     *
+     * @param config the subnode configuration
+     * @since 1.5
+     */
+    void registerSubnodeConfiguration(SubnodeConfiguration config)
+    {
+        config.addConfigurationListener(new ConfigurationListener()
+        {
+            public void configurationChanged(ConfigurationEvent event)
+            {
+                subnodeConfigurationChanged(event);
+            }
+        });
     }
 
     /**

Modified: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java Sun Apr 22 11:54:57 2007
@@ -245,7 +245,9 @@
      */
     protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
     {
-        return new SubnodeConfiguration(getParent(), node);
+        SubnodeConfiguration result = new SubnodeConfiguration(getParent(), node);
+        getParent().registerSubnodeConfiguration(result);
+        return result;
     }
 
     /**

Modified: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java Sun Apr 22 11:54:57 2007
@@ -33,7 +33,6 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
-import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
 import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
@@ -510,7 +509,7 @@
 
     public void testAutoSave() throws Exception
     {
-        conf.setFile(new File("target/testsave.xml"));
+        conf.setFile(testSaveConf);
         assertFalse(conf.isAutoSave());
         conf.setAutoSave(true);
         assertTrue(conf.isAutoSave());
@@ -1026,6 +1025,43 @@
     }
 
     /**
+     * Tests whether the auto save mechanism is triggered by changes at a
+     * subnode configuration.
+     */
+    public void testAutoSaveWithSubnodeConfig() throws ConfigurationException
+    {
+        final String newValue = "I am autosaved";
+        conf.setFile(testSaveConf);
+        conf.setAutoSave(true);
+        Configuration sub = conf.configurationAt("element2.subelement");
+        sub.setProperty("subsubelement", newValue);
+        assertEquals("Change not visible to parent", newValue, conf
+                .getString("element2.subelement.subsubelement"));
+        XMLConfiguration conf2 = new XMLConfiguration(testSaveConf);
+        assertEquals("Change was not saved", newValue, conf2
+                .getString("element2.subelement.subsubelement"));
+    }
+
+    /**
+     * Tests whether a subnode configuration created from another subnode
+     * configuration of a XMLConfiguration can trigger the auto save mechanism.
+     */
+    public void testAutoSaveWithSubSubnodeConfig() throws ConfigurationException
+    {
+        final String newValue = "I am autosaved";
+        conf.setFile(testSaveConf);
+        conf.setAutoSave(true);
+        SubnodeConfiguration sub1 = conf.configurationAt("element2");
+        SubnodeConfiguration sub2 = sub1.configurationAt("subelement");
+        sub2.setProperty("subsubelement", newValue);
+        assertEquals("Change not visible to parent", newValue, conf
+                .getString("element2.subelement.subsubelement"));
+        XMLConfiguration conf2 = new XMLConfiguration(testSaveConf);
+        assertEquals("Change was not saved", newValue, conf2
+                .getString("element2.subelement.subsubelement"));
+    }
+
+    /**
      * Prepares a configuration object for testing a reload operation.
      *
      * @return the initialized configuration
@@ -1036,14 +1072,7 @@
         removeTestFile();
         conf.save(testSaveConf);
         XMLConfiguration c = new XMLConfiguration(testSaveConf);
-        c.setReloadingStrategy(new FileChangedReloadingStrategy()
-        {
-            // Report always a change
-            protected boolean hasChanged()
-            {
-                return true;
-            }
-        });
+        c.setReloadingStrategy(new FileAlwaysReloadingStrategy());
         conf.setProperty("test(0).entity", "newValue");
         conf.save(testSaveConf);
         return c;

Modified: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/AbstractTestConfigurationEvents.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/AbstractTestConfigurationEvents.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/AbstractTestConfigurationEvents.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/AbstractTestConfigurationEvents.java Sun Apr 22 11:54:57 2007
@@ -218,14 +218,27 @@
         public void checkEvent(int type, String propName, Object propValue,
                 boolean before)
         {
-            assertFalse("Too few events received", events.isEmpty());
-            ConfigurationEvent e = (ConfigurationEvent) events.removeFirst();
-            assertEquals("Wrong event source", config, e.getSource());
-            assertEquals("Wrong event type", type, e.getType());
+            ConfigurationEvent e = nextEvent(type);
             assertEquals("Wrong property name", propName, e.getPropertyName());
             assertEquals("Wrong property value", propValue, e
                     .getPropertyValue());
             assertEquals("Wrong before flag", before, e.isBeforeUpdate());
+        }
+
+        /**
+         * Returns the next received event and checks for the expected type.
+         * This method can be used instead of <code>checkEvent()</code> for
+         * comparing complex event values.
+         * @param expectedType the expected type of the event
+         * @return the event object
+         */
+        public ConfigurationEvent nextEvent(int expectedType)
+        {
+            assertFalse("Too few events received", events.isEmpty());
+            ConfigurationEvent e = (ConfigurationEvent) events.removeFirst();
+            assertEquals("Wrong event source", config, e.getSource());
+            assertEquals("Wrong event type", expectedType, e.getType());
+            return e;
         }
 
         /**

Modified: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/TestHierarchicalConfigurationEvents.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/TestHierarchicalConfigurationEvents.java?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/TestHierarchicalConfigurationEvents.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/event/TestHierarchicalConfigurationEvents.java Sun Apr 22 11:54:57 2007
@@ -21,6 +21,7 @@
 
 import org.apache.commons.configuration.AbstractConfiguration;
 import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.SubnodeConfiguration;
 import org.apache.commons.configuration.tree.DefaultConfigurationNode;
 
 /**
@@ -78,5 +79,45 @@
         ((HierarchicalConfiguration) config).addNodes(TEST_PROPNAME,
                 new ArrayList());
         l.done();
+    }
+
+    /**
+     * Tests whether manipulations of a subnode configuration trigger correct
+     * events.
+     */
+    public void testSubnodeChangedEvent()
+    {
+        SubnodeConfiguration sub = ((HierarchicalConfiguration) config)
+                .configurationAt(EXIST_PROPERTY);
+        sub.addProperty("newProp", "newValue");
+        checkSubnodeEvent(l
+                .nextEvent(HierarchicalConfiguration.EVENT_SUBNODE_CHANGED),
+                true);
+        checkSubnodeEvent(l
+                .nextEvent(HierarchicalConfiguration.EVENT_SUBNODE_CHANGED),
+                false);
+        l.done();
+    }
+
+    /**
+     * Tests whether a received event contains a correct subnode event.
+     *
+     * @param event the event object
+     * @param before the expected before flag
+     */
+    private void checkSubnodeEvent(ConfigurationEvent event, boolean before)
+    {
+        assertEquals("Wrong before flag of nesting event", before, event
+                .isBeforeUpdate());
+        assertTrue("No subnode event found in value",
+                event.getPropertyValue() instanceof ConfigurationEvent);
+        ConfigurationEvent evSub = (ConfigurationEvent) event
+                .getPropertyValue();
+        assertEquals("Wrong event type",
+                HierarchicalConfiguration.EVENT_ADD_PROPERTY, evSub.getType());
+        assertEquals("Wrong property name", "newProp", evSub.getPropertyName());
+        assertEquals("Wrong property value", "newValue", evSub
+                .getPropertyValue());
+        assertEquals("Wrong before flag", before, evSub.isBeforeUpdate());
     }
 }

Modified: jakarta/commons/proper/configuration/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/xdocs/changes.xml?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/xdocs/changes.xml (original)
+++ jakarta/commons/proper/configuration/trunk/xdocs/changes.xml Sun Apr 22 11:54:57 2007
@@ -23,6 +23,12 @@
 
   <body>
     <release version="1.5-SNAPSHOT" date="in SVN" description="">
+      <action dev="oheger" type="update" issue="CONFIGURATION-265">
+        For hierarchical file-based configurations the auto-save mechanism is
+        now also triggered if a subnode configuration is changed. In such a case
+        the new event type EVENT_SUBNODE_CHANGED will be sent to registered
+        listeners.
+      </action>
       <action dev="oheger" type="update" issue="CONFIGURATION-266" due-to="Tobias Noebel">
         ConfigurationInterpolator now also invokes the default lookup object for
         variables with a prefix that could not be resolved by their associated

Modified: jakarta/commons/proper/configuration/trunk/xdocs/userguide/howto_events.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/xdocs/userguide/howto_events.xml?view=diff&rev=531254&r1=531253&r2=531254
==============================================================================
--- jakarta/commons/proper/configuration/trunk/xdocs/userguide/howto_events.xml (original)
+++ jakarta/commons/proper/configuration/trunk/xdocs/userguide/howto_events.xml Sun Apr 22 11:54:57 2007
@@ -80,8 +80,17 @@
         <dt>AbstractFileConfiguration</dt>
         <dd>EVENT_RELOAD (the configuration was reloaded)</dd>
         <dt>HierarchicalConfiguration</dt>
-        <dd>EVENT_ADD_NODES (the <code>addNodes()</code> method was called),
-        EVENT_CLEAR_TREE (the <code>clearTree()</code> method was called)</dd>
+        <dd>EVENT_ADD_NODES (the <code>addNodes()</code> method was called;
+        the event object contains the key, to which the nodes were added, and
+        a collection with the new nodes as value),
+        EVENT_CLEAR_TREE (the <code>clearTree()</code> method was called; the
+        event object stores the key of the removed sub tree),
+        EVENT_SUBNODE_CHANGED (a <code>SubnodeConfiguration</code> that was
+        created from this configuration has been changed. The value property
+        of the event object contains the original event object as it was sent by
+        the subnode configuration. <em>Note: At the moment it is not possible
+        to map the property key as it was received from the subnode configuration
+        into the namespace of the parent configuration.)</em></dd>
       </dl>
     </p>
     </subsection>



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org