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
The following commit(s) were added to refs/heads/master by this push:
new a4a6e7b0 Implement proper concurrency in ConstantLookup
a4a6e7b0 is described below
commit a4a6e7b015c503218874ad86b2696470b529bfb8
Author: Gary Gregory <[email protected]>
AuthorDate: Tue May 24 08:00:40 2022 -0400
Implement proper concurrency in ConstantLookup
---
src/changes/changes.xml | 3 +
.../configuration2/interpol/ConstantLookup.java | 67 ++++++++--------------
2 files changed, 26 insertions(+), 44 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f928b9bf..560e1104 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -65,6 +65,9 @@
<action issue="CONFIGURATION-764" type="fix" dev="ggregory"
due-to="Ning Zhang, Matt Juntunen, Bruno P. Kinoshita, Gary Gregory">
Single Variable Interpolation #182.
</action>
+ <action type="fix" dev="ggregory" due-to="Gary Gregory">
+ Implement proper concurrency in ConstantLookup.
+ </action>
<!-- ADD -->
<action type="add" dev="ggregory" due-to="SethiPandi">
Implement Iterable in ImmutableNode #74.
diff --git
a/src/main/java/org/apache/commons/configuration2/interpol/ConstantLookup.java
b/src/main/java/org/apache/commons/configuration2/interpol/ConstantLookup.java
index fd4e1cd5..7351d289 100644
---
a/src/main/java/org/apache/commons/configuration2/interpol/ConstantLookup.java
+++
b/src/main/java/org/apache/commons/configuration2/interpol/ConstantLookup.java
@@ -16,8 +16,8 @@
*/
package org.apache.commons.configuration2.interpol;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.logging.Log;
@@ -25,14 +25,13 @@ import org.apache.commons.logging.LogFactory;
/**
* <p>
- * A specialized lookup implementation that allows access to constant fields
of classes.
+ * Looks up constant fields in classes.
* </p>
* <p>
- * Sometimes it is necessary in a configuration file to refer to a constant
defined in a class. This can be done with
- * this lookup implementation. Variable names passed in must be of the form
{@code mypackage.MyClass.FIELD}. The
- * {@code lookup()} method will split the passed in string at the last dot,
separating the fully qualified class name
- * and the name of the constant (i.e. <strong>static final</strong>) member
field. Then the class is loaded and the
- * field's value is obtained using reflection.
+ * Variable names passed in must be of the form {@code
mypackage.MyClass.FIELD}. The {@code lookup()} method will split
+ * the passed in string at the last dot, separating the fully qualified class
name and the name of the constant (i.e.
+ * <strong>static final</strong>) member field. Then the class is loaded and
the field's value is obtained using
+ * reflection.
* </p>
* <p>
* Once retrieved values are cached for fast access. This class is
thread-safe. It can be used as a standard (i.e.
@@ -42,20 +41,20 @@ import org.apache.commons.logging.LogFactory;
* @since 1.4
*/
public class ConstantLookup implements Lookup {
+
/** Constant for the field separator. */
private static final char FIELD_SEPRATOR = '.';
- /** An internally used cache for already retrieved values. */
- private static final Map<String, Object> CONSTANT_CACHE = new HashMap<>();
+ /** Cache of field values. */
+ private static final Map<String, Object> CACHE = new ConcurrentHashMap<>();
/** The logger. */
private final Log log = LogFactory.getLog(getClass());
/**
- * Tries to resolve the specified variable. The passed in variable name is
interpreted as the name of a <b>static
- * final</b> member field of a class. If the value has already been
obtained, it can be retrieved from an internal
- * cache. Otherwise this method will invoke the {@code resolveField()}
method and pass in the name of the class and the
- * field.
+ * Looks up a variable. The passed in variable name is interpreted as the
name of a <b>static final</b> member field of
+ * a class. If the value has already been obtained, it can be retrieved
from an internal cache. Otherwise this method
+ * will invoke the {@code resolveField()} method and pass in the name of
the class and the field.
*
* @param var the name of the variable to be resolved
* @return the value of this variable or <b>null</b> if it cannot be
resolved
@@ -65,50 +64,30 @@ public class ConstantLookup implements Lookup {
if (var == null) {
return null;
}
-
- Object result;
- synchronized (CONSTANT_CACHE) {
- result = CONSTANT_CACHE.get(var);
- }
- if (result != null) {
- return result;
- }
-
- final int fieldPos = var.lastIndexOf(FIELD_SEPRATOR);
- if (fieldPos < 0) {
- return null;
- }
- try {
- final Object value = resolveField(var.substring(0, fieldPos),
var.substring(fieldPos + 1));
- if (value != null) {
- synchronized (CONSTANT_CACHE) {
- // In worst case, the value will be fetched multiple times
- // because of this lax synchronization, but for constant
- // values this shouldn't be a problem.
- CONSTANT_CACHE.put(var, value);
+ return CACHE.computeIfAbsent(var, k -> {
+ final int fieldPos = var.lastIndexOf(FIELD_SEPRATOR);
+ if (fieldPos >= 0) {
+ try {
+ return resolveField(var.substring(0, fieldPos),
var.substring(fieldPos + 1));
+ } catch (final Exception ex) {
+ log.warn("Could not obtain value for variable " + var, ex);
}
- result = value;
}
- } catch (final Exception ex) {
- log.warn("Could not obtain value for variable " + var, ex);
- }
-
- return result;
+ return null;
+ });
}
/**
* Clears the shared cache with the so far resolved constants.
*/
public static void clear() {
- synchronized (CONSTANT_CACHE) {
- CONSTANT_CACHE.clear();
- }
+ CACHE.clear();
}
/**
* Determines the value of the specified constant member field of a class.
This implementation will call
* {@code fetchClass()} to obtain the {@code java.lang.Class} object for
the target class. Then it will use reflection
- * to obtain the field's value. For this to work the field must be
accessable.
+ * to obtain the field's value. For this to work the field must be
accessible.
*
* @param className the name of the class
* @param fieldName the name of the member field of that class to read