Author: rgoers Date: Mon Jun 8 17:09:24 2009 New Revision: 782704 URL: http://svn.apache.org/viewvc?rev=782704&view=rev Log: VFSFileMonitorReloadingStrategy was not able to detect files with invalid urls - see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6351751
Added: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml (with props) Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/ConfigurationUtils.java commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestFileConfiguration.java commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/reloading/TestVFSFileMonitorReloadingStrategy.java Added: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml?rev=782704&view=auto ============================================================================== --- commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml (added) +++ commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml Mon Jun 8 17:09:24 2009 @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!-- Test configuration definition file that demonstrates complex initialization --> +<configuration> + <header> + <result delimiterParsingDisabled="true" forceReloadCheck="true" + config-class="org.apache.commons.configuration.DynamicCombinedConfiguration" + keyPattern="$${sys:Id}"> + <nodeCombiner config-class="org.apache.commons.configuration.tree.MergeCombiner"/> + <expressionEngine + config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/> + </result> + <fileSystem config-class="org.apache.commons.configuration.VFSFileSystem"/> + <providers> + <provider config-tag="multifile" + config-class="org.apache.commons.configuration.DefaultConfigurationBuilder$FileConfigurationProvider" + configurationClass="org.apache.commons.configuration.MultiFileHierarchicalConfiguration"/> + </providers> + </header> + <override> + <multifile filePattern="${sys:basePath}/testwrite/testMultiConfiguration_$$${sys:Id}.xml" + config-name="clientConfig" delimiterParsingDisabled="true" schemaValidation="false"> + <expressionEngine + config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/> + <reloadingStrategy delay="500" + config-class="org.apache.commons.configuration.reloading.VFSFileMonitorReloadingStrategy"/> + </multifile> + <xml fileName="testMultiConfiguration_default.xml" + config-name="defaultConfig" delimiterParsingDisabled="true"> + <expressionEngine + config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/> + <reloadingStrategy + config-class="org.apache.commons.configuration.reloading.VFSFileMonitorReloadingStrategy"/> + </xml> + </override> +</configuration> \ No newline at end of file Propchange: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder2.xml ------------------------------------------------------------------------------ svn:mime-type = text/xml Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java Mon Jun 8 17:09:24 2009 @@ -84,6 +84,12 @@ /** Constant for the configuration reload event.*/ public static final int EVENT_RELOAD = 20; + /** Constant fro the configuration changed event. */ + public static final int EVENT_CONFIG_CHANGED = 21; + + /** The root of the file scheme */ + private static final String FILE_SCHEME = "file:"; + /** Stores the file name.*/ protected String fileName; @@ -555,8 +561,14 @@ */ public void setFileName(String fileName) { + if (fileName != null && fileName.startsWith(FILE_SCHEME) && !fileName.startsWith("file://")) + { + fileName = "file://" + fileName.substring(FILE_SCHEME.length()); + } + sourceURL = null; this.fileName = fileName; + getLogger().debug("FileName set to " + fileName); } /** @@ -588,8 +600,13 @@ */ public void setBasePath(String basePath) { + if (basePath != null && basePath.startsWith(FILE_SCHEME) && !basePath.startsWith("file://")) + { + basePath = "file://" + basePath.substring(FILE_SCHEME.length()); + } sourceURL = null; this.basePath = basePath; + getLogger().debug("Base path set to " + basePath); } /** @@ -689,6 +706,7 @@ setBasePath(ConfigurationUtils.getBasePath(url)); setFileName(ConfigurationUtils.getFileName(url)); sourceURL = url; + getLogger().debug("URL set to " + url); } public void setAutoSave(boolean autoSave) @@ -825,6 +843,14 @@ } /** + * Send notification that the configuration has changed. + */ + public void configurationChanged() + { + fireEvent(EVENT_CONFIG_CHANGED, null, getURL(), true); + } + + /** * Enters the "No reloading mode". As long as this mode is active * no reloading will be performed. This is necessary for some * implementations of <code>save()</code> in derived classes, which may Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java Mon Jun 8 17:09:24 2009 @@ -559,7 +559,11 @@ */ public void configurationChanged(ConfigurationEvent event) { - if (!event.isBeforeUpdate()) + if (event.getType() == AbstractFileConfiguration.EVENT_CONFIG_CHANGED) + { + fireEvent(event.getType(), event.getPropertyName(), event.getPropertyValue(), event.isBeforeUpdate()); + } + else if (!event.isBeforeUpdate()) { invalidate(); } Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/ConfigurationUtils.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/ConfigurationUtils.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/ConfigurationUtils.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/ConfigurationUtils.java Mon Jun 8 17:09:24 2009 @@ -55,6 +55,9 @@ /** Constant for the resource path separator.*/ static final String RESOURCE_PATH_SEPARATOR = "/"; + /** Constanct for the file URL protocol */ + private static final String FILE_SCHEME = "file:"; + /** Constant for the name of the clone() method.*/ private static final String METHOD_CLONE = "clone"; @@ -569,6 +572,10 @@ } String s = url.toString(); + if (s.startsWith(FILE_SCHEME) && !s.startsWith("file://")) + { + s = "file://" + s.substring(FILE_SCHEME.length()); + } if (s.endsWith("/") || StringUtils.isEmpty(url.getPath())) { Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java Mon Jun 8 17:09:24 2009 @@ -772,13 +772,13 @@ config.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); config.setConversionExpressionEngine(getConversionExpressionEngine()); config.setListDelimiter(getListDelimiter()); - Iterator iter = config.getErrorListeners().iterator(); + Iterator iter = getErrorListeners().iterator(); while (iter.hasNext()) { ConfigurationErrorListener listener = (ConfigurationErrorListener) iter.next(); config.addErrorListener(listener); } - iter = config.getConfigurationListeners().iterator(); + iter = getConfigurationListeners().iterator(); while (iter.hasNext()) { ConfigurationListener listener = (ConfigurationListener) iter.next(); Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java Mon Jun 8 17:09:24 2009 @@ -17,9 +17,11 @@ package org.apache.commons.configuration.reloading; import org.apache.commons.configuration.FileConfiguration; +import org.apache.commons.configuration.FileSystemBased; import org.apache.commons.configuration.ConfigurationRuntimeException; import org.apache.commons.configuration.FileSystem; -import org.apache.commons.configuration.FileSystemBased; +import org.apache.commons.configuration.AbstractFileConfiguration; + import org.apache.commons.vfs.impl.DefaultFileMonitor; import org.apache.commons.vfs.FileListener; import org.apache.commons.vfs.FileChangeEvent; @@ -32,7 +34,6 @@ import java.util.Map; import java.util.HashMap; - /** * <p>A reloading strategy that will reload the configuration every time its * underlying file is changed.</p> @@ -113,6 +114,25 @@ { throw new IllegalStateException("No configuration has been set for this strategy"); } + FileObject file; + + try + { + FileSystemManager fsManager = VFS.getManager(); + FileSystem fs = ((FileSystemBased) configuration).getFileSystem(); + String uri = fs.getPath(null, configuration.getURL(), configuration.getBasePath(), + configuration.getFileName()); + if (uri == null) + { + throw new ConfigurationRuntimeException("Unable to determine file to monitor"); + } + file = fsManager.resolveFile(uri); + } + catch (FileSystemException fse) + { + String msg = "Unable to monitor " + configuration.getURL().toString(); + throw new ConfigurationRuntimeException(msg, fse); + } synchronized (INIT_GATE) { if (fm == null) @@ -130,35 +150,17 @@ fm.setDelay(delay); } } - } - - try - { - FileSystemManager fsManager = VFS.getManager(); - FileSystem fs = ((FileSystemBased) configuration).getFileSystem(); - String uri = fs.getPath(null, configuration.getURL(), configuration.getBasePath(), - configuration.getFileName()); - if (uri == null) - { - throw new ConfigurationRuntimeException("Unable to determine file to monitor"); - } - FileObject file = fsManager.resolveFile(uri); file.getFileSystem().addListener(file, this); fm.addFile(file); strategies.put(file, this); } - catch (FileSystemException fse) - { - String msg = "Unable to monitor " + configuration.getURL().toString(); - throw new ConfigurationRuntimeException(msg, fse); - } } /** - * Shutdown the reloading strategy + * Shutdown all reloading strategies */ - public void stopMonitor() + public static void stopMonitor() { synchronized (INIT_GATE) { @@ -167,6 +169,7 @@ fm.stop(); fm = null; } + Iterator iter = strategies.entrySet().iterator(); while (iter.hasNext()) @@ -207,6 +210,7 @@ public void fileCreated(FileChangeEvent event) throws Exception { reloadRequired = true; + fireEvent(); } /** @@ -227,6 +231,15 @@ public void fileChanged(FileChangeEvent event) throws Exception { reloadRequired = true; + fireEvent(); + } + + private void fireEvent() + { + if (configuration instanceof AbstractFileConfiguration) + { + ((AbstractFileConfiguration) configuration).configurationChanged(); + } } } Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestFileConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestFileConfiguration.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestFileConfiguration.java (original) +++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestFileConfiguration.java Mon Jun 8 17:09:24 2009 @@ -62,9 +62,9 @@ assertEquals("base path", "http://commons.apache.org/configuration/", config.getBasePath()); assertEquals("file name", "index.html", config.getFileName()); - // file URL + // file URL - This url is invalid, a valid url would be file:///temp/test.properties. config.setURL(new URL("file:/temp/test.properties")); - assertEquals("base path", "file:/temp/", config.getBasePath()); + assertEquals("base path", "file:///temp/", config.getBasePath()); assertEquals("file name", "test.properties", config.getFileName()); } Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java (original) +++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java Mon Jun 8 17:09:24 2009 @@ -31,8 +31,11 @@ import org.apache.commons.configuration.beanutils.BeanHelper; import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy; +import org.apache.commons.configuration.reloading.VFSFileMonitorReloadingStrategy; import org.apache.commons.configuration.tree.DefaultConfigurationNode; import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; +import org.apache.commons.configuration.event.ConfigurationListener; +import org.apache.commons.configuration.event.ConfigurationEvent; /** * Test class for DefaultConfigurationBuilder. @@ -40,7 +43,7 @@ * @author Oliver Heger * @version $Id$ */ -public class TestVFSConfigurationBuilder extends TestCase +public class TestVFSConfigurationBuilder extends TestCase implements ConfigurationListener { /** Test configuration definition file. */ private static final File TEST_FILE = new File( @@ -91,12 +94,27 @@ private static final File FILEMONITOR_FILE = new File( "target/test-classes/testFileMonitorConfigurationBuilder.xml"); + private static final File FILEMONITOR2_FILE = new File( + "target/test-classes/testFileMonitorConfigurationBuilder2.xml"); + + private static final String FILEMONITOR_URI = "file://" + System.getProperty("user.dir") + + "/target/test-classes/testFileMonitorConfigurationBuilder2.xml"; + /** Constant for the name of an optional configuration.*/ private static final String OPTIONAL_NAME = "optionalConfig"; + /** true when a file is changed */ + private boolean configChanged = false; + /** Stores the object to be tested. */ DefaultConfigurationBuilder factory; + public TestVFSConfigurationBuilder() + { + super(); + VFSFileMonitorReloadingStrategy.stopMonitor(); + } + protected void setUp() throws Exception { super.setUp(); @@ -105,6 +123,7 @@ "org.apache.commons.configuration.MockInitialContextFactory"); System.setProperty("test_file_xml", "test.xml"); System.setProperty("test_file_combine", "testcombine1.xml"); + System.setProperty("basePath", "file://" + System.getProperty("user.dir") + "/target/test-classes"); FileSystem.setDefaultFileSystem(new VFSFileSystem()); factory = new DefaultConfigurationBuilder(); factory.clearErrorListeners(); // avoid exception messages @@ -875,6 +894,7 @@ CombinedConfiguration config = factory.getConfiguration(true); assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration); + verify(null, config, 50); verify("1001", config, 15); verify("1002", config, 25); verify("1003", config, 35); @@ -1000,6 +1020,7 @@ CombinedConfiguration config = factory.getConfiguration(true); assertNotNull(config); + config.addConfigurationListener(this); verify("1001", config, 15); // Allow time for FileMonitor to set up. @@ -1007,10 +1028,11 @@ XMLConfiguration x = new XMLConfiguration(output); x.setProperty("rowsPerPage", "50"); x.save(); - // Let FileMonitor detect the change. - Thread.sleep(2000); + + waitForChange(); verify("1001", config, 50); output.delete(); + VFSFileMonitorReloadingStrategy.stopMonitor(); } public void testFileMonitor2() throws Exception @@ -1025,6 +1047,7 @@ System.getProperties().remove("Id"); CombinedConfiguration config = factory.getConfiguration(true); + config.addConfigurationListener(this); assertNotNull(config); verify("1002", config, 50); @@ -1034,12 +1057,71 @@ copyFile(input, output); // Allow time for the monitor to notice the change. - Thread.sleep(2000); + //Thread.sleep(2000); + waitForChange(); verify("1002", config, 25); output.delete(); + VFSFileMonitorReloadingStrategy.stopMonitor(); + } + + + public void testFileMonitor3() throws Exception + { + // create a new configuration + File input = new File("target/test-classes/testMultiConfiguration_1001.xml"); + File output = new File("target/test-classes/testwrite/testMultiConfiguration_1001.xml"); + output.delete(); + output.getParentFile().mkdir(); + copyFile(input, output); + + factory.setFile(FILEMONITOR2_FILE); + System.getProperties().remove("Id"); + + CombinedConfiguration config = factory.getConfiguration(true); + assertNotNull(config); + config.addConfigurationListener(this); + verify("1001", config, 15); + + // Allow time for FileMonitor to set up. + Thread.sleep(1000); + XMLConfiguration x = new XMLConfiguration(output); + x.setProperty("rowsPerPage", "50"); + x.save(); + // Let FileMonitor detect the change. + //Thread.sleep(2000); + waitForChange(); + verify("1001", config, 50); + output.delete(); + VFSFileMonitorReloadingStrategy.stopMonitor(); } + public void testFileMonitor4() throws Exception + { + // create a new configuration + File input = new File("target/test-classes/testMultiConfiguration_1002.xml"); + File output = new File("target/test-classes/testwrite/testMultiConfiguration_1002.xml"); + output.delete(); + + factory.setFileName(FILEMONITOR_URI); + System.getProperties().remove("Id"); + + CombinedConfiguration config = factory.getConfiguration(true); + assertNotNull(config); + config.addConfigurationListener(this); + + verify("1002", config, 50); + Thread.sleep(1000); + output.getParentFile().mkdir(); + copyFile(input, output); + + // Allow time for the monitor to notice the change. + //Thread.sleep(2000); + waitForChange(); + verify("1002", config, 25); + output.delete(); + VFSFileMonitorReloadingStrategy.stopMonitor(); + } private void copyFile(File input, File output) throws IOException { @@ -1058,9 +1140,50 @@ private void verify(String key, CombinedConfiguration config, int rows) { - System.setProperty("Id", key); + if (key == null) + { + System.getProperties().remove("Id"); + } + else + { + System.setProperty("Id", key); + } int actual = config.getInt("rowsPerPage"); assertTrue("expected: " + rows + " actual: " + actual, actual == rows); } + private void waitForChange() + { + synchronized(this) + { + try + { + int count = 0; + while (!configChanged && count++ <= 3) + { + this.wait(5000); + } + } + catch (InterruptedException ie) + { + throw new IllegalStateException("wait timed out"); + } + finally + { + configChanged = false; + } + } + } + + public void configurationChanged(ConfigurationEvent event) + { + if (event.getType() == AbstractFileConfiguration.EVENT_CONFIG_CHANGED) + { + synchronized(this) + { + configChanged = true; + this.notify(); + } + } + } } \ No newline at end of file Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/reloading/TestVFSFileMonitorReloadingStrategy.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/reloading/TestVFSFileMonitorReloadingStrategy.java?rev=782704&r1=782703&r2=782704&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/reloading/TestVFSFileMonitorReloadingStrategy.java (original) +++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/reloading/TestVFSFileMonitorReloadingStrategy.java Mon Jun 8 17:09:24 2009 @@ -28,6 +28,9 @@ import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.configuration.FileSystem; import org.apache.commons.configuration.VFSFileSystem; +import org.apache.commons.configuration.AbstractFileConfiguration; +import org.apache.commons.configuration.event.ConfigurationEvent; +import org.apache.commons.configuration.event.ConfigurationListener; /** * Test case for the VFSFileMonitorReloadingStrategy class. @@ -36,9 +39,10 @@ * @version $Revision$ */ public class TestVFSFileMonitorReloadingStrategy extends TestCase + implements ConfigurationListener { - /** Constant for the name of a test properties file.*/ - private static final String TEST_FILE = "test.properties"; + /** true when a file is changed */ + private boolean configChanged = false; protected void setUp() throws Exception { @@ -106,6 +110,7 @@ PropertiesConfiguration config = new PropertiesConfiguration(); config.setFile(file); + config.addConfigurationListener(this); VFSFileMonitorReloadingStrategy strategy = new VFSFileMonitorReloadingStrategy(); strategy.setDelay(500); config.setReloadingStrategy(strategy); @@ -118,14 +123,20 @@ out.flush(); out.close(); - Thread.sleep(2000); + waitForChange(); // test the automatic reloading - assertEquals("Modified value with enabled reloading", "value1", config.getString("string")); - strategy.stopMonitor(); - if (file.exists()) + try { - file.delete(); + assertEquals("Modified value with enabled reloading", "value1", config.getString("string")); + } + finally + { + strategy.stopMonitor(); + if (file.exists()) + { + file.delete(); + } } } @@ -179,4 +190,39 @@ file.delete(); } } + + private void waitForChange() + { + synchronized(this) + { + try + { + int count = 0; + while (!configChanged && count++ <= 3) + { + this.wait(5000); + } + } + catch (InterruptedException ie) + { + throw new IllegalStateException("wait timed out"); + } + finally + { + configChanged = false; + } + } + } + + public void configurationChanged(ConfigurationEvent event) + { + if (event.getType() == AbstractFileConfiguration.EVENT_CONFIG_CHANGED) + { + synchronized(this) + { + configChanged = true; + this.notify(); + } + } + } } \ No newline at end of file