You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2021/03/10 10:22:21 UTC
[ignite-3] branch main updated: IGNITE-14193 Default values support
for configuration framework (#61)
This is an automated email from the ASF dual-hosted git repository.
ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 0b09ebc IGNITE-14193 Default values support for configuration framework (#61)
0b09ebc is described below
commit 0b09ebc235709c27c14654841e6ba46cb99f27f3
Author: ibessonov <be...@gmail.com>
AuthorDate: Wed Mar 10 13:19:10 2021 +0300
IGNITE-14193 Default values support for configuration framework (#61)
---
.../processor/internal/ConfigSet.java | 26 +++-
.../internal/TestConfigurationSchema.java | 8 +-
.../processor/internal/Processor.java | 49 ++++++--
.../internal/util/ConfigurationUtilTest.java | 2 +-
.../sample/AutoAdjustConfigurationSchema.java | 4 +-
.../sample/CacheConfigurationSchema.java | 3 +-
.../sample/ConfigurationArrayTest.java | 2 +-
.../sample/ConstructableTreeNodeTest.java | 32 +++++
.../sample/DiscoveryConfigurationSchema.java | 4 +-
.../sample/NodeConfigurationSchema.java | 7 +-
.../sample/TraversableTreeNodeTest.java | 8 +-
.../sample/storage/ConfigurationChangerTest.java | 54 +++++++-
.../ignite/configuration/ConfigurationChanger.java | 136 ++++++++++++++++++---
.../ignite/configuration/annotation/Value.java | 6 +
.../configuration/internal/ConfigurationNode.java | 3 +
.../configuration/tree/ConstructableTreeNode.java | 9 ++
.../ignite/configuration/tree/NamedListChange.java | 3 +
.../ignite/configuration/tree/NamedListNode.java | 7 +-
.../configuration/RestConfigurationSchema.java | 5 +-
.../extended/AutoAdjustConfigurationSchema.java | 5 +-
.../extended/DataStorageConfigurationSchema.java | 7 +-
21 files changed, 321 insertions(+), 59 deletions(-)
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
index 6b0ab23..4f5a425 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
@@ -18,9 +18,15 @@ package org.apache.ignite.configuration.processor.internal;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.Collection;
import javax.tools.JavaFileObject;
import org.apache.commons.io.IOUtils;
import spoon.Launcher;
+import spoon.SpoonException;
+import spoon.compiler.SpoonResource;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtType;
+import spoon.support.compiler.VirtualFile;
/**
* Wrapper for generated classes of the configuration schema.
@@ -79,7 +85,7 @@ public class ConfigSet {
throw new RuntimeException("Failed to parse class: " + e.getMessage(), e);
}
- return new ParsedClass(Launcher.parseClass(classFileContent));
+ return new ParsedClass(parseClass(classFileContent));
}
/**
@@ -97,4 +103,22 @@ public class ConfigSet {
public ParsedClass getNodeClass() {
return node;
}
+
+ /**
+ * Butchered version of {@link Launcher#parseClass(String)}, because {@code spoon} is such a garbage it can't even
+ * parse valid classes without issues.
+ *
+ * @param code Code.
+ * @return AST.
+ */
+ private static CtClass<?> parseClass(String code) {
+ Launcher launcher = new Launcher();
+ launcher.addInputResource((SpoonResource)(new VirtualFile(code)));
+ launcher.getEnvironment().setNoClasspath(true);
+ launcher.getEnvironment().setAutoImports(true);
+ Collection<CtType<?>> allTypes = launcher.buildModel().getAllTypes();
+
+ // This is how we do "getLast" for streams. Pretty bad, I know.
+ return (CtClass)allTypes.stream().reduce((fst, snd) -> snd).orElseThrow(SpoonException::new);
+ }
}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/configuration/processor/internal/TestConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/configuration/processor/internal/TestConfigurationSchema.java
index 90fe957..b0144ea 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/configuration/processor/internal/TestConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/configuration/processor/internal/TestConfigurationSchema.java
@@ -23,14 +23,14 @@ import org.apache.ignite.configuration.annotation.Value;
@ConfigurationRoot(rootName = "test")
public class TestConfigurationSchema {
@Value
- private String value1;
+ public String value1;
@Value
- private long primitiveLong;
+ public long primitiveLong;
@Value
- private int primitiveInt;
+ public int primitiveInt;
@Value
- private String[] stringArray;
+ public String[] stringArray;
}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
index 898346c..3b2715a 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
@@ -92,6 +92,9 @@ public class Processor extends AbstractProcessor {
/** Wildcard (?) TypeName. */
private static final TypeName WILDCARD = WildcardTypeName.subtypeOf(Object.class);
+ /** Inherit doc javadoc. */
+ private static final String INHERIT_DOC = "{@inheritDoc}";
+
/** Class file writer. */
private Filer filer;
@@ -172,6 +175,8 @@ public class Processor extends AbstractProcessor {
CodeBlock.Builder constructorBodyBuilder = CodeBlock.builder();
for (VariableElement field : fields) {
+ assert field.getModifiers().contains(PUBLIC) : clazz.getQualifiedName() + "#" + field.getSimpleName();
+
Element fieldTypeElement = processingEnv.getTypeUtils().asElement(field.asType());
// Get original field type (must be another configuration schema or "primitive" like String or long)
@@ -270,7 +275,7 @@ public class Processor extends AbstractProcessor {
props.put(configClass, configDesc);
// Create VIEW, INIT and CHANGE classes
- createPojoBindings(fields, schemaClassName, configurationClassBuilder, configurationInterfaceBuilder);
+ createPojoBindings(clazz, fields, schemaClassName, configurationClassBuilder, configurationInterfaceBuilder);
if (isRoot) {
TypeMirror storageType = null;
@@ -342,7 +347,7 @@ public class Processor extends AbstractProcessor {
MethodSpec.Builder getMethodBuilder = MethodSpec.methodBuilder(fieldName)
.addAnnotation(Override.class)
- .addJavadoc("{@inheritDoc}")
+ .addJavadoc(INHERIT_DOC)
.addModifiers(PUBLIC, FINAL)
.returns(types.getInterfaceGetMethodType());
@@ -501,11 +506,13 @@ public class Processor extends AbstractProcessor {
/**
* Create VIEW, INIT and CHANGE classes and methods.
+ * @param clazz Original class for the schema.
* @param fields List of configuration fields.
* @param schemaClassName Class name of schema.
* @param configurationClassBuilder Configuration class builder.
*/
private void createPojoBindings(
+ TypeElement clazz,
List<VariableElement> fields,
ClassName schemaClassName,
TypeSpec.Builder configurationClassBuilder,
@@ -550,13 +557,18 @@ public class Processor extends AbstractProcessor {
.superclass(ClassName.get(InnerNode.class))
.addSuperinterface(viewClsName)
.addSuperinterface(changeClsName)
- .addSuperinterface(initClsName);
+ .addSuperinterface(initClsName)
+ // Cannot use "schemaClassName" here because it can't handle inner static classes.
+ .addField(FieldSpec.builder(ClassName.get(clazz), "_spec", PRIVATE, FINAL)
+ .initializer("new $T()", ClassName.get(clazz))
+ .build()
+ );
TypeVariableName t = TypeVariableName.get("T");
MethodSpec.Builder traverseChildrenBuilder = MethodSpec.methodBuilder("traverseChildren")
.addAnnotation(Override.class)
- .addJavadoc("{@inheritDoc}")
+ .addJavadoc(INHERIT_DOC)
.addModifiers(PUBLIC)
.addTypeVariable(t)
.returns(TypeName.VOID)
@@ -564,7 +576,7 @@ public class Processor extends AbstractProcessor {
MethodSpec.Builder traverseChildBuilder = MethodSpec.methodBuilder("traverseChild")
.addAnnotation(Override.class)
- .addJavadoc("{@inheritDoc}")
+ .addJavadoc(INHERIT_DOC)
.addModifiers(PUBLIC)
.addTypeVariable(t)
.returns(t)
@@ -575,13 +587,23 @@ public class Processor extends AbstractProcessor {
MethodSpec.Builder constructBuilder = MethodSpec.methodBuilder("construct")
.addAnnotation(Override.class)
- .addJavadoc("{@inheritDoc}")
+ .addJavadoc(INHERIT_DOC)
.addModifiers(PUBLIC)
.returns(TypeName.VOID)
+ .addException(NoSuchElementException.class)
.addParameter(ClassName.get(String.class), "key")
.addParameter(ClassName.get(ConfigurationSource.class), "src")
.beginControlFlow("switch (key)");
+ MethodSpec.Builder constructDefaultBuilder = MethodSpec.methodBuilder("constructDefault")
+ .addAnnotation(Override.class)
+ .addJavadoc(INHERIT_DOC)
+ .addModifiers(PUBLIC)
+ .returns(TypeName.BOOLEAN)
+ .addException(NoSuchElementException.class)
+ .addParameter(ClassName.get(String.class), "key")
+ .beginControlFlow("switch (key)");
+
ClassName consumerClsName = ClassName.get(Consumer.class);
for (VariableElement field : fields) {
@@ -817,6 +839,14 @@ public class Processor extends AbstractProcessor {
schemaFieldType.box()
)
.addStatement(INDENT + "break");
+
+ if (valAnnotation.hasDefault()) {
+ constructDefaultBuilder
+ .addStatement("case $S: $L = _spec.$L", fieldName, fieldName, fieldName)
+ .addStatement(INDENT + "return true");
+ }
+ else
+ constructDefaultBuilder.addStatement("case $S: return false", fieldName);
}
else if (namedListField) {
constructBuilder
@@ -862,10 +892,15 @@ public class Processor extends AbstractProcessor {
.addStatement("default: throw new $T(key)", NoSuchElementException.class)
.endControlFlow();
+ constructDefaultBuilder
+ .addStatement("default: throw new $T(key)", NoSuchElementException.class)
+ .endControlFlow();
+
nodeClsBuilder
.addMethod(traverseChildrenBuilder.build())
.addMethod(traverseChildBuilder.build())
- .addMethod(constructBuilder.build());
+ .addMethod(constructBuilder.build())
+ .addMethod(constructDefaultBuilder.build());
TypeSpec viewCls = viewClsBuilder.build();
TypeSpec changeCls = changeClsBuilder.build();
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
index a989733..0a0fa7a 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
@@ -93,7 +93,7 @@ public class ConfigurationUtilTest {
public static class ChildConfigurationSchema {
/** */
@Value
- private String str;
+ public String str;
}
/** */
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/AutoAdjustConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/AutoAdjustConfigurationSchema.java
index ef3c02b..86245a5 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/AutoAdjustConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/AutoAdjustConfigurationSchema.java
@@ -34,9 +34,9 @@ public class AutoAdjustConfigurationSchema {
@Min(value = 0, message = "Minimal is 0")
@Validate(value = AutoAdjustValidator.class, message = "a")
@Validate(value = AutoAdjustValidator2.class, message = "b")
- private long timeout;
+ public long timeout;
/** Enabled. */
@Value
- private boolean enabled;
+ public boolean enabled;
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/CacheConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/CacheConfigurationSchema.java
index 8d35e66..5a04640 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/CacheConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/CacheConfigurationSchema.java
@@ -29,6 +29,5 @@ public class CacheConfigurationSchema {
/** Size. */
@Value
@Min(value = 1, message = "Minimal cache size is 1")
- private int size;
-
+ public int size;
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
index a565653..4f9929e 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
@@ -38,7 +38,7 @@ public class ConfigurationArrayTest {
public static class TestArrayConfigurationSchema {
/** */
@Value
- private String[] array;
+ public String[] array;
}
/**
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
index e1e8421..12f260d 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
@@ -28,10 +28,12 @@ import org.apache.ignite.configuration.tree.NamedListNode;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
/** */
public class ConstructableTreeNodeTest {
@@ -144,4 +146,34 @@ public class ConstructableTreeNodeTest {
assertEquals("value", elementsNode.get("name").strCfg());
}
+
+ /** */
+ @Test
+ public void constructDefault() {
+ // Inner node with no leaves.
+ var parentNode = new ParentNode();
+
+ assertThrows(NoSuchElementException.class, () -> parentNode.constructDefault("child"));
+ assertThrows(NoSuchElementException.class, () -> parentNode.constructDefault("elements"));
+
+ // Inner node with a leaf.
+ parentNode.changeElements(elements -> elements.create("name", element -> {}));
+
+ NamedElementNode elementNode = parentNode.elements().get("name");
+
+ assertFalse(elementNode.constructDefault("strCfg"));
+
+ // Another inner node with leaves.
+ parentNode.changeChild(child -> {});
+
+ ChildNode child = parentNode.child();
+
+ assertFalse(child.constructDefault("strCfg"));
+
+ assertThrows(NullPointerException.class, () -> child.intCfg());
+
+ assertTrue(child.constructDefault("intCfg"));
+
+ assertEquals(99, child.intCfg());
+ }
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/DiscoveryConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/DiscoveryConfigurationSchema.java
index 289878d..e709c21 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/DiscoveryConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/DiscoveryConfigurationSchema.java
@@ -27,9 +27,9 @@ import org.apache.ignite.configuration.annotation.Value;
public class DiscoveryConfigurationSchema {
/** Node failure detection timeout. */
@Value
- private int failureDetectionTimeout;
+ public int failureDetectionTimeout;
/** Node join timeout. */
@Value
- private int joinTimeout;
+ public int joinTimeout;
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NodeConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NodeConfigurationSchema.java
index 74d2d48..df21d02 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NodeConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NodeConfigurationSchema.java
@@ -31,15 +31,14 @@ public class NodeConfigurationSchema {
/** Consistent id. */
@Value(immutable = true)
@NotNull(message = "Consistent id must not be null")
- private String consistentId;
+ public String consistentId;
/** Port. */
@Value
- private int port;
+ public int port;
/** Auto adjust enabled. */
@Value
@Validate(NodeValidator.class)
- private boolean autoAdjustEnabled;
-
+ public boolean autoAdjustEnabled;
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
index 214a173..6248654 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
@@ -61,12 +61,12 @@ public class TraversableTreeNodeTest {
@Config
public static class ChildConfigurationSchema {
/** */
- @Value(immutable = true)
- private int intCfg;
+ @Value(immutable = true, hasDefault = true)
+ public int intCfg = 99;
/** */
@Value
- private String strCfg;
+ public String strCfg;
}
/** */
@@ -74,7 +74,7 @@ public class TraversableTreeNodeTest {
public static class NamedElementConfigurationSchema {
/** */
@Value
- private String strCfg;
+ public String strCfg;
}
/** */
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/ConfigurationChangerTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/ConfigurationChangerTest.java
index 60d4b0a..d35f4c2 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/ConfigurationChangerTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/ConfigurationChangerTest.java
@@ -29,6 +29,7 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.configuration.sample.storage.impl.ANode;
+import org.apache.ignite.configuration.sample.storage.impl.DefaultsNode;
import org.apache.ignite.configuration.storage.Data;
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.junit.jupiter.api.Test;
@@ -61,11 +62,11 @@ public class ConfigurationChangerTest {
public static class BConfigurationSchema {
/** */
@Value(immutable = true)
- private int intCfg;
+ public int intCfg;
/** */
@Value
- private String strCfg;
+ public String strCfg;
}
/** */
@@ -73,7 +74,7 @@ public class ConfigurationChangerTest {
public static class CConfigurationSchema {
/** */
@Value
- private String strCfg;
+ public String strCfg;
}
/**
@@ -234,6 +235,53 @@ public class ConfigurationChangerTest {
assertNull(newRoot.child());
}
+ /** */
+ @ConfigurationRoot(rootName = "def", storage = TestConfigurationStorage.class)
+ public static class DefaultsConfigurationSchema {
+ /** */
+ @ConfigValue
+ private DefaultsChildConfigurationSchema child;
+
+ /** */
+ @NamedConfigValue
+ private DefaultsChildConfigurationSchema childsList;
+
+ /** */
+ @Value(hasDefault = true)
+ public String defStr = "foo";
+ }
+
+ /** */
+ @Config
+ public static class DefaultsChildConfigurationSchema {
+ /** */
+ @Value(hasDefault = true)
+ public String defStr = "bar";
+ }
+
+ @Test
+ public void defaultsOnInit() throws Exception {
+ var changer = new ConfigurationChanger();
+
+ changer.addRootKey(DefaultsConfiguration.KEY);
+
+ changer.init(new TestConfigurationStorage());
+
+ DefaultsNode root = (DefaultsNode)changer.getRootNode(DefaultsConfiguration.KEY);
+
+ assertEquals("foo", root.defStr());
+ assertEquals("bar", root.child().defStr());
+
+ // This is not init, move it to another test =(
+ changer.change(Map.of(DefaultsConfiguration.KEY, new DefaultsNode().changeChildsList(childs ->
+ childs.create("name", child -> {})
+ ))).get(1, SECONDS);
+
+ root = (DefaultsNode)changer.getRootNode(DefaultsConfiguration.KEY);
+
+ assertEquals("bar", root.childsList().get("name").defStr());
+ }
+
/**
* Wrapper for Configurator mock to control validation.
*/
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
index b6f91fb..1934f72 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
@@ -27,13 +27,17 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;
import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
import org.apache.ignite.configuration.storage.ConfigurationStorage;
import org.apache.ignite.configuration.storage.Data;
import org.apache.ignite.configuration.storage.StorageException;
+import org.apache.ignite.configuration.tree.ConfigurationSource;
+import org.apache.ignite.configuration.tree.ConfigurationVisitor;
import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.NamedListNode;
import org.apache.ignite.configuration.tree.TraversableTreeNode;
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
import org.apache.ignite.configuration.validation.ValidationIssue;
@@ -110,23 +114,26 @@ public class ConfigurationChanger {
}
Map<RootKey<?>, InnerNode> storageRootsMap = new HashMap<>();
+ // Map to collect defaults for not initialized configurations.
+ Map<RootKey<?>, InnerNode> storageDefaultsMap = new HashMap<>();
Map<String, ?> dataValuesPrefixMap = ConfigurationUtil.toPrefixMap(data.values());
for (RootKey<?> rootKey : storageRootKeys) {
Map<String, ?> rootPrefixMap = (Map<String, ?>)dataValuesPrefixMap.get(rootKey.key());
- if (rootPrefixMap == null) {
- //TODO IGNITE-14193 Init with defaults.
- storageRootsMap.put(rootKey, rootKey.createRootNode());
- }
- else {
- InnerNode rootNode = rootKey.createRootNode();
+ InnerNode rootNode = rootKey.createRootNode();
+ if (rootPrefixMap != null)
ConfigurationUtil.fillFromPrefixMap(rootNode, rootPrefixMap);
- storageRootsMap.put(rootKey, rootNode);
- }
+ // Collecting defaults requires fresh new root.
+ InnerNode defaultsNode = rootKey.createRootNode();
+
+ addDefaults(rootNode, defaultsNode);
+
+ storageRootsMap.put(rootKey, rootNode);
+ storageDefaultsMap.put(rootKey, defaultsNode);
}
storagesRootsMap.put(configurationStorage.getClass(), new StorageRoots(storageRootsMap, data.version()));
@@ -135,6 +142,74 @@ public class ConfigurationChanger {
configurationStorage.getClass(),
changedEntries
));
+
+ // Do this strictly after adding listeners, otherwise we can lose these changes.
+ try {
+ //TODO IGNITE-14183 Do not write defaults that have not been validated. This can ruin everything.
+ change(storageDefaultsMap).get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ throw new ConfigurationChangeException("too bad", e);
+ }
+ }
+
+ /**
+ * Fill {@code dst} node with default values, required to complete {@code src} node.
+ * These two objects can be the same, this would mean that all {@code null} values of {@code scr} will be
+ * replaced with defaults if it's possible.
+ *
+ * @param src Source node.
+ * @param dst Destination node.
+ */
+ private void addDefaults(InnerNode src, InnerNode dst) {
+ src.traverseChildren(new ConfigurationVisitor<>() {
+ @Override public Object visitLeafNode(String key, Serializable val) {
+ // If source value is null then inititalise the same value on the destination node.
+ if (val == null)
+ dst.constructDefault(key);
+
+ return null;
+ }
+
+ @Override public Object visitInnerNode(String key, InnerNode srcNode) {
+ // Instantiate field in destination node before doing something else.
+ // Not a big deal if it wasn't null.
+ dst.construct(key, new ConfigurationSource() {});
+
+ // Get that inner node from destination to continue the processing.
+ InnerNode dstNode = dst.traverseChild(key, new ConfigurationVisitor<>() {
+ @Override public InnerNode visitInnerNode(String key, InnerNode dstNode) {
+ return dstNode;
+ }
+ });
+
+ // "dstNode" is guaranteed to not be null even if "src" and "dst" match.
+ // Null in "srcNode" means that we should initialize everything that we can in "dstNode"
+ // unconditionally. It's only possible if we pass it as a source as well.
+ addDefaults(srcNode == null ? dstNode : srcNode, dstNode);
+
+ return null;
+ }
+
+ @Override public <N extends InnerNode> Object visitNamedListNode(String key, NamedListNode<N> srcNamedList) {
+ // Here we don't need to preemptively initialise corresponsing field, because it can never be null.
+ NamedListNode<?> dstNamedList = dst.traverseChild(key, new ConfigurationVisitor<>() {
+ @Override public <N extends InnerNode> NamedListNode<?> visitNamedListNode(String key, NamedListNode<N> dstNode) {
+ return dstNode;
+ }
+ });
+
+ for (String namedListKey : srcNamedList.namedListKeys()) {
+ // But, in order to get non-null value from "dstNamedList.get(namedListKey)" we must explicitly
+ // ensure its existance.
+ dstNamedList.construct(namedListKey, new ConfigurationSource() {});
+
+ addDefaults(srcNamedList.get(namedListKey), dstNamedList.get(namedListKey));
+ }
+
+ return null;
+ }
+ });
}
/**
@@ -161,7 +236,7 @@ public class ConfigurationChanger {
* Change configuration.
* @param changes Map of changes by root key.
*/
- public CompletableFuture<Void> change(Map<RootKey<?>, TraversableTreeNode> changes) {
+ public CompletableFuture<Void> change(Map<RootKey<?>, ? extends TraversableTreeNode> changes) {
if (changes.isEmpty())
return CompletableFuture.completedFuture(null);
@@ -195,18 +270,45 @@ public class ConfigurationChanger {
* @param fut Future, that must be completed after changes are written to the storage.
*/
private void change0(
- Map<RootKey<?>, TraversableTreeNode> changes,
+ Map<RootKey<?>, ? extends TraversableTreeNode> changes,
ConfigurationStorage storage,
CompletableFuture<?> fut
) {
+ StorageRoots storageRoots = storagesRootsMap.get(storage.getClass());
+
Map<String, Serializable> allChanges = new HashMap<>();
- for (Map.Entry<RootKey<?>, TraversableTreeNode> change : changes.entrySet())
- allChanges.putAll(nodeToFlatMap(change.getKey(), getRootNode(change.getKey()), change.getValue()));
+ for (Map.Entry<RootKey<?>, ? extends TraversableTreeNode> entry : changes.entrySet()) {
+ RootKey<?> rootKey = entry.getKey();
+ TraversableTreeNode change = entry.getValue();
+
+ // It's important to get the root from "roots" object rather then "storageRootMap" or "getRootNode(...)".
+ InnerNode currentRootNode = storageRoots.roots.get(rootKey);
+
+ // These are changes explicitly provided by the client.
+ allChanges.putAll(nodeToFlatMap(rootKey, currentRootNode, change));
+
+ // It is necessary to reinitialize default values every time.
+ // Possible use case that explicitly requires it: creation of the same named list entry with slightly
+ // different set of values and different dynamic defaults at the same time.
+ InnerNode patchedRootNode = ConfigurationUtil.patch(currentRootNode, change);
+ InnerNode defaultsNode = rootKey.createRootNode();
- StorageRoots roots = storagesRootsMap.get(storage.getClass());
+ addDefaults(patchedRootNode, defaultsNode);
+
+ // These are default values for non-initialized values, required to complete the configuration.
+ //TODO IGNITE-14183 Take these defaults into account during validation.
+ allChanges.putAll(nodeToFlatMap(rootKey, patchedRootNode, defaultsNode));
+ }
+
+ // Unlikely but still possible.
+ if (allChanges.isEmpty()) {
+ fut.complete(null);
+
+ return;
+ }
- ValidationResult validationResult = validate(roots, changes);
+ ValidationResult validationResult = validate(storageRoots, changes);
List<ValidationIssue> validationIssues = validationResult.issues();
@@ -216,7 +318,7 @@ public class ConfigurationChanger {
return;
}
- CompletableFuture<Boolean> writeFut = storage.write(allChanges, roots.version);
+ CompletableFuture<Boolean> writeFut = storage.write(allChanges, storageRoots.version);
writeFut.whenCompleteAsync((casResult, throwable) -> {
if (throwable != null)
@@ -301,11 +403,11 @@ public class ConfigurationChanger {
@SuppressWarnings("unused")
private ValidationResult validate(
StorageRoots storageRoots,
- Map<RootKey<?>, TraversableTreeNode> changes
+ Map<RootKey<?>, ? extends TraversableTreeNode> changes
) {
List<ValidationIssue> issues = new ArrayList<>();
- for (Map.Entry<RootKey<?>, TraversableTreeNode> entry : changes.entrySet()) {
+ for (Map.Entry<RootKey<?>, ? extends TraversableTreeNode> entry : changes.entrySet()) {
RootKey<?> rootKey = entry.getKey();
TraversableTreeNode changesForRoot = entry.getValue();
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
index b39c484..a5efbb5 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
@@ -45,4 +45,10 @@ public @interface Value {
* @return {@code true} if this value can only be initialized and can't be changed afterwards.
*/
boolean immutable() default false;
+
+ /**
+ * Indicator that current cpnfoguration value has default value. Value itself is derived from instantiated object
+ * of corresponding schema type. This means that default is not necessarily a constant value.
+ */
+ boolean hasDefault() default false;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
index 12461a8..bcee71a 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
@@ -19,6 +19,7 @@ package org.apache.ignite.configuration.internal;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
import org.apache.ignite.configuration.ConfigurationChanger;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
@@ -68,6 +69,8 @@ public abstract class ConfigurationNode<VIEW> {
this.key = key;
this.rootKey = rootKey;
this.changer = changer;
+
+ assert Objects.equals(rootKey.key(), keys.get(0));
}
/**
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
index 44b77d8..2584006 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
@@ -32,6 +32,15 @@ public interface ConstructableTreeNode {
void construct(String key,/* boolean canMutate, */ ConfigurationSource src) throws NoSuchElementException;
/**
+ * Assigns default value to the corresponding leaf. Defaults are gathered from configuration schema class.
+ *
+ * @param key Field name to be initialized.
+ * @return {@code true} if default value has been assigned, {@code false} otherwise.
+ * @throws NoSuchElementException If there's no such field or it is not a leaf value.
+ */
+ boolean constructDefault(String key) throws NoSuchElementException;
+
+ /**
* Public equivalent of {@link Object#clone()} method. Creates a copy with effectively the same content.
* Helps to preserve trees immutability after construction is completed.
*
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListChange.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListChange.java
index 1dc15f6..1e57a55 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListChange.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListChange.java
@@ -40,4 +40,7 @@ public interface NamedListChange<Change, Init> extends NamedListInit<Init> {
* @throws IllegalStateException If {@link #update(String, Consumer)} has been invoked with the same key previously.
*/
NamedListChange<Change, Init> delete(String key) throws IllegalStateException;
+
+ /** {@inheritDoc} */
+ @Override NamedListChange<Change, Init> create(String key, Consumer<Init> valConsumer);
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
index 55ca78a..609abfa 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
@@ -95,7 +95,7 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
return this;
}
- @Override public NamedListInit<N> create(String key, Consumer<N> valConsumer) {
+ @Override public NamedListChange<N, N> create(String key, Consumer<N> valConsumer) {
Objects.requireNonNull(valConsumer, "valConsumer");
N val = map.get(key);
@@ -124,6 +124,11 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
}
/** {@inheritDoc} */
+ @Override public boolean constructDefault(String key) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
@Override public NamedListNode<N> copy() {
return new NamedListNode<>(this);
}
diff --git a/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java b/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
index 10c5e52..5cd7bc1 100644
--- a/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
+++ b/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
@@ -23,14 +23,13 @@ import org.apache.ignite.configuration.annotation.Value;
/**
* Configuration schema for REST endpoint subtree.
*/
-@SuppressWarnings("PMD.UnusedPrivateField")
@ConfigurationRoot(rootName = "rest")
public class RestConfigurationSchema {
/** */
@Value
- private int port;
+ public int port;
/** */
@Value
- private int portRange;
+ public int portRange;
}
diff --git a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/AutoAdjustConfigurationSchema.java b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/AutoAdjustConfigurationSchema.java
index e86df97..87ebef5 100644
--- a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/AutoAdjustConfigurationSchema.java
+++ b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/AutoAdjustConfigurationSchema.java
@@ -22,15 +22,14 @@ import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.Value;
/** */
-@SuppressWarnings("PMD.UnusedPrivateField")
@Config
public class AutoAdjustConfigurationSchema {
/** */
@Value
- private boolean enabled;
+ public boolean enabled;
/** */
@Value
@Min(value = 0, message = "Minimum value is 0")
- private int timeout;
+ public int timeout;
}
diff --git a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/DataStorageConfigurationSchema.java b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/DataStorageConfigurationSchema.java
index f9d9b93..2a8b678 100644
--- a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/DataStorageConfigurationSchema.java
+++ b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/DataStorageConfigurationSchema.java
@@ -21,18 +21,17 @@ import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.Value;
/** */
-@SuppressWarnings("PMD.UnusedPrivateField")
@Config
public class DataStorageConfigurationSchema {
/** */
@Value
- private int pageSize;
+ public int pageSize;
/** */
@Value
- private String storagePath;
+ public String storagePath;
/** */
@Value
- private String walPath;
+ public String walPath;
}