This is an automated email from the ASF dual-hosted git repository.
adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new c935f17339 HDDS-8668. Allow reconfiguration of annotated config object
(#4774)
c935f17339 is described below
commit c935f173390f52cb749d8fb421cf849de578b651
Author: Doroszlai, Attila <[email protected]>
AuthorDate: Tue Jun 6 08:42:45 2023 +0200
HDDS-8668. Allow reconfiguration of annotated config object (#4774)
---
hadoop-hdds/config/pom.xml | 14 ++
.../java/org/apache/hadoop/hdds/conf/Config.java | 5 +
.../org/apache/hadoop/hdds/conf/ConfigType.java | 83 ++++++++-
.../hdds/conf/ConfigurationReflectionUtil.java | 189 ++++++++++++---------
.../hadoop/hdds/conf/ConfigurationSource.java | 12 +-
.../hadoop/hdds/conf/ReconfigurableConfig.java} | 27 +--
.../hadoop/hdds/conf/ConfigurationExample.java | 18 ++
.../hdds/conf/ConfigurationExampleGrandParent.java | 6 +-
.../hdds/conf/TestConfigurationReflectionUtil.java | 145 +++++++---------
.../hadoop/hdds/conf/TestConfigurationSource.java | 49 ++++--
...ndParent.java => TestReconfigurableConfig.java} | 24 +--
.../config/src/test/resources/log4j.properties | 23 +++
12 files changed, 389 insertions(+), 206 deletions(-)
diff --git a/hadoop-hdds/config/pom.xml b/hadoop-hdds/config/pom.xml
index 52837149f8..ac1fb8ba30 100644
--- a/hadoop-hdds/config/pom.xml
+++ b/hadoop-hdds/config/pom.xml
@@ -37,6 +37,16 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>ch.qos.reload4j</groupId>
+ <artifactId>reload4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-reload4j</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.ozone</groupId>
<artifactId>hdds-test-utils</artifactId>
@@ -49,6 +59,10 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/Config.java
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/Config.java
index 5d4b4774a5..19018e6629 100644
--- a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/Config.java
+++ b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/Config.java
@@ -61,4 +61,9 @@ public @interface Config {
StorageUnit sizeUnit() default StorageUnit.BYTES;
ConfigTag[] tags();
+
+ /**
+ * Indicates whether this property can be dynamically reconfigured at
runtime.
+ */
+ boolean reconfigurable() default false;
}
diff --git
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigType.java
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigType.java
index 7dd38ac08a..96c6f0ed84 100644
---
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigType.java
+++
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigType.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.hdds.conf;
+import static
org.apache.hadoop.hdds.conf.TimeDurationUtil.getTimeDurationHelper;
+
/**
* Possible type of injected configuration.
* <p>
@@ -24,13 +26,76 @@ package org.apache.hadoop.hdds.conf;
* the configuration field.
*/
public enum ConfigType {
- AUTO,
- STRING,
- BOOLEAN,
- INT,
- LONG,
- TIME,
- SIZE,
- CLASS,
- DOUBLE
+ AUTO {
+ @Override
+ Object parse(String value, Config config, Class<?> type, String key) {
+ throw new UnsupportedOperationException();
+ }
+
+ },
+ STRING {
+ @Override
+ String parse(String value, Config config, Class<?> type, String key) {
+ return value;
+ }
+
+ },
+ BOOLEAN {
+ @Override
+ Boolean parse(String value, Config config, Class<?> type, String key) {
+ return Boolean.parseBoolean(value);
+ }
+
+ },
+ INT {
+ @Override
+ Integer parse(String value, Config config, Class<?> type, String key) {
+ return Integer.parseInt(value);
+ }
+
+ },
+ LONG {
+ @Override
+ Long parse(String value, Config config, Class<?> type, String key) {
+ return Long.parseLong(value);
+ }
+
+ },
+ TIME {
+ @Override
+ Long parse(String value, Config config, Class<?> type, String key) {
+ return getTimeDurationHelper(key, value, config.timeUnit());
+ }
+
+ },
+ SIZE {
+ @Override
+ Object parse(String value, Config config, Class<?> type, String key) {
+ StorageSize measure = StorageSize.parse(value);
+ long val = Math.round(measure.getUnit().toBytes(measure.getValue()));
+ if (type == int.class) {
+ return (int) val;
+ }
+ return val;
+ }
+
+ },
+ CLASS {
+ @Override
+ Class<?> parse(String value, Config config, Class<?> type, String key)
+ throws ClassNotFoundException {
+ return Class.forName(value);
+ }
+
+ },
+ DOUBLE {
+ @Override
+ Double parse(String value, Config config, Class<?> type, String key) {
+ return Double.parseDouble(value);
+ }
+
+ };
+
+ abstract Object parse(String value, Config config, Class<?> type, String key)
+ throws Exception;
}
diff --git
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationReflectionUtil.java
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationReflectionUtil.java
index 189d97207c..96e5546da2 100644
---
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationReflectionUtil.java
+++
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationReflectionUtil.java
@@ -22,7 +22,9 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Deque;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
@@ -34,99 +36,101 @@ public final class ConfigurationReflectionUtil {
private ConfigurationReflectionUtil() {
}
+ public static <T> Map<String, Field> mapReconfigurableProperties(
+ Class<T> configurationClass) {
+ Optional<String> prefix = getPrefix(configurationClass);
+ Map<String, Field> props =
+ mapReconfigurableProperties(configurationClass, prefix);
+ Class<? super T> superClass = configurationClass.getSuperclass();
+ while (superClass != null) {
+ props.putAll(mapReconfigurableProperties(superClass, prefix));
+ superClass = superClass.getSuperclass();
+ }
+ return props;
+ }
+
+ private static <T> Map<String, Field> mapReconfigurableProperties(
+ Class<T> configurationClass, Optional<String> prefix) {
+ Map<String, Field> props = new HashMap<>();
+ for (Field field : configurationClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(Config.class)) {
+ Config configAnnotation = field.getAnnotation(Config.class);
+
+ if (configAnnotation.reconfigurable()) {
+ checkNotFinal(configurationClass, field);
+ props.put(getFullKey(prefix, configAnnotation), field);
+ }
+ }
+ }
+ return props;
+ }
+
public static <T> void injectConfiguration(
ConfigurationSource configuration,
Class<T> configurationClass,
- T configObject, String prefix) {
+ T configObject, String prefix, boolean reconfiguration) {
injectConfigurationToObject(configuration, configurationClass,
configObject,
- prefix);
+ prefix, reconfiguration);
Class<? super T> superClass = configurationClass.getSuperclass();
while (superClass != null) {
injectConfigurationToObject(configuration, superClass, configObject,
- prefix);
+ prefix, reconfiguration);
superClass = superClass.getSuperclass();
}
}
- public static <T> void injectConfigurationToObject(ConfigurationSource from,
+ private static <T> void injectConfigurationToObject(ConfigurationSource from,
Class<T> configurationClass,
T configuration,
- String prefix) {
+ String prefix,
+ boolean reconfiguration
+ ) {
for (Field field : configurationClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Config.class)) {
- if ((field.getModifiers() & Modifier.FINAL) != 0) {
- throw new ConfigurationException(String.format(
- "Trying to set final field %s#%s, probably indicates misplaced "
+
- "@Config annotation",
- configurationClass.getSimpleName(), field.getName()));
- }
-
- String fieldLocation =
- configurationClass + "." + field.getName();
+ checkNotFinal(configurationClass, field);
Config configAnnotation = field.getAnnotation(Config.class);
- String key = prefix + "." + configAnnotation.key();
+ if (reconfiguration && !configAnnotation.reconfigurable()) {
+ continue;
+ }
+ String key = prefix + "." + configAnnotation.key();
String defaultValue = configAnnotation.defaultValue();
+ String value = from.get(key, defaultValue);
- ConfigType type = configAnnotation.type();
+ setField(configurationClass, configuration, field, configAnnotation,
+ key, value);
+ }
+ }
+ }
- if (type == ConfigType.AUTO) {
- type = detectConfigType(field.getType(), fieldLocation);
- }
+ public static <T> void reconfigureProperty(T configuration, Field field,
+ String key, String value) {
+ if (!field.isAnnotationPresent(Config.class)) {
+ throw new ConfigurationException("Not configurable field: "
+ + field.getDeclaringClass() + "." + field.getName());
+ }
+ Config configAnnotation = field.getAnnotation(Config.class);
- try {
- switch (type) {
- case STRING:
- forcedFieldSet(field, configuration, from.get(key, defaultValue));
- break;
- case INT:
- forcedFieldSet(field, configuration,
- from.getInt(key, Integer.parseInt(defaultValue)));
- break;
- case BOOLEAN:
- forcedFieldSet(field, configuration,
- from.getBoolean(key, Boolean.parseBoolean(defaultValue)));
- break;
- case LONG:
- forcedFieldSet(field, configuration,
- from.getLong(key, Long.parseLong(defaultValue)));
- break;
- case DOUBLE:
- forcedFieldSet(field, configuration,
- from.getDouble(key, Double.parseDouble(defaultValue)));
- break;
- case TIME:
- forcedFieldSet(field, configuration,
- from.getTimeDuration(key, defaultValue,
- configAnnotation.timeUnit()));
- break;
- case SIZE:
- final long value =
- Math.round(from.getStorageSize(key,
- defaultValue, StorageUnit.BYTES));
- if (field.getType() == int.class) {
- forcedFieldSet(field, configuration, (int) value);
- } else {
- forcedFieldSet(field, configuration, value);
+ setField(field.getDeclaringClass(), configuration, field, configAnnotation,
+ key, value);
+ }
- }
- break;
- case CLASS:
- forcedFieldSet(field, configuration,
- from.getClass(key, Class.forName(defaultValue)));
- break;
- default:
- throw new ConfigurationException(
- "Unsupported ConfigType " + type + " on " + fieldLocation);
- }
- } catch (IllegalAccessException | ClassNotFoundException e) {
- throw new ConfigurationException(
- "Can't inject configuration to " + fieldLocation, e);
- }
+ private static <T> void setField(
+ Class<?> configurationClass, T configuration, Field field,
+ Config configAnnotation, String key, String value) {
+ ConfigType type = configAnnotation.type();
+ if (type == ConfigType.AUTO) {
+ type = detectConfigType(field);
+ }
- }
+ try {
+ Object parsed = type.parse(value, configAnnotation, field.getType(),
key);
+ forcedFieldSet(field, configuration, parsed);
+ } catch (Exception e) {
+ throw new ConfigurationException("Failed to inject configuration to "
+ + configurationClass.getSimpleName() + "." + field.getName(), e);
}
}
@@ -146,9 +150,9 @@ public final class ConfigurationReflectionUtil {
}
}
- private static ConfigType detectConfigType(Class<?> parameterType,
- String methodLocation) {
+ private static ConfigType detectConfigType(Field field) {
ConfigType type;
+ Class<?> parameterType = field.getType();
if (parameterType == String.class) {
type = ConfigType.STRING;
} else if (parameterType == Integer.class || parameterType == int.class) {
@@ -163,9 +167,9 @@ public final class ConfigurationReflectionUtil {
} else if (parameterType == Class.class) {
type = ConfigType.CLASS;
} else {
- throw new ConfigurationException(
- "Unsupported configuration type " + parameterType + " in "
- + methodLocation);
+ throw new ConfigurationException("Unsupported configuration type "
+ + parameterType + " in "
+ + field.getDeclaringClass() + "." + field.getName());
}
return type;
}
@@ -223,7 +227,7 @@ public final class ConfigurationReflectionUtil {
ConfigType type = configAnnotation.type();
if (type == ConfigType.AUTO) {
- type = detectConfigType(field.getType(), fieldLocation);
+ type = detectConfigType(field);
}
//Note: default value is handled by ozone-default.xml. Here we can
@@ -291,13 +295,10 @@ public final class ConfigurationReflectionUtil {
public static Optional<String> getKey(Class<?> configClass,
String fieldName) {
- ConfigGroup configGroup =
- configClass.getAnnotation(ConfigGroup.class);
+ Optional<String> prefix = getPrefix(configClass);
- return findFieldConfigAnnotationByName(configClass,
- fieldName).map(
- config -> configGroup == null ? config.key()
- : configGroup.prefix() + "." + config.key());
+ return findFieldConfigAnnotationByName(configClass, fieldName)
+ .map(config -> getFullKey(prefix, config));
}
public static Optional<ConfigType> getType(Class<?> configClass,
@@ -326,4 +327,32 @@ public final class ConfigurationReflectionUtil {
}
return Optional.empty();
}
+
+ private static <T> void checkNotFinal(
+ Class<T> configurationClass, Field field) {
+
+ if ((field.getModifiers() & Modifier.FINAL) != 0) {
+ throw new ConfigurationException(String.format(
+ "Trying to set final field %s#%s, probably indicates misplaced " +
+ "@Config annotation",
+ configurationClass.getSimpleName(), field.getName()));
+ }
+ }
+
+ private static <T> Optional<String> getPrefix(Class<T> configurationClass) {
+ ConfigGroup configGroup =
+ configurationClass.getAnnotation(ConfigGroup.class);
+ return configGroup != null
+ ? Optional.of(configGroup.prefix())
+ : Optional.empty();
+ }
+
+ private static String getFullKey(
+ Optional<String> optionalPrefix, Config configAnnotation) {
+ String key = configAnnotation.key();
+ return optionalPrefix
+ .map(prefix -> prefix + "." + key)
+ .orElse(key);
+ }
+
}
diff --git
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
index 8c4ee7cd2f..63231cfa14 100644
---
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
+++
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
@@ -175,7 +175,7 @@ public interface ConfigurationSource {
ConfigurationReflectionUtil
.injectConfiguration(this, configurationClass, configObject,
- prefix);
+ prefix, false);
ConfigurationReflectionUtil
.callPostConstruct(configurationClass, configObject);
@@ -184,6 +184,16 @@ public interface ConfigurationSource {
}
+ /**
+ * Update {@code object}'s reconfigurable properties from this configuration.
+ */
+ default <T> void reconfigure(Class<T> configClass, T object) {
+ ConfigGroup configGroup = configClass.getAnnotation(ConfigGroup.class);
+ String prefix = configGroup.prefix();
+ ConfigurationReflectionUtil.injectConfiguration(
+ this, configClass, object, prefix, true);
+ }
+
/**
* Get the value of the <code>name</code> property as a <code>Class</code>
* implementing the interface specified by <code>xface</code>.
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ReconfigurableConfig.java
similarity index 56%
copy from
hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
copy to
hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ReconfigurableConfig.java
index f6007c2221..1427364000 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
+++
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ReconfigurableConfig.java
@@ -1,4 +1,4 @@
-/**
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -17,20 +17,27 @@
*/
package org.apache.hadoop.hdds.conf;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.Set;
+
/**
- * Example configuration to test inherited configuration injection.
+ * Base class for config with reconfigurable properties.
*/
-public class ConfigurationExampleGrandParent {
+public abstract class ReconfigurableConfig {
- @Config(key = "number", defaultValue = "2", description = "Example numeric "
- + "configuration", tags = ConfigTag.MANAGEMENT)
- private int number = 1;
+ private final Map<String, Field> reconfigurableProperties =
+ ConfigurationReflectionUtil.mapReconfigurableProperties(getClass());
- public int getNumber() {
- return number;
+ public void reconfigureProperty(String property, String newValue) {
+ Field field = reconfigurableProperties.get(property);
+ if (field != null) {
+ ConfigurationReflectionUtil.reconfigureProperty(this,
+ field, property, newValue);
+ }
}
- public void setNumber(int number) {
- this.number = number;
+ public Set<String> reconfigurableProperties() {
+ return reconfigurableProperties.keySet();
}
}
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExample.java
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExample.java
index 9e039d1279..1a2e76b59f 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExample.java
+++
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExample.java
@@ -47,11 +47,25 @@ public class ConfigurationExample extends
ConfigurationExampleParent {
+ "test TIME config type)", tags = ConfigTag.MANAGEMENT)
private long waitTime = 1;
+ @Config(key = "size.small", type = ConfigType.SIZE, defaultValue = "42MB",
+ tags = {},
+ description = "Testing SIZE with int field")
+ private int smallSize;
+
+ @Config(key = "size.large", type = ConfigType.SIZE,
+ defaultValue = "5GB", tags = {},
+ description = "Testing SIZE with long field")
+ private long largeSize;
+
@Config(key = "threshold", type = ConfigType.DOUBLE, defaultValue = "10",
description = "Threshold (To test DOUBLE config type)",
tags = ConfigTag.MANAGEMENT)
private double threshold = 10;
+ @Config(key = "dynamic", reconfigurable = true, defaultValue = "original",
+ description = "Test dynamic property", tags = {})
+ private String dynamic;
+
public void setClientAddress(String clientAddress) {
this.clientAddress = clientAddress;
}
@@ -99,4 +113,8 @@ public class ConfigurationExample extends
ConfigurationExampleParent {
public double getThreshold() {
return threshold;
}
+
+ public String getDynamic() {
+ return dynamic;
+ }
}
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
index f6007c2221..e302638e5c 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
+++
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
@@ -20,12 +20,16 @@ package org.apache.hadoop.hdds.conf;
/**
* Example configuration to test inherited configuration injection.
*/
-public class ConfigurationExampleGrandParent {
+public class ConfigurationExampleGrandParent extends ReconfigurableConfig {
@Config(key = "number", defaultValue = "2", description = "Example numeric "
+ "configuration", tags = ConfigTag.MANAGEMENT)
private int number = 1;
+ @Config(key = "grandpa.dyna", reconfigurable = true, defaultValue = "x",
+ description = "Test inherited dynamic property", tags = {})
+ private String grandpaDynamic;
+
public int getNumber() {
return number;
}
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationReflectionUtil.java
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationReflectionUtil.java
index d9f3d43198..5732f6c561 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationReflectionUtil.java
+++
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationReflectionUtil.java
@@ -17,99 +17,82 @@
*/
package org.apache.hadoop.hdds.conf;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import com.google.common.collect.ImmutableSet;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* Test the configuration reflection utility class.
*/
-@RunWith(Parameterized.class)
-public class TestConfigurationReflectionUtil {
-
- private final ConfigType type;
- private final String key;
- private final String defaultValue;
- private final Class testClass;
- private final String fieldName;
- private final boolean typePresent;
- private final boolean keyPresent;
- private final boolean defaultValuePresent;
+class TestConfigurationReflectionUtil {
- @Parameterized.Parameters
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][]{
- {ConfigurationExample.class, "waitTime",
- ConfigType.TIME, true,
- "ozone.scm.client.wait", true,
- "30m", true},
- {ConfigurationExampleGrandParent.class, "number",
- ConfigType.AUTO, true,
- "number", true,
- "2", true},
- {ConfigurationExample.class, "secure",
- ConfigType.AUTO, true,
- "ozone.scm.client.secure", true,
- "true", true},
- {ConfigurationExample.class, "no-such-field",
- null, false,
- "", false,
- "", false},
- {ConfigFileAppender.class, "document",
- null, false,
- "", false,
- "", false},
- {ConfigurationExample.class, "threshold",
- ConfigType.DOUBLE, true,
- "ozone.scm.client.threshold", true,
- "10", true},
- });
+ static Stream<Arguments> data() {
+ return Stream.of(
+ arguments(ConfigurationExample.class, "waitTime",
+ Optional.of(ConfigType.TIME),
+ Optional.of("ozone.scm.client.wait"),
+ Optional.of("30m")),
+ arguments(ConfigurationExampleGrandParent.class, "number",
+ Optional.of(ConfigType.AUTO),
+ Optional.of("number"),
+ Optional.of("2")),
+ arguments(ConfigurationExample.class, "secure",
+ Optional.of(ConfigType.AUTO),
+ Optional.of("ozone.scm.client.secure"),
+ Optional.of("true")),
+ arguments(ConfigurationExample.class, "no-such-field",
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty()),
+ arguments(ConfigFileAppender.class, "document",
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty()),
+ arguments(ConfigurationExample.class, "threshold",
+ Optional.of(ConfigType.DOUBLE),
+ Optional.of("ozone.scm.client.threshold"),
+ Optional.of("10"))
+ );
}
- @SuppressWarnings("checkstyle:ParameterNumber")
- public TestConfigurationReflectionUtil(
- Class testClass, String fieldName,
- ConfigType type, boolean typePresent,
- String key, boolean keyPresent,
- String defaultValue, boolean defaultValuePresent) {
- this.testClass = testClass;
- this.fieldName = fieldName;
- this.typePresent = typePresent;
- this.type = type;
- this.key = key;
- this.keyPresent = keyPresent;
- this.defaultValue = defaultValue;
- this.defaultValuePresent = defaultValuePresent;
+ @ParameterizedTest
+ @MethodSource("data")
+ void testForGivenClasses(Class<?> testClass, String fieldName,
+ Optional<ConfigType> expectedType,
+ Optional<String> expectedKey,
+ Optional<String> expectedDefault) {
+ Optional<ConfigType> type = ConfigurationReflectionUtil.getType(
+ testClass, fieldName);
+ assertEquals(expectedType, type);
+
+ Optional<String> key = ConfigurationReflectionUtil.getKey(
+ testClass, fieldName);
+ assertEquals(expectedKey, key);
+
+ Optional<String> defaultValue =
ConfigurationReflectionUtil.getDefaultValue(
+ testClass, fieldName);
+ assertEquals(expectedDefault, defaultValue);
}
@Test
- public void testForGivenClasses() {
- Optional<ConfigType> actualType =
- ConfigurationReflectionUtil.getType(
- testClass, fieldName);
- Assert.assertEquals(typePresent, actualType.isPresent());
- if (typePresent) {
- Assert.assertEquals(type, actualType.get());
- }
+ void listReconfigurableProperties() {
+ Set<String> props =
+ ConfigurationReflectionUtil.mapReconfigurableProperties(
+ ConfigurationExample.class).keySet();
- Optional<String> actualKey =
- ConfigurationReflectionUtil.getKey(
- testClass, fieldName);
- Assert.assertEquals(keyPresent, actualKey.isPresent());
- if (keyPresent) {
- Assert.assertEquals(key, actualKey.get());
- }
- Optional<String> actualDefaultValue =
- ConfigurationReflectionUtil.getDefaultValue(
- testClass, fieldName);
- Assert.assertEquals(defaultValuePresent, actualDefaultValue.isPresent());
- if (defaultValuePresent) {
- Assert.assertEquals(defaultValue, actualDefaultValue.get());
- }
+ String prefix = "ozone.scm.client";
+ assertEquals(ImmutableSet.of(
+ prefix + ".dynamic",
+ prefix + ".grandpa.dyna"
+ ), props);
}
}
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationSource.java
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationSource.java
index ac92276a73..e135fb8c36 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationSource.java
+++
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestConfigurationSource.java
@@ -18,10 +18,11 @@
package org.apache.hadoop.hdds.conf;
-import org.junit.Assert;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
-import java.util.Map;
+import static org.junit.jupiter.api.Assertions.assertEquals;
class TestConfigurationSource {
@@ -29,22 +30,44 @@ class TestConfigurationSource {
void getPropsMatchPrefixAndTrimPrefix() {
MutableConfigurationSource c = new InMemoryConfiguration();
c.set("somePrefix.key", "value");
- ConfigurationSource config = c;
- Map<String, String> entry =
- config.getPropsMatchPrefixAndTrimPrefix("somePrefix.");
- Assert.assertEquals("key", entry.keySet().toArray()[0]);
- Assert.assertEquals("value", entry.values().toArray()[0]);
+
+ assertEquals(ImmutableMap.of("key", "value"),
+ c.getPropsMatchPrefixAndTrimPrefix("somePrefix."));
}
@Test
void getPropsMatchPrefix() {
MutableConfigurationSource c = new InMemoryConfiguration();
c.set("somePrefix.key", "value");
- ConfigurationSource config = c;
- Map<String, String> entry =
- config.getPropsMatchPrefix("somePrefix.");
- Assert.assertEquals("somePrefix.key",
- entry.keySet().toArray()[0]);
- Assert.assertEquals("value", entry.values().toArray()[0]);
+
+ assertEquals(ImmutableMap.of("somePrefix.key", "value"),
+ c.getPropsMatchPrefix("somePrefix."));
+ }
+ @Test
+ void reconfigurableProperties() {
+ String prefix = "ozone.scm.client";
+ ImmutableSet<String> expected = ImmutableSet.of(
+ prefix + ".dynamic",
+ prefix + ".grandpa.dyna"
+ );
+
+ ConfigurationExample obj = new InMemoryConfiguration().getObject(
+ ConfigurationExample.class);
+
+ assertEquals(expected, obj.reconfigurableProperties());
+ }
+
+ @Test
+ void reconfiguration() {
+ MutableConfigurationSource subject = new InMemoryConfiguration();
+ ConfigurationExample orig = subject.getObject(ConfigurationExample.class);
+ ConfigurationExample obj = subject.getObject(ConfigurationExample.class);
+
+ subject.set("ozone.scm.client.dynamic", "updated");
+ subject.setLong("ozone.scm.client.wait", orig.getWaitTime() + 42);
+ subject.reconfigure(ConfigurationExample.class, obj);
+
+ assertEquals("updated", obj.getDynamic());
+ assertEquals(orig.getWaitTime(), obj.getWaitTime());
}
}
diff --git
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestReconfigurableConfig.java
similarity index 65%
copy from
hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
copy to
hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestReconfigurableConfig.java
index f6007c2221..ed1123aeb7 100644
---
a/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/ConfigurationExampleGrandParent.java
+++
b/hadoop-hdds/config/src/test/java/org/apache/hadoop/hdds/conf/TestReconfigurableConfig.java
@@ -1,4 +1,4 @@
-/**
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -17,20 +17,22 @@
*/
package org.apache.hadoop.hdds.conf;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
- * Example configuration to test inherited configuration injection.
+ * Test for {@link ReconfigurableConfig}.
*/
-public class ConfigurationExampleGrandParent {
+class TestReconfigurableConfig {
- @Config(key = "number", defaultValue = "2", description = "Example numeric "
- + "configuration", tags = ConfigTag.MANAGEMENT)
- private int number = 1;
+ @Test
+ void testReconfigureProperty() {
+ ConfigurationExample subject = new InMemoryConfiguration()
+ .getObject(ConfigurationExample.class);
- public int getNumber() {
- return number;
- }
+ subject.reconfigureProperty("ozone.scm.client.dynamic", "updated");
- public void setNumber(int number) {
- this.number = number;
+ assertEquals("updated", subject.getDynamic());
}
}
diff --git a/hadoop-hdds/config/src/test/resources/log4j.properties
b/hadoop-hdds/config/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..398786689a
--- /dev/null
+++ b/hadoop-hdds/config/src/test/resources/log4j.properties
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# log4j configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2}
(%F:%M(%L)) - %m%n
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]