This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-configuration.git

commit 688f77d5d9fad1ab591caa799789979d8eb63536
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Tue Jul 2 19:01:24 2024 -0400

    Reduce internal code duplication
---
 src/conf/checkstyle-suppressions.xml               |   2 +
 .../configuration2/AbstractConfiguration.java      | 146 ++++++++++-----------
 .../AbstractHierarchicalConfiguration.java         |  66 +++-------
 .../BaseHierarchicalConfiguration.java             |  48 ++-----
 .../configuration2/CompositeConfiguration.java     |  45 ++-----
 .../commons/configuration2/INIConfiguration.java   |  62 +++------
 .../configuration2/PropertiesConfiguration.java    |  28 +---
 .../commons/configuration2/XMLConfiguration.java   |  54 +++-----
 .../configuration2/TestCombinedConfiguration.java  |   2 +
 9 files changed, 144 insertions(+), 309 deletions(-)

diff --git a/src/conf/checkstyle-suppressions.xml 
b/src/conf/checkstyle-suppressions.xml
index fd47dcf0..d096b185 100644
--- a/src/conf/checkstyle-suppressions.xml
+++ b/src/conf/checkstyle-suppressions.xml
@@ -46,4 +46,6 @@
   <suppress checks="SuperClone" 
files="src[/\\]test[/\\]java[/\\]org[/\\]apache[/\\]commons[/\\]configuration2[/\\]TestConfigurationUtils.java"
 />
   <!-- Class can't be final for Mockito -->
   <suppress checks="FinalClass" 
files="src[/\\]test[/\\]java[/\\]org[/\\]apache[/\\]commons[/\\]configuration2[/\\]io[/\\]TestFileHandler.java"
 />
+  <!-- False positive -->
+  <suppress checks="SuperClone" files="AbstractHierarchicalConfiguration.java" 
lines="412"/>
 </suppressions>
diff --git 
a/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java
index c91953cc..0b078eb7 100644
--- a/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java
@@ -53,6 +53,8 @@ import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.function.FailableRunnable;
+import org.apache.commons.lang3.function.FailableSupplier;
 
 /**
  * <p>
@@ -226,14 +228,11 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
 
     @Override
     public final void addProperty(final String key, final Object value) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, true);
             addPropertyInternal(key, value);
             fireEvent(ConfigurationEvent.ADD_PROPERTY, key, value, false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -265,16 +264,16 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      * structure (i.e. the parent-child-relationships will get lost). So when 
dealing with hierarchical configuration
      * objects their {@link BaseHierarchicalConfiguration#clone() clone()} 
methods should be used.
      *
-     * @param c the configuration to be appended (can be <b>null</b>, then 
this operation will have no effect)
+     * @param configuration the configuration to be appended (can be 
<b>null</b>, then this operation will have no effect)
      * @since 1.5
      */
