Author: oheger
Date: Mon May 13 20:26:32 2013
New Revision: 1482086
URL: http://svn.apache.org/r1482086
Log:
FileHandler now takes care for synchronization.
If the associated FileBased object implements the SynchronizerSupport
interface, the object is locked during load and save operations.
Modified:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/io/FileHandler.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/io/TestFileHandler.java
Modified:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/io/FileHandler.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/io/FileHandler.java?rev=1482086&r1=1482085&r2=1482086&view=diff
==============================================================================
---
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/io/FileHandler.java
(original)
+++
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/io/FileHandler.java
Mon May 13 20:26:32 2013
@@ -34,6 +34,10 @@ import java.util.concurrent.CopyOnWriteA
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConfigurationUtils;
import org.apache.commons.configuration.FileSystem;
+import org.apache.commons.configuration.sync.LockMode;
+import org.apache.commons.configuration.sync.NoOpSynchronizer;
+import org.apache.commons.configuration.sync.Synchronizer;
+import org.apache.commons.configuration.sync.SynchronizerSupport;
/**
* <p>
@@ -70,6 +74,22 @@ import org.apache.commons.configuration.
* location is not changed.
* </p>
* <p>
+ * When loading or saving a {@code FileBased} object some additional
functionality
+ * is performed if the object implements one of the following interfaces:
+ * <ul>
+ * <li>{@code FileLocatorAware}: In this case an object with the current file
+ * location is injected before the load or save operation is executed. This is
+ * useful for {@code FileBased} objects that depend on their current location,
+ * e.g. to resolve relative path names.</li>
+ * <li>{@code SynchronizerSupport}: If this interface is implemented, load and
+ * save operations obtain a write lock on the {@code FileBased} object before
+ * they access it. (In case of a save operation, a read lock would probably be
+ * sufficient, but because of the possible injection of a {@link FileLocator}
+ * object it is not allowed to perform multiple save operations in parallel;
+ * therefore, by obtaining a write lock, we are on the safe side.)</li>
+ * </ul>
+ * </p>
+ * <p>
* This class is thread-safe.
* </p>
*
@@ -83,6 +103,33 @@ public class FileHandler
/** Constant for the URI scheme for files with slashes. */
private static final String FILE_SCHEME_SLASH = FILE_SCHEME + "//";
+ /**
+ * A dummy implementation of {@code SynchronizerSupport}. This object is
+ * used when the file handler's content does not implement the
+ * {@code SynchronizerSupport} interface. All methods are just empty dummy
+ * implementations.
+ */
+ private static final SynchronizerSupport DUMMY_SYNC_SUPPORT =
+ new SynchronizerSupport()
+ {
+ public void unlock(LockMode mode)
+ {
+ }
+
+ public void setSynchronizer(Synchronizer sync)
+ {
+ }
+
+ public void lock(LockMode mode)
+ {
+ }
+
+ public Synchronizer getSynchronizer()
+ {
+ return NoOpSynchronizer.INSTANCE;
+ }
+ };
+
/** The file-based object managed by this handler. */
private final FileBased content;
@@ -713,6 +760,23 @@ public class FileHandler
}
/**
+ * Obtains a {@code SynchronizerSupport} for the current content. If the
+ * content implements this interface, it is returned. Otherwise, result is
a
+ * dummy object. This method is called before load and save operations. The
+ * returned object is used for synchronization.
+ *
+ * @return the {@code SynchronizerSupport} for synchronization
+ */
+ private SynchronizerSupport fetchSynchronizerSupport()
+ {
+ if (getContent() instanceof SynchronizerSupport)
+ {
+ return (SynchronizerSupport) getContent();
+ }
+ return DUMMY_SYNC_SUPPORT;
+ }
+
+ /**
* Internal helper method for loading the associated file from the location
* specified in the given {@code FileSpec}.
*
@@ -809,15 +873,24 @@ public class FileHandler
throws ConfigurationException
{
checkContent();
- injectFileLocator(url);
-
- if (getContent() instanceof InputStreamSupport)
+ SynchronizerSupport syncSupport = fetchSynchronizerSupport();
+ syncSupport.lock(LockMode.WRITE);
+ try
{
- loadFromStreamDirectly(in);
+ injectFileLocator(url);
+
+ if (getContent() instanceof InputStreamSupport)
+ {
+ loadFromStreamDirectly(in);
+ }
+ else
+ {
+ loadFromTransformedStream(in, encoding);
+ }
}
- else
+ finally
{
- loadFromTransformedStream(in, encoding);
+ syncSupport.unlock(LockMode.WRITE);
}
}
@@ -1039,29 +1112,38 @@ public class FileHandler
throws ConfigurationException
{
checkContent();
- injectFileLocator(url);
- Writer writer = null;
-
- if (encoding != null)
+ SynchronizerSupport syncSupport = fetchSynchronizerSupport();
+ syncSupport.lock(LockMode.WRITE);
+ try
{
- try
+ injectFileLocator(url);
+ Writer writer = null;
+
+ if (encoding != null)
{
- writer = new OutputStreamWriter(out, encoding);
+ try
+ {
+ writer = new OutputStreamWriter(out, encoding);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new ConfigurationException(
+ "The requested encoding is not supported, try the
default encoding.",
+ e);
+ }
}
- catch (UnsupportedEncodingException e)
+
+ if (writer == null)
{
- throw new ConfigurationException(
- "The requested encoding is not supported, try the
default encoding.",
- e);
+ writer = new OutputStreamWriter(out);
}
- }
- if (writer == null)
+ saveToWriter(writer);
+ }
+ finally
{
- writer = new OutputStreamWriter(out);
+ syncSupport.unlock(LockMode.WRITE);
}
-
- saveToWriter(writer);
}
/**
Modified:
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/io/TestFileHandler.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/io/TestFileHandler.java?rev=1482086&r1=1482085&r2=1482086&view=diff
==============================================================================
---
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/io/TestFileHandler.java
(original)
+++
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/io/TestFileHandler.java
Mon May 13 20:26:32 2013
@@ -42,6 +42,9 @@ import java.net.URL;
import org.apache.commons.configuration.ConfigurationAssert;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileSystem;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.SynchronizerTestImpl;
+import org.apache.commons.configuration.SynchronizerTestImpl.Methods;
import org.easymock.EasyMock;
import org.junit.Rule;
import org.junit.Test;
@@ -1178,6 +1181,37 @@ public class TestFileHandler
}
/**
+ * Tests whether a load() operation is correctly synchronized.
+ */
+ @Test
+ public void testLoadSynchronized() throws ConfigurationException
+ {
+ PropertiesConfiguration config = new PropertiesConfiguration();
+ SynchronizerTestImpl sync = new SynchronizerTestImpl();
+ config.setSynchronizer(sync);
+ FileHandler handler = new FileHandler(config);
+ handler.load(ConfigurationAssert.getTestFile("test.properties"));
+ sync.verifyStart(Methods.BEGIN_WRITE);
+ sync.verifyEnd(Methods.END_WRITE);
+ }
+
+ /**
+ * Tests whether a save() operation is correctly synchronized.
+ */
+ @Test
+ public void testSaveSynchronized() throws ConfigurationException,
IOException
+ {
+ PropertiesConfiguration config = new PropertiesConfiguration();
+ config.addProperty("test.synchronized", Boolean.TRUE);
+ SynchronizerTestImpl sync = new SynchronizerTestImpl();
+ config.setSynchronizer(sync);
+ FileHandler handler = new FileHandler(config);
+ File f = folder.newFile();
+ handler.save(f);
+ sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+ }
+
+ /**
* An implementation of the FileBased interface used for test purposes.
*/
private static class FileBasedTestImpl implements FileBased