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/03 16:05:29 UTC
[ignite-3] branch main updated: IGNITE-14182 Implemented naming
list configuration elements removal. (#58)
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 81921cb IGNITE-14182 Implemented naming list configuration elements removal. (#58)
81921cb is described below
commit 81921cb21b427484b3ff81455eb5bfcc1a961f6a
Author: ibessonov <be...@gmail.com>
AuthorDate: Wed Mar 3 19:05:20 2021 +0300
IGNITE-14182 Implemented naming list configuration elements removal. (#58)
---
.../processor/internal/Processor.java | 79 +++++++------
.../configuration/processor/internal/Utils.java | 9 ++
.../ignite/configuration/sample/UsageTest.java | 7 ++
.../sample/storage/TestConfigurationStorage.java | 11 +-
.../ignite/configuration/ConfigurationChanger.java | 39 ++++++-
...nfigurator.java => NamedConfigurationTree.java} | 31 +++---
.../configuration/annotation/NamedConfigValue.java | 2 -
.../ignite/configuration/annotation/Value.java | 2 +-
.../configuration/internal/ConfigurationNode.java | 4 +-
.../internal/DynamicConfiguration.java | 14 ++-
.../configuration/internal/DynamicProperty.java | 12 +-
.../ignite/configuration/internal/NamedList.java | 44 --------
.../internal/NamedListConfiguration.java | 18 ++-
.../internal/util/ConfigurationUtil.java | 122 ++++++++++++++++-----
.../ITScaleCubeNetworkClusterMessagingTest.java | 2 +
15 files changed, 233 insertions(+), 163 deletions(-)
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 2ec61bf..898346c 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
@@ -17,7 +17,6 @@
package org.apache.ignite.configuration.processor.internal;
-import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@@ -57,6 +56,7 @@ import org.apache.ignite.configuration.ConfigurationChanger;
import org.apache.ignite.configuration.ConfigurationRegistry;
import org.apache.ignite.configuration.ConfigurationTree;
import org.apache.ignite.configuration.ConfigurationValue;
+import org.apache.ignite.configuration.NamedConfigurationTree;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.ConfigValue;
@@ -80,6 +80,7 @@ import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
+import static org.apache.ignite.configuration.processor.internal.Utils.suppressWarningsUnchecked;
/**
* Annotation processor that produces configuration classes.
@@ -181,7 +182,7 @@ public class Processor extends AbstractProcessor {
// Get configuration types (VIEW, INIT, CHANGE and so on)
final ConfigurationFieldTypes types = getTypes(field);
- TypeName getMethodType = types.getGetMethodType();
+ TypeName fieldType = types.getFieldType();
TypeName viewClassType = types.getViewClassType();
TypeName initClassType = types.getInitClassType();
TypeName changeClassType = types.getChangeClassType();
@@ -198,13 +199,13 @@ public class Processor extends AbstractProcessor {
// Create DynamicConfiguration (descendant) field
final FieldSpec nestedConfigField =
FieldSpec
- .builder(getMethodType, fieldName, Modifier.PRIVATE, FINAL)
+ .builder(fieldType, fieldName, Modifier.PRIVATE, FINAL)
.build();
configurationClassBuilder.addField(nestedConfigField);
// Constructor statement
- constructorBodyBuilder.addStatement("add($L = new $T(keys, $S, rootKey, changer))", fieldName, getMethodType, fieldName);
+ constructorBodyBuilder.addStatement("add($L = new $T(keys, $S, rootKey, changer))", fieldName, fieldType, fieldName);
}
final NamedConfigValue namedConfigAnnotation = field.getAnnotation(NamedConfigValue.class);
@@ -216,11 +217,9 @@ public class Processor extends AbstractProcessor {
);
}
- ClassName fieldType = Utils.getConfigurationName((ClassName) baseType);
-
// Create NamedListConfiguration<> field
final FieldSpec nestedConfigField = FieldSpec.builder(
- getMethodType,
+ fieldType,
fieldName,
Modifier.PRIVATE,
FINAL
@@ -232,9 +231,9 @@ public class Processor extends AbstractProcessor {
constructorBodyBuilder.addStatement(
"add($L = new $T(keys, $S, rootKey, changer, (p, k) -> new $T(p, k, rootKey, changer)))",
fieldName,
- getMethodType,
+ fieldType,
fieldName,
- fieldType
+ Utils.getConfigurationName((ClassName) baseType)
);
}
@@ -250,7 +249,7 @@ public class Processor extends AbstractProcessor {
}
// Create value (DynamicProperty<>) field
- final FieldSpec generatedField = FieldSpec.builder(getMethodType, fieldName, Modifier.PRIVATE, FINAL).build();
+ final FieldSpec generatedField = FieldSpec.builder(fieldType, fieldName, Modifier.PRIVATE, FINAL).build();
configurationClassBuilder.addField(generatedField);
@@ -259,11 +258,11 @@ public class Processor extends AbstractProcessor {
// Constructor statement
constructorBodyBuilder.addStatement(
"add($L = new $T(keys, $S, rootKey, changer), $L)",
- fieldName, getMethodType, fieldName, validatorsBlock
+ fieldName, fieldType, fieldName, validatorsBlock
);
}
- configDesc.getFields().add(new ConfigurationElement(getMethodType, fieldName, viewClassType, initClassType, changeClassType));
+ configDesc.getFields().add(new ConfigurationElement(fieldType, fieldName, viewClassType, initClassType, changeClassType));
createGetters(configurationClassBuilder, configurationInterfaceBuilder, fieldName, types);
}
@@ -341,14 +340,20 @@ public class Processor extends AbstractProcessor {
.build();
configurationInterfaceBuilder.addMethod(interfaceGetMethod);
- MethodSpec getMethod = MethodSpec.methodBuilder(fieldName)
+ MethodSpec.Builder getMethodBuilder = MethodSpec.methodBuilder(fieldName)
.addAnnotation(Override.class)
.addJavadoc("{@inheritDoc}")
.addModifiers(PUBLIC, FINAL)
- .returns(types.getGetMethodType())
- .addStatement("return $L", fieldName)
- .build();
- configurationClassBuilder.addMethod(getMethod);
+ .returns(types.getInterfaceGetMethodType());
+
+ if (Utils.isNamedConfiguration(types.getFieldType())) {
+ getMethodBuilder.addAnnotation(suppressWarningsUnchecked())
+ .addStatement("return ($T)$L", NamedConfigurationTree.class, fieldName);
+ }
+ else
+ getMethodBuilder.addStatement("return $L", fieldName);
+
+ configurationClassBuilder.addMethod(getMethodBuilder.build());
}
/**
@@ -357,7 +362,7 @@ public class Processor extends AbstractProcessor {
* @return Bundle with all types for configuration
*/
private ConfigurationFieldTypes getTypes(final VariableElement field) {
- TypeName getMethodType = null;
+ TypeName fieldType = null;
TypeName interfaceGetMethodType = null;
final TypeName baseType = TypeName.get(field.asType());
@@ -369,10 +374,10 @@ public class Processor extends AbstractProcessor {
final ConfigValue confAnnotation = field.getAnnotation(ConfigValue.class);
if (confAnnotation != null) {
- getMethodType = Utils.getConfigurationName((ClassName) baseType);
+ fieldType = Utils.getConfigurationName((ClassName) baseType);
interfaceGetMethodType = Utils.getConfigurationInterfaceName((ClassName) baseType);
- unwrappedType = getMethodType;
+ unwrappedType = fieldType;
viewClassType = Utils.getViewName((ClassName) baseType);
initClassType = Utils.getInitName((ClassName) baseType);
changeClassType = Utils.getChangeName((ClassName) baseType);
@@ -380,16 +385,14 @@ public class Processor extends AbstractProcessor {
final NamedConfigValue namedConfigAnnotation = field.getAnnotation(NamedConfigValue.class);
if (namedConfigAnnotation != null) {
- ClassName fieldType = Utils.getConfigurationName((ClassName) baseType);
- //TODO IGNITE-14182 This is BS, interface name must be used instead.
- ClassName interfaceFieldType = Utils.getConfigurationName((ClassName) baseType);
+ ClassName interfaceGetType = Utils.getConfigurationInterfaceName((ClassName) baseType);
viewClassType = Utils.getViewName((ClassName) baseType);
initClassType = Utils.getInitName((ClassName) baseType);
changeClassType = Utils.getChangeName((ClassName) baseType);
- getMethodType = ParameterizedTypeName.get(ClassName.get(NamedListConfiguration.class), viewClassType, fieldType, initClassType, changeClassType);
- interfaceGetMethodType = ParameterizedTypeName.get(ClassName.get(NamedListConfiguration.class), viewClassType, interfaceFieldType, initClassType, changeClassType);
+ fieldType = ParameterizedTypeName.get(ClassName.get(NamedListConfiguration.class), interfaceGetType, viewClassType, changeClassType, initClassType);
+ interfaceGetMethodType = ParameterizedTypeName.get(ClassName.get(NamedConfigurationTree.class), interfaceGetType, viewClassType, changeClassType, initClassType);
}
final Value valueAnnotation = field.getAnnotation(Value.class);
@@ -403,11 +406,11 @@ public class Processor extends AbstractProcessor {
genericType = genericType.box();
}
- getMethodType = ParameterizedTypeName.get(dynPropClass, genericType);
+ fieldType = ParameterizedTypeName.get(dynPropClass, genericType);
interfaceGetMethodType = ParameterizedTypeName.get(confValueClass, genericType);
}
- return new ConfigurationFieldTypes(getMethodType, unwrappedType, viewClassType, initClassType, changeClassType, interfaceGetMethodType);
+ return new ConfigurationFieldTypes(fieldType, unwrappedType, viewClassType, initClassType, changeClassType, interfaceGetMethodType);
}
/**
@@ -415,7 +418,7 @@ public class Processor extends AbstractProcessor {
*/
private static class ConfigurationFieldTypes {
/** Field get method type. */
- private final TypeName getMethodType;
+ private final TypeName fieldType;
/** Configuration type (if marked with @ConfigValue or @NamedConfig), or original type (if marked with @Value) */
private final TypeName unwrappedType;
@@ -432,8 +435,8 @@ public class Processor extends AbstractProcessor {
/** Get method type for public interface. */
private final TypeName interfaceGetMethodType;
- private ConfigurationFieldTypes(TypeName getMethodType, TypeName unwrappedType, TypeName viewClassType, TypeName initClassType, TypeName changeClassType, TypeName interfaceGetMethodType) {
- this.getMethodType = getMethodType;
+ private ConfigurationFieldTypes(TypeName fieldType, TypeName unwrappedType, TypeName viewClassType, TypeName initClassType, TypeName changeClassType, TypeName interfaceGetMethodType) {
+ this.fieldType = fieldType;
this.unwrappedType = unwrappedType;
this.viewClassType = viewClassType;
this.initClassType = initClassType;
@@ -447,8 +450,8 @@ public class Processor extends AbstractProcessor {
}
/** */
- public TypeName getGetMethodType() {
- return getMethodType;
+ public TypeName getFieldType() {
+ return fieldType;
}
/** */
@@ -711,11 +714,7 @@ public class Processor extends AbstractProcessor {
nodeChangeMtdBuilder.addStatement("$L.accept($L)", paramName, fieldName);
}
else {
- nodeChangeMtdBuilder.addAnnotation(
- AnnotationSpec.builder(SuppressWarnings.class)
- .addMember("value", "$S", "unchecked")
- .build()
- );
+ nodeChangeMtdBuilder.addAnnotation(suppressWarningsUnchecked());
nodeChangeMtdBuilder.addStatement("$L.accept((NamedListChange)$L)", paramName, fieldName);
}
@@ -776,11 +775,7 @@ public class Processor extends AbstractProcessor {
nodeInitMtdBuilder.addStatement("$L.accept($L)", paramName, fieldName);
}
else {
- nodeInitMtdBuilder.addAnnotation(
- AnnotationSpec.builder(SuppressWarnings.class)
- .addMember("value", "$S", "unchecked")
- .build()
- );
+ nodeInitMtdBuilder.addAnnotation(suppressWarningsUnchecked());
nodeInitMtdBuilder.addStatement("$L.accept((NamedListChange)$L)", paramName, fieldName);
}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
index e08cf13..f11fe9b 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
@@ -16,6 +16,7 @@
*/
package org.apache.ignite.configuration.processor.internal;
+import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
@@ -222,4 +223,12 @@ public class Utils {
throw new ProcessorException(type + " is not a NamedListConfiguration class");
}
+ /**
+ * @return {@code @SuppressWarnings("unchecked")} annotation spec object.
+ */
+ public static AnnotationSpec suppressWarningsUnchecked() {
+ return AnnotationSpec.builder(SuppressWarnings.class)
+ .addMember("value", "$S", "unchecked")
+ .build();
+ }
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
index ba6dcd2..4eb513c 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -54,6 +55,7 @@ public class UsageTest {
)
)
).get(1, SECONDS);
+
assertTrue(root.baseline().autoAdjust().enabled().value());
root.baseline().autoAdjust().enabled().update(false).get(1, SECONDS);
@@ -73,6 +75,11 @@ public class UsageTest {
root.baseline().nodes().get("node1").change(node -> node.changeAutoAdjustEnabled(false)).get(1, SECONDS);
assertFalse(root.value().baseline().nodes().get("node1").autoAdjustEnabled());
+
+ root.baseline().nodes().change(nodes -> nodes.delete("node1")).get(1, SECONDS);
+
+ assertNull(root.baseline().nodes().get("node1"));
+ assertNull(root.value().baseline().nodes().get("node1"));
}
/**
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/TestConfigurationStorage.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/TestConfigurationStorage.java
index f29a6af..5c2d405 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/TestConfigurationStorage.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/storage/TestConfigurationStorage.java
@@ -55,7 +55,7 @@ public class TestConfigurationStorage implements ConfigurationStorage {
}
/** {@inheritDoc} */
- @Override public Data readAll() throws StorageException {
+ @Override public synchronized Data readAll() throws StorageException {
if (fail)
throw new StorageException("Failed to read data");
@@ -70,7 +70,12 @@ public class TestConfigurationStorage implements ConfigurationStorage {
if (sentVersion != version.get())
return CompletableFuture.completedFuture(false);
- map.putAll(newValues);
+ for (Map.Entry<String, Serializable> entry : newValues.entrySet()) {
+ if (entry.getValue() != null)
+ map.put(entry.getKey(), entry.getValue());
+ else
+ map.remove(entry.getKey());
+ }
version.incrementAndGet();
@@ -80,7 +85,7 @@ public class TestConfigurationStorage implements ConfigurationStorage {
}
/** {@inheritDoc} */
- @Override public Set<String> keys() throws StorageException {
+ @Override public synchronized Set<String> keys() throws StorageException {
if (fail)
throw new StorageException("Failed to get keys");
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 1ee2d04..b6f91fb 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
@@ -173,7 +173,7 @@ public class ConfigurationChanger {
if (storagesTypes.size() != 1) {
return CompletableFuture.failedFuture(
- new ConfigurationChangeException("Cannot change configurations belonging to different roots")
+ new ConfigurationChangeException("Cannot change configurations belonging to different storages.")
);
}
@@ -199,10 +199,10 @@ public class ConfigurationChanger {
ConfigurationStorage storage,
CompletableFuture<?> fut
) {
- Map<String, Serializable> allChanges = changes.entrySet().stream()
- .map((Map.Entry<RootKey<?>, TraversableTreeNode> change) -> nodeToFlatMap(change.getKey(), change.getValue()))
- .flatMap(map -> map.entrySet().stream())
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ Map<String, Serializable> allChanges = new HashMap<>();
+
+ for (Map.Entry<RootKey<?>, TraversableTreeNode> change : changes.entrySet())
+ allChanges.putAll(nodeToFlatMap(change.getKey(), getRootNode(change.getKey()), change.getValue()));
StorageRoots roots = storagesRootsMap.get(storage.getClass());
@@ -243,8 +243,9 @@ public class ConfigurationChanger {
Map<String, ?> dataValuesPrefixMap = ConfigurationUtil.toPrefixMap(changedEntries.values());
+ compressDeletedEntries(dataValuesPrefixMap);
+
for (RootKey<?> rootKey : oldStorageRoots.roots.keySet()) {
- //TODO IGNITE-14182 Remove is not yet supported here.
Map<String, ?> rootPrefixMap = (Map<String, ?>)dataValuesPrefixMap.get(rootKey.key());
if (rootPrefixMap != null) {
@@ -264,6 +265,32 @@ public class ConfigurationChanger {
}
/**
+ * "Compress" prefix map - this means that deleted named list elements will be represented as a single {@code null}
+ * objects instead of a number of nullified configuration leaves.
+ *
+ * @param prefixMap Prefix map, constructed from the storage notification data or its subtree.
+ */
+ private void compressDeletedEntries(Map<String, ?> prefixMap) {
+ // Here we basically assume that if prefix subtree contains single null child then all its childrens are nulls.
+ Set<String> keysForRemoval = prefixMap.entrySet().stream()
+ .filter(entry ->
+ entry.getValue() instanceof Map && ((Map<?, ?>)entry.getValue()).containsValue(null)
+ )
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
+
+ // Replace all such elements will nulls, signifying that these are deleted named list elements.
+ for (String key : keysForRemoval)
+ prefixMap.put(key, null);
+
+ // Continue recursively.
+ for (Object value : prefixMap.values()) {
+ if (value instanceof Map)
+ compressDeletedEntries((Map<String, ?>)value);
+ }
+ }
+
+ /**
* Validate configuration changes.
*
* @param storageRoots Storage roots.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/PublicConfigurator.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/NamedConfigurationTree.java
similarity index 54%
rename from modules/configuration/src/main/java/org/apache/ignite/configuration/PublicConfigurator.java
rename to modules/configuration/src/main/java/org/apache/ignite/configuration/NamedConfigurationTree.java
index 7b8f953..e4f096c 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/PublicConfigurator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/NamedConfigurationTree.java
@@ -17,26 +17,23 @@
package org.apache.ignite.configuration;
-import org.apache.ignite.configuration.internal.DynamicConfiguration;
+import org.apache.ignite.configuration.tree.NamedListChange;
+import org.apache.ignite.configuration.tree.NamedListView;
/**
- * Public configurator.
- * @param <T> Public type.
+ * Configuration tree representing arbitrary set of named underlying configuration tree of the same type.
+ *
+ * @param <T> Type of the underlying configuration tree.
+ * @param <VALUE> Value type of the underlying node.
+ * @param <CHANGE> Type of the object that changes underlying nodes values.
*/
-public class PublicConfigurator<T extends ConfigurationTree<?, ?>> {
- /** Configuration root. */
- private T root;
-
- public <VIEW, INIT, CHANGE> PublicConfigurator(Configurator<? extends DynamicConfiguration<VIEW, INIT, CHANGE>> configurator) {
- final ConfigurationTree<VIEW, CHANGE> root = configurator.getRoot();
- this.root = (T) root;
- }
-
+public interface NamedConfigurationTree<T extends ConfigurationProperty<VIEW, CHANGE>, VIEW, CHANGE, INIT>
+ extends ConfigurationTree<NamedListView<VIEW>, NamedListChange<CHANGE, INIT>>
+{
/**
- * Get root of the configuration.
- * @return Configuration root.
+ * Get named configuration by name.
+ * @param name Name.
+ * @return Configuration.
*/
- public T getRoot() {
- return root;
- }
+ T get(String name);
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
index f3d3f77..5ecc62b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
@@ -21,7 +21,6 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.ignite.configuration.internal.NamedListConfiguration;
-import org.apache.ignite.configuration.internal.NamedList;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -42,7 +41,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
*
* }
* </pre>
- * @see NamedList
*/
@Target({ FIELD })
@Retention(SOURCE)
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 4459890..b39c484 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
@@ -28,7 +28,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* This annotation marks configuration schema field as a configuration tree leaf.
* Every field annotated with this annotation will produce a {@link DynamicProperty} field in generated configuration class.
- * <br/> Type must be one of the following:
+ * <br/> Type must be one of the following (or array of one of the following):
* <ul>
* <li>boolean</li>
* <li>int</li>
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 62bbba5..12461a8 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
@@ -93,7 +93,7 @@ public abstract class ConfigurationNode<VIEW> {
if (cachedRootNode == oldRootNode) {
cachedRootNode = newRootNode;
- refreshValue0(newVal);
+ beforeRefreshValue(newVal);
return val = newVal;
}
@@ -128,5 +128,5 @@ public abstract class ConfigurationNode<VIEW> {
*
* @param newValue New configuration value.
*/
- protected abstract void refreshValue0(VIEW newValue);
+ protected abstract void beforeRefreshValue(VIEW newValue);
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
index 5b2bbeb..97c217f 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
@@ -18,6 +18,7 @@
package org.apache.ignite.configuration.internal;
import java.io.Serializable;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -25,7 +26,6 @@ import java.util.Objects;
import java.util.RandomAccess;
import java.util.concurrent.Future;
import java.util.function.Consumer;
-import java.util.stream.Collectors;
import org.apache.ignite.configuration.ConfigurationChanger;
import org.apache.ignite.configuration.ConfigurationProperty;
import org.apache.ignite.configuration.ConfigurationTree;
@@ -99,18 +99,20 @@ public abstract class DynamicConfiguration<VIEW, INIT, CHANGE> extends Configura
else {
assert keys instanceof RandomAccess;
+ // Transform inner node closure into update tree.
rootNodeChange.construct(keys.get(1), new ConfigurationSource() {
- private int i = 1;
+ private int level = 1;
@Override public void descend(ConstructableTreeNode node) {
- if (++i == keys.size())
+ if (++level == keys.size())
change.accept((CHANGE)node);
else
- node.construct(keys.get(i), this);
+ node.construct(keys.get(level), this);
}
});
}
+ // Use resulting tree as update request for the storage.
return changer.change(Map.of(rootKey, rootNodeChange));
}
@@ -126,11 +128,11 @@ public abstract class DynamicConfiguration<VIEW, INIT, CHANGE> extends Configura
/** {@inheritDoc} */
@Override public Map<String, ConfigurationProperty<?, ?>> members() {
- return members.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ return Collections.unmodifiableMap(members);
}
/** {@inheritDoc} */
- @Override protected void refreshValue0(VIEW newValue) {
+ @Override protected void beforeRefreshValue(VIEW newValue) {
// No-op.
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
index 883ad93..1beb83a 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
@@ -79,17 +79,18 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
assert keys instanceof RandomAccess;
assert !keys.isEmpty();
+ // Transform leaf value into update tree.
rootNodeChange.construct(keys.get(1), new ConfigurationSource() {
- private int i = 1;
+ private int level = 1;
@Override public void descend(ConstructableTreeNode node) {
- assert i < keys.size() - 1;
+ assert level < keys.size() - 1;
- node.construct(keys.get(++i), this);
+ node.construct(keys.get(++level), this);
}
@Override public <T> T unwrap(Class<T> clazz) {
- assert i == keys.size() - 1;
+ assert level == keys.size() - 1;
assert clazz.isInstance(newValue);
@@ -97,6 +98,7 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
}
});
+ // Use resulting tree as update request for the storage.
return changer.change(Map.of(rootKey, rootNodeChange));
}
@@ -106,7 +108,7 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
}
/** {@inheritDoc} */
- @Override protected void refreshValue0(T newValue) {
+ @Override protected void beforeRefreshValue(T newValue) {
// No-op.
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedList.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedList.java
deleted file mode 100644
index edce953..0000000
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedList.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.configuration.internal;
-
-import java.util.Map;
-
-/**
- * This class holds named configurations in VIEW object.
- */
-public class NamedList<T> {
- /** Named values. */
- private final Map<String, T> values;
-
- /**
- * Constructor.
- * @param values Named values.
- */
- public NamedList(Map<String, T> values) {
- this.values = values;
- }
-
- /**
- * Get named values.
- * @return Named values.
- */
- public Map<String, T> getValues() {
- return values;
- }
-}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedListConfiguration.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedListConfiguration.java
index 5f3f450..107f06f 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedListConfiguration.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/NamedListConfiguration.java
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.function.BiFunction;
import org.apache.ignite.configuration.ConfigurationChanger;
import org.apache.ignite.configuration.ConfigurationProperty;
+import org.apache.ignite.configuration.NamedConfigurationTree;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.tree.NamedListChange;
import org.apache.ignite.configuration.tree.NamedListInit;
@@ -31,8 +32,10 @@ import org.apache.ignite.configuration.tree.NamedListView;
/**
* Named configuration wrapper.
*/
-public class NamedListConfiguration<VIEW, T extends ConfigurationProperty<VIEW, CHANGE>, INIT, CHANGE>
- extends DynamicConfiguration<NamedListView<VIEW>, NamedListInit<INIT>, NamedListChange<CHANGE, INIT>> {
+public class NamedListConfiguration<T extends ConfigurationProperty<VIEW, CHANGE>, VIEW, CHANGE, INIT>
+ extends DynamicConfiguration<NamedListView<VIEW>, NamedListInit<INIT>, NamedListChange<CHANGE, INIT>>
+ implements NamedConfigurationTree<T, VIEW, CHANGE, INIT>
+{
/** Creator of named configuration. */
private final BiFunction<List<String>, String, T> creator;
@@ -57,20 +60,15 @@ public class NamedListConfiguration<VIEW, T extends ConfigurationProperty<VIEW,
this.creator = creator;
}
- /**
- * Get named configuration by name.
- * @param name Name.
- * @return Configuration.
- */
- public T get(String name) {
+ /** {@inheritDoc} */
+ @Override public T get(String name) {
refreshValue();
- //TODO IGNITE-14182 Exceptions.
return values.get(name);
}
/** {@inheritDoc} */
- @Override protected synchronized void refreshValue0(NamedListView<VIEW> newValue) {
+ @Override protected synchronized void beforeRefreshValue(NamedListView<VIEW> newValue) {
Map<String, T> oldValues = this.values;
Map<String, T> newValues = new HashMap<>();
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
index 2d491b9..1c09c97 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
@@ -275,64 +275,136 @@ public class ConfigurationUtil {
/**
* Convert a traversable tree to a map of qualified keys to values.
+ *
* @param rootKey Root configuration key.
- * @param node Tree.
+ * @param curRoot Current root tree.
+ * @param updates Tree with updates.
* @return Map of changes.
*/
- public static Map<String, Serializable> nodeToFlatMap(RootKey<?> rootKey, TraversableTreeNode node) {
- Map<String, Serializable> values = new HashMap<>();
+ public static Map<String, Serializable> nodeToFlatMap(
+ RootKey<?> rootKey,
+ TraversableTreeNode curRoot,
+ TraversableTreeNode updates
+ ) {
+ return updates.accept(rootKey.key(), new ConfigurationVisitor<>() {
+ /** Resulting flat map. */
+ private Map<String, Serializable> values = new HashMap<>();
- node.accept(rootKey.key(), new ConfigurationVisitor<>() {
/** Current key, aggregated by visitor. */
- StringBuilder currentKey = new StringBuilder();
+ private StringBuilder currentKey = new StringBuilder();
+
+ /** Current keys list, almost the same as {@link #currentKey}. */
+ private List<String> currentPath = new ArrayList<>();
+
+ /** Write nulls instead of actual values. Makes sense for deletions from named lists. */
+ private boolean writeNulls;
/** {@inheritDoc} */
- @Override public Void visitLeafNode(String key, Serializable val) {
+ @Override public Map<String, Serializable> visitLeafNode(String key, Serializable val) {
if (val != null)
- values.put(currentKey.toString() + key, val);
+ values.put(currentKey.toString() + key, writeNulls ? null : val);
- return null;
+ return values;
}
/** {@inheritDoc} */
- @Override public Void visitInnerNode(String key, InnerNode node) {
+ @Override public Map<String, Serializable> visitInnerNode(String key, InnerNode node) {
if (node == null)
return null;
- int previousKeyLength = currentKey.length();
-
- currentKey.append(key).append('.');
+ int previousKeyLength = startVisit(key, false);
node.traverseChildren(this);
- currentKey.setLength(previousKeyLength);
+ endVisit(previousKeyLength);
- return null;
+ return values;
}
/** {@inheritDoc} */
- @Override public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> node) {
- int previousKeyLength = currentKey.length();
-
- if (key != null)
- currentKey.append(key).append('.');
+ @Override public <N extends InnerNode> Map<String, Serializable> visitNamedListNode(String key, NamedListNode<N> node) {
+ int previousKeyLength = startVisit(key, false);
for (String namedListKey : node.namedListKeys()) {
- int loopPreviousKeyLength = currentKey.length();
+ int loopPreviousKeyLength = startVisit(namedListKey, true);
- currentKey.append(ConfigurationUtil.escape(namedListKey)).append('.');
+ N namedElement = node.get(namedListKey);
- node.get(namedListKey).traverseChildren(this);
+ if (namedElement == null)
+ visitDeletedNamedListElement();
+ else
+ namedElement.traverseChildren(this);
- currentKey.setLength(loopPreviousKeyLength);
+ endVisit(loopPreviousKeyLength);
}
+ endVisit(previousKeyLength);
+
+ return values;
+ }
+
+ /**
+ * Prepares values of {@link #currentKey} and {@link #currentPath} for further processing.
+ *
+ * @param key Key.
+ * @param escape Whether we need to escape the key before appending it to {@link #currentKey}.
+ * @return Previous length of {@link #currentKey} so it can be passed to {@link #endVisit(int)} later.
+ */
+ private int startVisit(String key, boolean escape) {
+ int previousKeyLength = currentKey.length();
+
+ currentKey.append(escape ? ConfigurationUtil.escape(key) : key).append('.');
+
+ if (!writeNulls)
+ currentPath.add(key);
+
+ return previousKeyLength;
+ }
+
+ /**
+ * Puts {@link #currentKey} and {@link #currentPath} in the same state as they were before
+ * {@link #startVisit(String, boolean)}.
+ *
+ * @param previousKeyLength Value return by corresponding {@link #startVisit(String, boolean)} invocation.
+ */
+ private void endVisit(int previousKeyLength) {
currentKey.setLength(previousKeyLength);
- return null;
+ if (!writeNulls)
+ currentPath.remove(currentPath.size() - 1);
+ }
+
+ /**
+ * Here we must list all joined keys belonging to deleted element. The only way to do it is to traverse
+ * cached configuration tree and write {@code null} into all values.
+ */
+ private void visitDeletedNamedListElement() {
+ // It must be impossible to delete something inside of the element that we're currently deleting.
+ assert !writeNulls;
+
+ Object originalNamedElement = null;
+
+ try {
+ // This code can in fact be better optimized for deletion scenario,
+ // but there's no point in doing that, since the operation is so rare and it will
+ // complicate code even more.
+ originalNamedElement = find(currentPath.subList(1, currentPath.size()), curRoot);
+ }
+ catch (KeyNotFoundException ignore) {
+ // May happen, not a big deal. This means that element never existed in the first place.
+ }
+
+ if (originalNamedElement != null) {
+ assert originalNamedElement instanceof InnerNode : currentPath;
+
+ writeNulls = true;
+
+ ((InnerNode)originalNamedElement).traverseChildren(this);
+
+ writeNulls = false;
+ }
}
});
- return values;
}
/**
diff --git a/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ITScaleCubeNetworkClusterMessagingTest.java b/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ITScaleCubeNetworkClusterMessagingTest.java
index 89e6376..67e8977 100644
--- a/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ITScaleCubeNetworkClusterMessagingTest.java
+++ b/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ITScaleCubeNetworkClusterMessagingTest.java
@@ -47,6 +47,8 @@ class ITScaleCubeNetworkClusterMessagingTest {
iterator.remove();
}
+
+ TestNetworkHandlersProvider.MESSAGE_STORAGE.clear();
}
/** */