-    public void append(final Configuration c) {
-        if (c != null) {
-            c.lock(LockMode.READ);
+    public void append(final Configuration configuration) {
+        if (configuration != null) {
+            configuration.lock(LockMode.READ);
             try {
-                c.getKeys().forEachRemaining(key -> addProperty(key, 
encodeForCopy(c.getProperty(key))));
+                configuration.getKeys().forEachRemaining(key -> 
addProperty(key, encodeForCopy(configuration.getProperty(key))));
             } finally {
-                c.unlock(LockMode.READ);
+                configuration.unlock(LockMode.READ);
             }
         }
     }
@@ -314,14 +313,11 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
 
     @Override
     public final void clear() {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.CLEAR, null, null, true);
             clearInternal();
             fireEvent(ConfigurationEvent.CLEAR, null, null, false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -368,14 +364,11 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final void clearProperty(final String key) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, true);
             clearPropertyDirect(key);
             fireEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -433,12 +426,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final boolean containsKey(final String key) {
-        beginRead(false);
-        try {
-            return containsKeyInternal(key);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> containsKeyInternal(key), false);
     }
 
     /**
@@ -457,12 +445,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final boolean containsValue(final Object value) {
-        beginRead(false);
-        try {
-            return containsValueInternal(value);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> containsValueInternal(value), false);
     }
 
     /**
@@ -535,16 +518,16 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      * hierarchical configuration objects their {@link 
BaseHierarchicalConfiguration#clone() clone()} methods should be
      * used.
      *
-     * @param c the configuration to copy (can be <b>null</b>, then this 
operation will have no effect)
+     * @param configuration the configuration to copy (can be <b>null</b>, 
then this operation will have no effect)
      * @since 1.5
      */
-    public void copy(final Configuration c) {
-        if (c != null) {
-            c.lock(LockMode.READ);
+    public void copy(final Configuration configuration) {
+        if (configuration != null) {
+            configuration.lock(LockMode.READ);
             try {
-                c.getKeys().forEachRemaining(key -> setProperty(key, 
encodeForCopy(c.getProperty(key))));
+                configuration.getKeys().forEachRemaining(key -> 
setProperty(key, encodeForCopy(configuration.getProperty(key))));
             } finally {
-                c.unlock(LockMode.READ);
+                configuration.unlock(LockMode.READ);
             }
         }
     }
@@ -885,12 +868,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final Iterator<String> getKeys() {
-        beginRead(false);
-        try {
-            return getKeysInternal();
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getKeysInternal(), false);
     }
 
     /**
@@ -900,12 +878,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final Iterator<String> getKeys(final String prefix) {
-        beginRead(false);
-        try {
-            return getKeysInternal(prefix);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getKeysInternal(prefix), false);
     }
 
     /**
@@ -915,12 +888,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final Iterator<String> getKeys(final String prefix, final String 
delimiter) {
-        beginRead(false);
-        try {
-            return getKeysInternal(prefix, delimiter);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getKeysInternal(prefix, delimiter), false);
     }
 
     /**
@@ -1104,12 +1072,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final Object getProperty(final String key) {
-        beginRead(false);
-        try {
-            return getPropertyInternal(key);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getPropertyInternal(key), false);
     }
 
     /**
@@ -1275,12 +1238,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final boolean isEmpty() {
-        beginRead(false);
-        try {
-            return isEmptyInternal();
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> isEmptyInternal(), false);
     }
 
     /**
@@ -1488,14 +1446,11 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
 
     @Override
     public final void setProperty(final String key, final Object value) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, true);
             setPropertyInternal(key, value);
             fireEvent(ConfigurationEvent.SET_PROPERTY, key, value, false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -1548,12 +1503,7 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
      */
     @Override
     public final int size() {
-        beginRead(false);
-        try {
-            return sizeInternal();
-        } finally {
-            endRead();
-        }
+        return syncRead(this::sizeInternal, false);
     }
 
     /**
@@ -1576,6 +1526,42 @@ public abstract class AbstractConfiguration extends 
BaseEventSource implements C
         return new SubsetConfiguration(this, prefix, DELIMITER);
     }
 
+    void syncRead(final Runnable runnable, final boolean optimize) {
+        beginRead(optimize);
+        try {
+            runnable.run();
+        } finally {
+            endRead();
+        }
+    }
+
+    <T, E extends Throwable> T syncRead(final FailableSupplier<T, E> supplier, 
final boolean optimize) throws E {
+        beginRead(optimize);
+        try {
+            return supplier.get();
+        } finally {
+            endRead();
+        }
+    }
+
+    <T> T syncReadValue(final T value, final boolean optimize) {
+        beginRead(optimize);
+        try {
+            return value;
+        } finally {
+            endRead();
+        }
+    }
+
+    <E extends Throwable> void syncWrite(final FailableRunnable<E> runnable, 
final boolean optimize) throws E {
+        beginWrite(optimize);
+        try {
+            runnable.run();
+        } finally {
+            endWrite();
+        }
+    }
+
     /**
      * {@inheritDoc} This implementation delegates to {@code endRead()} or 
{@code endWrite()}, depending on the
      * {@code LockMode} argument. Subclasses can override these protected 
methods to perform additional steps when a
diff --git 
a/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
 
b/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
index b7b2fd04..84531d9b 100644
--- 
a/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
@@ -299,15 +299,11 @@ public abstract class 
AbstractHierarchicalConfiguration<T> extends AbstractConfi
         if (nodes == null || nodes.isEmpty()) {
             return;
         }
-
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, true);
             addNodesInternal(key, nodes);
             fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -384,13 +380,10 @@ public abstract class 
AbstractHierarchicalConfiguration<T> extends AbstractConfi
      */
     @Override
     public final void clearTree(final String key) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             fireEvent(ConfigurationEvent.CLEAR_TREE, key, null, true);
             fireEvent(ConfigurationEvent.CLEAR_TREE, key, 
clearTreeInternal(key), false);
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -414,24 +407,22 @@ public abstract class 
AbstractHierarchicalConfiguration<T> extends AbstractConfi
      * @return the copy
      * @since 1.2
      */
+    @SuppressWarnings("unchecked")
     @Override
     public Object clone() {
-        beginRead(false);
-        try {
-            @SuppressWarnings("unchecked") // clone returns the same type
-            final AbstractHierarchicalConfiguration<T> copy = 
(AbstractHierarchicalConfiguration<T>) super.clone();
-            copy.setSynchronizer(NoOpSynchronizer.INSTANCE);
-            copy.cloneInterpolator(this);
-            
copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer()));
-            copy.nodeModel = cloneNodeModel();
-
-            return copy;
-        } catch (final CloneNotSupportedException cex) {
-            // should not happen
-            throw new ConfigurationRuntimeException(cex);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> {
+            try {
+                AbstractHierarchicalConfiguration<T> copy = 
(AbstractHierarchicalConfiguration<T>) 
AbstractHierarchicalConfiguration.super.clone();
+                copy.setSynchronizer(NoOpSynchronizer.INSTANCE);
+                copy.cloneInterpolator(this);
+                
copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer()));
+                copy.nodeModel = cloneNodeModel();
+                return copy;
+            } catch (final CloneNotSupportedException cex) {
+                // should not happen
+                throw new ConfigurationRuntimeException(cex);
+            }
+        }, false);
     }
 
     /**
@@ -553,12 +544,7 @@ public abstract class AbstractHierarchicalConfiguration<T> 
extends AbstractConfi
      */
     @Override
     public final int getMaxIndex(final String key) {
-        beginRead(false);
-        try {
-            return getMaxIndexInternal(key);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getMaxIndexInternal(key), false);
     }
 
     /**
@@ -590,12 +576,7 @@ public abstract class AbstractHierarchicalConfiguration<T> 
extends AbstractConfi
      */
     @Override
     public NodeModel<T> getNodeModel() {
-        beginRead(false);
-        try {
-            return getModel();
-        } finally {
-            endRead();
-        }
+        return syncRead(this::getModel, false);
     }
 
     /**
@@ -625,12 +606,7 @@ public abstract class AbstractHierarchicalConfiguration<T> 
extends AbstractConfi
      */
     @Override
     public final String getRootElementName() {
-        beginRead(false);
-        try {
-            return getRootElementNameInternal();
-        } finally {
-            endRead();
-        }
+        return syncRead(this::getRootElementNameInternal, false);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/configuration2/BaseHierarchicalConfiguration.java
 
b/src/main/java/org/apache/commons/configuration2/BaseHierarchicalConfiguration.java
index 281fd293..941991b8 100644
--- 
a/src/main/java/org/apache/commons/configuration2/BaseHierarchicalConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/BaseHierarchicalConfiguration.java
@@ -386,18 +386,10 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
      */
     @Override
     public List<HierarchicalConfiguration<ImmutableNode>> 
childConfigurationsAt(final String key) {
-        List<ImmutableNode> nodes;
-        beginRead(false);
-        try {
-            nodes = fetchFilteredNodeResults(key);
-        } finally {
-            endRead();
-        }
-
+        List<ImmutableNode> nodes = syncRead(() -> 
fetchFilteredNodeResults(key), false);
         if (nodes.size() != 1) {
             return Collections.emptyList();
         }
-
         return 
nodes.get(0).stream().map(this::createIndependentSubConfigurationForNode).collect(Collectors.toList());
     }
 
@@ -450,12 +442,7 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
      */
     @Override
     public HierarchicalConfiguration<ImmutableNode> configurationAt(final 
String key, final boolean supportUpdates) {
-        beginRead(false);
-        try {
-            return supportUpdates ? createConnectedSubConfiguration(key) : 
createIndependentSubConfiguration(key);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> supportUpdates ? 
createConnectedSubConfiguration(key) : createIndependentSubConfiguration(key), 
false);
     }
 
     /**
@@ -464,13 +451,7 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
      */
     @Override
     public List<HierarchicalConfiguration<ImmutableNode>> 
configurationsAt(final String key) {
-        List<ImmutableNode> nodes;
-        beginRead(false);
-        try {
-            nodes = fetchFilteredNodeResults(key);
-        } finally {
-            endRead();
-        }
+        List<ImmutableNode> nodes = syncRead(() -> 
fetchFilteredNodeResults(key), false);
         return 
nodes.stream().map(this::createIndependentSubConfigurationForNode).collect(Collectors.toList());
     }
 
@@ -483,17 +464,8 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
         if (!supportUpdates) {
             return configurationsAt(key);
         }
-
-        InMemoryNodeModel parentModel;
-        beginRead(false);
-        try {
-            parentModel = getSubConfigurationParentModel();
-        } finally {
-            endRead();
-        }
-
-        final Collection<NodeSelector> selectors = 
parentModel.selectAndTrackNodes(key, this);
-        return createConnectedSubConfigurations(this, selectors);
+        final InMemoryNodeModel parentModel = 
syncRead(this::getSubConfigurationParentModel, false);
+        return createConnectedSubConfigurations(this, 
parentModel.selectAndTrackNodes(key, this));
     }
 
     /**
@@ -764,15 +736,14 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
      */
     @Override
     public Configuration subset(final String prefix) {
-        beginRead(false);
-        try {
+        return syncRead(() -> {
             final List<QueryResult<ImmutableNode>> results = 
fetchNodeList(prefix);
             if (results.isEmpty()) {
                 return new BaseHierarchicalConfiguration();
             }
-
             final BaseHierarchicalConfiguration parent = this;
             final BaseHierarchicalConfiguration result = new 
BaseHierarchicalConfiguration() {
+
                 @Override
                 public ConfigurationInterpolator getInterpolator() {
                     return parent.getInterpolator();
@@ -785,14 +756,11 @@ public class BaseHierarchicalConfiguration extends 
AbstractHierarchicalConfigura
                 }
             };
             result.getModel().setRootNode(createSubsetRootNode(results));
-
             if (result.isEmpty()) {
                 return new BaseHierarchicalConfiguration();
             }
             result.setSynchronizer(getSynchronizer());
             return result;
-        } finally {
-            endRead();
-        }
+        }, false);
     }
 }
