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>[EMAIL PROTECTED] 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:[EMAIL PROTECTED]">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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]