diff --git 
a/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
index 5c8f10c9..66cb9748 100644
--- 
a/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
@@ -142,14 +142,12 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @since 1.8
      */
     public void addConfiguration(final Configuration config, final boolean 
asInMemory) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             if (!configList.contains(config)) {
                 if (asInMemory) {
                     replaceInMemoryConfiguration(config);
                     inMemoryConfigIsChild = true;
                 }
-
                 if (!inMemoryConfigIsChild) {
                     // As the inMemoryConfiguration contains all manually added
                     // keys, we must make sure that it is always last. 
"Normal", non
@@ -161,14 +159,11 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
                     // only the order in which child configurations are added 
is relevant
                     configList.add(config);
                 }
-
                 if (config instanceof AbstractConfiguration) {
                     ((AbstractConfiguration) 
config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
                 }
             }
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -194,22 +189,18 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @since 2.3
      */
     public void addConfigurationFirst(final Configuration config, final 
boolean asInMemory) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             if (!configList.contains(config)) {
                 if (asInMemory) {
                     replaceInMemoryConfiguration(config);
                     inMemoryConfigIsChild = true;
                 }
                 configList.add(0, config);
-
                 if (config instanceof AbstractConfiguration) {
                     ((AbstractConfiguration) 
config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
                 }
             }
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
@@ -315,12 +306,7 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @return the configuration at this index
      */
     public Configuration getConfiguration(final int index) {
-        beginRead(false);
-        try {
-            return configList.get(index);
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> configList.get(index), false);
     }
 
     /**
@@ -329,12 +315,7 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @return the in memory configuration
      */
     public Configuration getInMemoryConfiguration() {
-        beginRead(false);
-        try {
-            return inMemoryConfiguration;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(inMemoryConfiguration, false);
     }
 
     @Override
@@ -395,12 +376,7 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @return the number of configuration
      */
     public int getNumberOfConfigurations() {
-        beginRead(false);
-        try {
-            return configList.size();
-        } finally {
-            endRead();
-        }
+        return syncRead(configList::size, false);
     }
 
     /**
@@ -474,16 +450,13 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Clo
      * @param config The configuration to remove
      */
     public void removeConfiguration(final Configuration config) {
-        beginWrite(false);
-        try {
+        syncWrite(() -> {
             // Make sure that you can't remove the inMemoryConfiguration from
             // the CompositeConfiguration object
             if (!config.equals(inMemoryConfiguration)) {
                 configList.remove(config);
             }
-        } finally {
-            endWrite();
-        }
+        }, false);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/configuration2/INIConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/INIConfiguration.java
index 62198516..6d2f7d1c 100644
--- a/src/main/java/org/apache/commons/configuration2/INIConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/INIConfiguration.java
@@ -602,12 +602,7 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 2.5
      */
     public String getCommentLeadingCharsUsedInInput() {
-        beginRead(false);
-        try {
-            return commentCharsUsedInInput;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(commentCharsUsedInInput, false);
     }
 
     /**
@@ -660,18 +655,16 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
     }
 
     /**
-     * Gets a set containing the sections in this ini configuration. Note that 
changes to this set do not affect the
+     * Gets a set containing the sections in this INI configuration. Note that 
changes to this set do not affect the
      * configuration.
      *
      * @return a set containing the sections.
      */
     public Set<String> getSections() {
-        final Set<String> sections = new LinkedHashSet<>();
-        boolean globalSection = false;
-        boolean inSection = false;
-
-        beginRead(false);
-        try {
+        return syncRead(() -> {
+            final Set<String> sections = new LinkedHashSet<>();
+            boolean globalSection = false;
+            boolean inSection = false;
             for (final ImmutableNode node : 
getModel().getNodeHandler().getRootNode().getChildren()) {
                 if (isSectionNode(node)) {
                     inSection = true;
@@ -681,11 +674,8 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
                     sections.add(null);
                 }
             }
-        } finally {
-            endRead();
-        }
-
-        return sections;
+            return sections;
+        }, false);
     }
 
     /**
@@ -695,12 +685,7 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 2.5
      */
     public String getSeparatorUsedInInput() {
-        beginRead(false);
-        try {
-            return separatorUsedInInput;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(separatorUsedInInput, false);
     }
 
     /**
@@ -710,12 +695,7 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 2.2
      */
     public String getSeparatorUsedInOutput() {
-        beginRead(false);
-        try {
-            return separatorUsedInOutput;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(separatorUsedInOutput, false);
     }
 
     /**
@@ -923,12 +903,7 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 2.2
      */
     public void setSeparatorUsedInOutput(final String separator) {
-        beginWrite(false);
-        try {
-            this.separatorUsedInOutput = separator;
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> this.separatorUsedInOutput = separator, false);
     }
 
     /**
@@ -940,12 +915,10 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
      */
     @Override
     public void write(final Writer writer) throws ConfigurationException, 
IOException {
-        final PrintWriter out = new PrintWriter(writer);
-        boolean first = true;
-        final String separator = getSeparatorUsedInOutput();
-
-        beginRead(false);
-        try {
+        syncRead(() -> {
+            final PrintWriter out = new PrintWriter(writer);
+            boolean first = true;
+            final String separator = getSeparatorUsedInOutput();
             for (final ImmutableNode node : 
getModel().getNodeHandler().getRootNode().getChildren()) {
                 if (isSectionNode(node)) {
                     if (!first) {
@@ -964,9 +937,8 @@ public class INIConfiguration extends 
BaseHierarchicalConfiguration implements F
             }
             out.println();
             out.flush();
-        } finally {
-            endRead();
-        }
+
+        }, false);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
index 5ee9d653..7d8a0491 100644
--- 
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
@@ -1286,12 +1286,7 @@ public class PropertiesConfiguration extends 
BaseConfiguration implements FileBa
      * @since 2.0
      */
     public String getFooter() {
-        beginRead(false);
-        try {
-            return getLayout().getFooterComment();
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getLayout().getFooterComment(), false);
     }
 
     /**
@@ -1301,12 +1296,7 @@ public class PropertiesConfiguration extends 
BaseConfiguration implements FileBa
      * @since 1.1
      */
     public String getHeader() {
-        beginRead(false);
-        try {
-            return getLayout().getHeaderComment();
-        } finally {
-            endRead();
-        }
+        return syncRead(() -> getLayout().getHeaderComment(), false);
     }
 
     /**
@@ -1507,12 +1497,7 @@ public class PropertiesConfiguration extends 
BaseConfiguration implements FileBa
      * @since 2.0
      */
     public void setFooter(final String footer) {
-        beginWrite(false);
-        try {
-            getLayout().setFooterComment(footer);
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> getLayout().setFooterComment(footer), false);
     }
 
     /**
@@ -1522,12 +1507,7 @@ public class PropertiesConfiguration extends 
BaseConfiguration implements FileBa
      * @since 1.1
      */
     public void setHeader(final String header) {
-        beginWrite(false);
-        try {
-            getLayout().setHeaderComment(header);
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> getLayout().setHeaderComment(header), false);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
index ed465733..4f0d7f3c 100644
--- a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
@@ -798,12 +798,7 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 1.3
      */
     public String getPublicID() {
-        beginRead(false);
-        try {
-            return publicID;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(publicID, false);
     }
 
     /**
@@ -839,12 +834,7 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 1.3
      */
     public String getSystemID() {
-        beginRead(false);
-        try {
-            return systemID;
-        } finally {
-            endRead();
-        }
+        return syncReadValue(systemID, false);
     }
 
     /**
@@ -1006,12 +996,7 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 1.3
      */
     public void setPublicID(final String publicID) {
-        beginWrite(false);
-        try {
-            this.publicID = publicID;
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> this.publicID = publicID, false);
     }
 
     /**
@@ -1058,12 +1043,7 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @since 1.3
      */
     public void setSystemID(final String systemID) {
-        beginWrite(false);
-        try {
-            this.systemID = systemID;
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> this.systemID = systemID, false);
     }
 
     /**
@@ -1085,21 +1065,17 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @throws ConfigurationException if the validation fails.
      */
     public void validate() throws ConfigurationException {
-        beginWrite(false);
-        try {
-            final Transformer transformer = createTransformer();
-            final Source source = new DOMSource(createDocument());
-            final StringWriter writer = new StringWriter();
-            final Result result = new StreamResult(writer);
-            XMLDocumentHelper.transform(transformer, source, result);
-            final Reader reader = new 
StringReader(writer.getBuffer().toString());
-            final DocumentBuilder builder = createDocumentBuilder();
-            builder.parse(new InputSource(reader));
-        } catch (final SAXException | IOException | 
ParserConfigurationException pce) {
-            throw new ConfigurationException("Validation failed", pce);
-        } finally {
-            endWrite();
-        }
+        syncWrite(() -> {
+            try {
+                final StringWriter writer = new StringWriter();
+                final Result result = new StreamResult(writer);
+                XMLDocumentHelper.transform(createTransformer(), new 
DOMSource(createDocument()), result);
+                final Reader reader = new 
StringReader(writer.getBuffer().toString());
+                createDocumentBuilder().parse(new InputSource(reader));
+            } catch (final SAXException | IOException | 
ParserConfigurationException pce) {
+                throw new ConfigurationException("Validation failed", pce);
+            }
+        }, false);
     }
 
     /**
diff --git 
a/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
 
b/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
index 876aba39..3489e39d 100644
--- 
a/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
+++ 
b/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
@@ -766,9 +766,11 @@ public class TestCombinedConfiguration {
         final BaseConfiguration conf1 = new BaseConfiguration();
         final String key = "x1";
         conf1.addProperty(key, 1);
+        assertEquals(1, conf1.getProperty(key));
 
         final CombinedConfiguration conf2 = new CombinedConfiguration();
         conf2.addConfiguration(conf1, null, "");
+        assertEquals(conf1, conf2.getConfiguration(0));
 
         // Actual test
         final Iterator<String> keys = conf2.getKeys();


Reply via email to