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/02/12 13:44:55 UTC

[ignite-3] branch main updated: IGNITE-14145 ConfigurationUtil moved to other package; Named lists enhancements; minor refactoring. (#51)

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 8d4430d  IGNITE-14145 ConfigurationUtil moved to other package; Named lists enhancements; minor refactoring. (#51)
8d4430d is described below

commit 8d4430de7aa933d0108cc85f51514a35edfb59e9
Author: ibessonov <be...@gmail.com>
AuthorDate: Fri Feb 12 16:44:49 2021 +0300

    IGNITE-14145 ConfigurationUtil moved to other package; Named lists enhancements; minor refactoring. (#51)
---
 modules/configuration-annotation-processor/pom.xml |  1 -
 .../internal/TestConfigurationSchema.java          |  4 +-
 .../processor/internal/Processor.java              | 40 ++++++++-------
 .../{ => internal}/util/ConfigurationUtilTest.java | 20 ++++----
 .../sample/ClusterWideConfigurationSchema.java     |  7 ++-
 .../sample/ConstructableTreeNodeTest.java          |  4 +-
 .../sample/LocalConfigurationSchema.java           |  5 +-
 .../sample/NetworkConfigurationSchema.java         |  6 +--
 .../sample/TraversableTreeNodeTest.java            | 52 +++++++++----------
 .../sample/storage/ConfigurationChangerTest.java   | 14 ++---
 .../ignite/configuration/ConfigurationChanger.java | 18 ++++---
 .../ignite/configuration/annotation/Config.java    | 10 ----
 .../annotation/ConfigurationRoot.java              | 43 ++++++++++++++++
 .../configuration/internal/DynamicProperty.java    |  2 +-
 .../{ => internal}/util/ConfigurationUtil.java     | 59 +++++++++++-----------
 .../{ => internal}/util/KeyNotFoundException.java  |  2 +-
 .../configuration/tree/ConfigurationVisitor.java   | 11 ++--
 .../ignite/configuration/tree/InnerNode.java       |  6 +--
 .../ignite/configuration/tree/NamedListChange.java | 10 ++--
 ...TraversableTreeNode.java => NamedListInit.java} | 13 +++--
 .../ignite/configuration/tree/NamedListNode.java   | 23 +++++++--
 .../configuration/tree/TraversableTreeNode.java    |  2 +-
 .../configuration/RestConfigurationSchema.java     |  4 +-
 .../extended/AutoAdjustConfigurationSchema.java    |  2 +-
 .../extended/LocalConfigurationSchema.java         |  4 +-
 25 files changed, 210 insertions(+), 152 deletions(-)

diff --git a/modules/configuration-annotation-processor/pom.xml b/modules/configuration-annotation-processor/pom.xml
index f74d5fd..be4a44b 100644
--- a/modules/configuration-annotation-processor/pom.xml
+++ b/modules/configuration-annotation-processor/pom.xml
@@ -89,7 +89,6 @@
             <artifactId>junit-jupiter-engine</artifactId>
             <scope>test</scope>
         </dependency>
-
     </dependencies>
 
     <build>
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 682f952..7847c4f 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
@@ -17,10 +17,10 @@
 
 package org.apache.ignite.configuration.processor.internal;
 
-import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
 
-@Config(value = "test", root = true)
+@ConfigurationRoot(rootName = "test")
 public class TestConfigurationSchema {
     @Value
     private String value1;
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 ef64d72..fe46cd9 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
@@ -26,13 +26,13 @@ import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
 import com.squareup.javapoet.WildcardTypeName;
 import java.io.IOException;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -62,6 +62,7 @@ import org.apache.ignite.configuration.Configurator;
 import org.apache.ignite.configuration.RootKey;
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
+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.internal.DynamicConfiguration;
@@ -78,6 +79,7 @@ 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.NamedListChange;
+import org.apache.ignite.configuration.tree.NamedListInit;
 import org.apache.ignite.configuration.tree.NamedListNode;
 import org.apache.ignite.configuration.tree.NamedListView;
 
@@ -145,7 +147,8 @@ public class Processor extends AbstractProcessor {
         String packageForUtil = "";
 
         // All classes annotated with @Config
-        final Set<TypeElement> annotatedConfigs = roundEnvironment.getElementsAnnotatedWith(Config.class).stream()
+        final Set<TypeElement> annotatedConfigs = roundEnvironment
+            .getElementsAnnotatedWithAny(Set.of(ConfigurationRoot.class, Config.class)).stream()
             .filter(element -> element.getKind() == ElementKind.CLASS)
             .map(TypeElement.class::cast)
             .collect(Collectors.toSet());
@@ -164,12 +167,13 @@ public class Processor extends AbstractProcessor {
                 .map(VariableElement.class::cast)
                 .collect(Collectors.toList());
 
-            final Config classConfigAnnotation = clazz.getAnnotation(Config.class);
+            ConfigurationRoot rootAnnotation = clazz.getAnnotation(ConfigurationRoot.class);
 
-            // Configuration name
-            final String configName = classConfigAnnotation.value();
             // Is root of the configuration
-            final boolean isRoot = classConfigAnnotation.root();
+            final boolean isRoot = rootAnnotation != null;
+            // Configuration name
+            final String configName = isRoot ? rootAnnotation.rootName() : null;
+
             final ClassName schemaClassName = ClassName.get(packageName, clazz.getSimpleName().toString());
 
             // Get name for generated configuration class and it's interface
@@ -866,14 +870,17 @@ public class Processor extends AbstractProcessor {
             .returns(TypeName.VOID)
             .addParameter(ClassName.get(ConfigurationVisitor.class), "visitor");
 
+        TypeVariableName t = TypeVariableName.get("T");
+
         MethodSpec.Builder traverseChildBuilder = MethodSpec.methodBuilder("traverseChild")
             .addAnnotation(Override.class)
             .addJavadoc("{@inheritDoc}")
             .addModifiers(PUBLIC)
-            .returns(TypeName.VOID)
+            .addTypeVariable(t)
+            .returns(t)
             .addException(NoSuchElementException.class)
             .addParameter(ClassName.get(String.class), "key")
-            .addParameter(ClassName.get(ConfigurationVisitor.class), "visitor")
+            .addParameter(ParameterizedTypeName.get(ClassName.get(ConfigurationVisitor.class), t), "visitor")
             .beginControlFlow("switch (key)");
 
         MethodSpec.Builder constructBuilder = MethodSpec.methodBuilder("construct")
@@ -922,11 +929,11 @@ public class Processor extends AbstractProcessor {
             if (namedListField) {
                 viewFieldType = ParameterizedTypeName.get(ClassName.get(NamedListView.class), WildcardTypeName.subtypeOf(viewFieldType));
 
-                changeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListChange.class), changeFieldType);
+                nodeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListNode.class), nodeFieldType);
 
-                initFieldType = ParameterizedTypeName.get(ClassName.get(NamedListChange.class), initFieldType);
+                changeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListChange.class), changeFieldType, initFieldType);
 
-                nodeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListNode.class), nodeFieldType);
+                initFieldType = ParameterizedTypeName.get(ClassName.get(NamedListInit.class), initFieldType);
             }
 
             {
@@ -1080,22 +1087,19 @@ public class Processor extends AbstractProcessor {
                     traverseChildrenBuilder.addStatement("visitor.visitLeafNode($S, $L)", fieldName, fieldName);
 
                     traverseChildBuilder
-                        .addStatement("case $S: visitor.visitLeafNode($S, $L)", fieldName, fieldName, fieldName)
-                        .addStatement(INDENT + "break");
+                        .addStatement("case $S: return visitor.visitLeafNode($S, $L)", fieldName, fieldName, fieldName);
                 }
                 else if (namedListField) {
                     traverseChildrenBuilder.addStatement("visitor.visitNamedListNode($S, $L)", fieldName, fieldName);
 
                     traverseChildBuilder
-                        .addStatement("case $S: visitor.visitNamedListNode($S, $L)", fieldName, fieldName, fieldName)
-                        .addStatement(INDENT + "break");
+                        .addStatement("case $S: return visitor.visitNamedListNode($S, $L)", fieldName, fieldName, fieldName);
                 }
                 else {
                     traverseChildrenBuilder.addStatement("visitor.visitInnerNode($S, $L)", fieldName, fieldName);
 
                     traverseChildBuilder
-                        .addStatement("case $S: visitor.visitInnerNode($S, $L)", fieldName, fieldName, fieldName)
-                        .addStatement(INDENT + "break");
+                        .addStatement("case $S: return visitor.visitInnerNode($S, $L)", fieldName, fieldName, fieldName);
                 }
             }
 
@@ -1337,7 +1341,7 @@ public class Processor extends AbstractProcessor {
 
     /** {@inheritDoc} */
     @Override public Set<String> getSupportedAnnotationTypes() {
-        return Collections.singleton(Config.class.getCanonicalName());
+        return Set.of(Config.class.getCanonicalName(), ConfigurationRoot.class.getCanonicalName());
     }
 
     /** {@inheritDoc} */
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
similarity index 93%
rename from modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java
rename to modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
index 6b12e28..c88e5eb 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.configuration.util;
+package org.apache.ignite.configuration.internal.util;
 
 import java.util.List;
 import java.util.Map;
@@ -23,7 +23,7 @@ import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.configuration.util.impl.ParentNode;
+import org.apache.ignite.configuration.internal.util.impl.ParentNode;
 import org.junit.jupiter.api.Test;
 
 import static java.util.Collections.singletonMap;
@@ -100,7 +100,7 @@ public class ConfigurationUtilTest {
     @Test
     public void findSuccessfully() {
         var parent = new ParentNode().changeElements(elements ->
-            elements.put("name", element ->
+            elements.update("name", element ->
                 element.changeChild(child ->
                     child.changeStr("value")
                 )
@@ -140,7 +140,7 @@ public class ConfigurationUtilTest {
 
         assertNull(ConfigurationUtil.find(List.of("elements", "name"), parent));
 
-        parent.changeElements(elements -> elements.put("name", element -> {}));
+        parent.changeElements(elements -> elements.update("name", element -> {}));
 
         assertNull(ConfigurationUtil.find(List.of("elements", "name", "child"), parent));
 
@@ -159,7 +159,7 @@ public class ConfigurationUtilTest {
             () -> ConfigurationUtil.find(List.of("elements", "name", "child"), parent)
         );
 
-        parent.changeElements(elements -> elements.put("name", element -> {}));
+        parent.changeElements(elements -> elements.update("name", element -> {}));
 
         assertThrows(
             KeyNotFoundException.class,
@@ -198,7 +198,7 @@ public class ConfigurationUtilTest {
     @Test
     public void fillFromSuffixMapSuccessfullyWithRemove() {
         var parentNode = new ParentNode().changeElements(elements ->
-            elements.put("name", element ->
+            elements.update("name", element ->
                 element.changeChild(child -> {})
             )
         );
@@ -214,14 +214,14 @@ public class ConfigurationUtilTest {
     @Test
     public void patch() {
         var originalRoot = new ParentNode().initElements(elements ->
-            elements.put("name1", element ->
+            elements.create("name1", element ->
                 element.initChild(child -> child.initStr("value1"))
             )
         );
 
         // Updating config.
         ParentNode updatedRoot = ConfigurationUtil.patch(originalRoot, new ParentNode().changeElements(elements ->
-            elements.put("name1", element ->
+            elements.update("name1", element ->
                 element.changeChild(child -> child.changeStr("value2"))
             )
         ));
@@ -236,7 +236,7 @@ public class ConfigurationUtilTest {
 
         // Expanding config.
         ParentNode expandedRoot = ConfigurationUtil.patch(originalRoot, new ParentNode().changeElements(elements ->
-            elements.put("name2", element ->
+            elements.update("name2", element ->
                 element.changeChild(child -> child.changeStr("value2"))
             )
         ));
@@ -252,7 +252,7 @@ public class ConfigurationUtilTest {
 
         // Shrinking config.
         ParentNode shrinkedRoot = ConfigurationUtil.patch(expandedRoot, new ParentNode().changeElements(elements ->
-            elements.remove("name1")
+            elements.delete("name1")
         ));
 
         assertNotSame(expandedRoot, shrinkedRoot);
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ClusterWideConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ClusterWideConfigurationSchema.java
index 8594c87..e2742df 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ClusterWideConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ClusterWideConfigurationSchema.java
@@ -17,21 +17,20 @@
 
 package org.apache.ignite.configuration.sample;
 
-import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
 
 /**
  * Test cluster wide configuration schema.
  */
-@Config(value = "cluster", root = true)
+@ConfigurationRoot(rootName = "cluster")
 public class ClusterWideConfigurationSchema {
     /** Cache. */
     @NamedConfigValue
-    CacheConfigurationSchema cacheConfig;
+    private CacheConfigurationSchema cacheConfig;
 
     /** Baseline. */
     @ConfigValue
     private BaselineConfigurationSchema baseline;
-
 }
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 9791637..e1e8421 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
@@ -50,7 +50,7 @@ public class ConstructableTreeNodeTest {
             child.initStrCfg("value")
         )
         .initElements(elements ->
-            elements.put("name", element -> {})
+            elements.create("name", element -> {})
         );
 
         // Named list node.
@@ -89,7 +89,7 @@ public class ConstructableTreeNodeTest {
 
         /** {@inheritDoc} */
         @Override public <T> T unwrap(Class<T> clazz) {
-            return (T)constant;
+            return clazz.cast(constant);
         }
     }
 
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/LocalConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/LocalConfigurationSchema.java
index 6f6cf7f..2d565e4 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/LocalConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/LocalConfigurationSchema.java
@@ -17,16 +17,15 @@
 
 package org.apache.ignite.configuration.sample;
 
-import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 
 /**
  * Test local configuration schema.
  */
-@Config(value = "local", root = true)
+@ConfigurationRoot(rootName = "local")
 public class LocalConfigurationSchema {
     /** Baseline. */
     @ConfigValue
     private BaselineConfigurationSchema baseline;
-
 }
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NetworkConfigurationSchema.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NetworkConfigurationSchema.java
index 8f2fb7f..f2facb6 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NetworkConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/NetworkConfigurationSchema.java
@@ -17,17 +17,15 @@
 
 package org.apache.ignite.configuration.sample;
 
-import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 
 /**
  * Test network configuration schema.
  */
-@Config(value = "network", root = true)
+@ConfigurationRoot(rootName = "network")
 public class NetworkConfigurationSchema {
     /** Discovery. */
     @ConfigValue
     private DiscoveryConfigurationSchema discovery;
-
-
 }
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 393f17c..214a173 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
@@ -154,7 +154,7 @@ public class TraversableTreeNodeTest {
         // Named list node must always be instantiated.
         assertNotNull(elementsNode);
 
-        parentNode.changeElements(elements -> elements.put("key", element -> {}));
+        parentNode.changeElements(elements -> elements.update("key", element -> {}));
 
         // Assert that change method applied its closure to the same object instead of creating a new one.
         assertSame(elementsNode, parentNode.elements());
@@ -194,7 +194,7 @@ public class TraversableTreeNodeTest {
 
         NamedListNode<NamedElementNode> elementsNode = parentNode.elements();
 
-        parentNode.initElements(elements -> elements.put("key", element -> {}));
+        parentNode.initElements(elements -> elements.create("key", element -> {}));
 
         // Assert that change method applied its closure to the same object instead of creating a new one.
         assertSame(elementsNode, parentNode.elements());
@@ -209,7 +209,7 @@ public class TraversableTreeNodeTest {
 
         assertEquals(emptySet(), elementsNode.namedListKeys());
 
-        elementsNode.put("keyPut", element -> {});
+        elementsNode.update("keyPut", element -> {});
 
         assertThat(elementsNode.namedListKeys(), hasItem("keyPut"));
 
@@ -219,7 +219,7 @@ public class TraversableTreeNodeTest {
 
         assertNull(elementNode.strCfg());
 
-        elementsNode.put("keyPut", element -> element.changeStrCfg("val"));
+        elementsNode.update("keyPut", element -> element.changeStrCfg("val"));
 
         // Assert that consecutive put methods don't create new object every time.
         assertSame(elementNode, elementsNode.get("keyPut"));
@@ -227,9 +227,9 @@ public class TraversableTreeNodeTest {
         assertEquals("val", elementNode.strCfg());
 
         // Assert that once you put something into list, removing it makes no sense and hence prohibited.
-        assertThrows(IllegalStateException.class, () -> elementsNode.remove("keyPut"));
+        assertThrows(IllegalStateException.class, () -> elementsNode.delete("keyPut"));
 
-        elementsNode.remove("keyRemove");
+        elementsNode.delete("keyRemove");
 
         // Assert that "remove" method creates null element inside of the node.
         assertThat(elementsNode.namedListKeys(), hasItem("keyRemove"));
@@ -237,7 +237,7 @@ public class TraversableTreeNodeTest {
         assertNull(elementsNode.get("keyRemove"));
 
         // Assert that once you remove something from list, you can't put it back again with different set of fields.
-        assertThrows(IllegalStateException.class, () -> elementsNode.put("keyRemove", element -> {}));
+        assertThrows(IllegalStateException.class, () -> elementsNode.update("keyRemove", element -> {}));
     }
 
     /**
@@ -248,8 +248,8 @@ public class TraversableTreeNodeTest {
         var parentNode = new ParentNode();
 
         assertThrows(VisitException.class, () ->
-            parentNode.accept("root", new ConfigurationVisitor() {
-                @Override public void visitInnerNode(String key, InnerNode node) {
+            parentNode.accept("root", new ConfigurationVisitor<Void>() {
+                @Override public Void visitInnerNode(String key, InnerNode node) {
                     throw new VisitException();
                 }
             })
@@ -264,8 +264,8 @@ public class TraversableTreeNodeTest {
         var elementsNode = new NamedListNode<>(NamedElementNode::new);
 
         assertThrows(VisitException.class, () ->
-            elementsNode.accept("root", new ConfigurationVisitor() {
-                @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+            elementsNode.accept("root", new ConfigurationVisitor<Void>() {
+                @Override public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> node) {
                     throw new VisitException();
                 }
             })
@@ -281,19 +281,19 @@ public class TraversableTreeNodeTest {
 
         List<String> keys = new ArrayList<>(2);
 
-        parentNode.traverseChildren(new ConfigurationVisitor() {
-            @Override public void visitInnerNode(String key, InnerNode node) {
+        parentNode.traverseChildren(new ConfigurationVisitor<Object>() {
+            @Override public Object visitInnerNode(String key, InnerNode node) {
                 assertNull(node);
 
                 assertEquals("child", key);
 
-                keys.add(key);
+                return keys.add(key);
             }
 
-            @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+            @Override public <N extends InnerNode> Object visitNamedListNode(String key, NamedListNode<N> node) {
                 assertEquals("elements", key);
 
-                keys.add(key);
+                return keys.add(key);
             }
         });
 
@@ -304,9 +304,9 @@ public class TraversableTreeNodeTest {
 
         ChildNode childNode = new ChildNode();
 
-        childNode.traverseChildren(new ConfigurationVisitor() {
-            @Override public void visitLeafNode(String key, Serializable val) {
-                keys.add(key);
+        childNode.traverseChildren(new ConfigurationVisitor<Object>() {
+            @Override public Object visitLeafNode(String key, Serializable val) {
+                return keys.add(key);
             }
         });
 
@@ -323,8 +323,8 @@ public class TraversableTreeNodeTest {
 
         // Assert that proper method has been invoked.
         assertThrows(VisitException.class, () ->
-            parentNode.traverseChild("child", new ConfigurationVisitor() {
-                @Override public void visitInnerNode(String key, InnerNode node) {
+            parentNode.traverseChild("child", new ConfigurationVisitor<Void>() {
+                @Override public Void visitInnerNode(String key, InnerNode node) {
                     assertEquals("child", key);
 
                     throw new VisitException();
@@ -334,9 +334,9 @@ public class TraversableTreeNodeTest {
 
         // Assert that proper method has been invoked.
         assertThrows(VisitException.class, () ->
-            parentNode.traverseChild("elements", new ConfigurationVisitor() {
+            parentNode.traverseChild("elements", new ConfigurationVisitor<Void>() {
                 @Override
-                public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+                public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> node) {
                     assertEquals("elements", key);
 
                     throw new VisitException();
@@ -348,8 +348,8 @@ public class TraversableTreeNodeTest {
 
         // Assert that proper method has been invoked.
         assertThrows(VisitException.class, () ->
-            childNode.traverseChild("intCfg", new ConfigurationVisitor() {
-                @Override public void visitLeafNode(String key, Serializable val) {
+            childNode.traverseChild("intCfg", new ConfigurationVisitor<Void>() {
+                @Override public Void visitLeafNode(String key, Serializable val) {
                     assertEquals("intCfg", key);
 
                     throw new VisitException();
@@ -359,7 +359,7 @@ public class TraversableTreeNodeTest {
 
         // Assert that traversing inexistent field leads to exception.
         assertThrows(NoSuchElementException.class, () ->
-            childNode.traverseChild("foo", new ConfigurationVisitor() {})
+            childNode.traverseChild("foo", new ConfigurationVisitor<>() {})
         );
     }
 }
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 9eab60f..8460862 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
@@ -90,7 +90,7 @@ public class ConfigurationChangerTest {
 
         ANode data = new ANode()
             .initChild(init -> init.initIntCfg(1).initStrCfg("1"))
-            .initElements(change -> change.put("a", init -> init.initStrCfg("1")));
+            .initElements(change -> change.create("a", init -> init.initStrCfg("1")));
 
         final ConfigurationChanger changer = new ConfigurationChanger(storage);
         changer.init();
@@ -120,13 +120,13 @@ public class ConfigurationChangerTest {
 
         ANode data1 = new ANode()
             .initChild(init -> init.initIntCfg(1).initStrCfg("1"))
-            .initElements(change -> change.put("a", init -> init.initStrCfg("1")));
+            .initElements(change -> change.create("a", init -> init.initStrCfg("1")));
 
         ANode data2 = new ANode()
             .initChild(init -> init.initIntCfg(2).initStrCfg("2"))
             .initElements(change -> change
-                .put("a", init -> init.initStrCfg("2"))
-                .put("b", init -> init.initStrCfg("2"))
+                .create("a", init -> init.initStrCfg("2"))
+                .create("b", init -> init.initStrCfg("2"))
             );
 
         final ConfigurationChanger changer1 = new ConfigurationChanger(storage);
@@ -163,13 +163,13 @@ public class ConfigurationChangerTest {
 
         ANode data1 = new ANode()
             .initChild(init -> init.initIntCfg(1).initStrCfg("1"))
-            .initElements(change -> change.put("a", init -> init.initStrCfg("1")));
+            .initElements(change -> change.create("a", init -> init.initStrCfg("1")));
 
         ANode data2 = new ANode()
             .initChild(init -> init.initIntCfg(2).initStrCfg("2"))
             .initElements(change -> change
-                .put("a", init -> init.initStrCfg("2"))
-                .put("b", init -> init.initStrCfg("2"))
+                .create("a", init -> init.initStrCfg("2"))
+                .create("b", init -> init.initStrCfg("2"))
             );
 
         final ConfigurationChanger changer1 = new ConfigurationChanger(storage);
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 5c884ce..afcc0e6 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
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 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;
@@ -31,7 +32,6 @@ 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.util.ConfigurationUtil;
 import org.apache.ignite.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.configuration.validation.ValidationIssue;
 
@@ -160,19 +160,21 @@ public class ConfigurationChanger {
     private Map<String, Serializable> convertChangesToMap(RootKey<?> rootKey, TraversableTreeNode node) {
         Map<String, Serializable> values = new HashMap<>();
 
-        node.accept(rootKey.key(), new ConfigurationVisitor() {
+        node.accept(rootKey.key(), new ConfigurationVisitor<>() {
             /** Current key, aggregated by visitor. */
             StringBuilder currentKey = new StringBuilder();
 
             /** {@inheritDoc} */
-            @Override public void visitLeafNode(String key, Serializable val) {
+            @Override public Void visitLeafNode(String key, Serializable val) {
                 values.put(currentKey.toString() + key, val);
+
+                return null;
             }
 
             /** {@inheritDoc} */
-            @Override public void visitInnerNode(String key, InnerNode node) {
+            @Override public Void visitInnerNode(String key, InnerNode node) {
                 if (node == null)
-                    return;
+                    return null;
 
                 int previousKeyLength = currentKey.length();
 
@@ -181,10 +183,12 @@ public class ConfigurationChanger {
                 node.traverseChildren(this);
 
                 currentKey.setLength(previousKeyLength);
+
+                return null;
             }
 
             /** {@inheritDoc} */
-            @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+            @Override public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> node) {
                 int previousKeyLength = currentKey.length();
 
                 if (key != null)
@@ -201,6 +205,8 @@ public class ConfigurationChanger {
                 }
 
                 currentKey.setLength(previousKeyLength);
+
+                return null;
             }
         });
         return values;
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Config.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Config.java
index 6328d34..70669ca 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Config.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Config.java
@@ -49,7 +49,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
  *
  *      {@literal @}ConfigValue
  *      private SomeOtherConfiguration someOther;
- *
  * }
  * </pre>
  */
@@ -57,13 +56,4 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
 @Retention(SOURCE)
 @Documented
 public @interface Config {
-    /**
-     * @return The name of the configuration.
-     */
-    String value() default "";
-
-    /**
-     * @return {@code true } if marked class is the root of the configuration schema.
-     */
-    boolean root() default false;
 }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
new file mode 100644
index 0000000..6406f6d
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
@@ -0,0 +1,43 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.apache.ignite.configuration.storage.ConfigurationStorage;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Annotation that marks underlying class as a root configuration schema. Has basically the same properties as
+ * {@link Config} + adds extra properties.
+ *
+ * @see Config
+ */
+@Target(TYPE)
+@Retention(SOURCE)
+@Documented
+public @interface ConfigurationRoot {
+    /** @return Unique root name. */
+    String rootName();
+
+    /** @return Class of storage where to store configuration of the given root. */
+    Class<? extends ConfigurationStorage> storage() default ConfigurationStorage.class;
+}
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 189eea7..591a294 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
@@ -24,9 +24,9 @@ import org.apache.ignite.configuration.ConfigurationValue;
 import org.apache.ignite.configuration.Configurator;
 import org.apache.ignite.configuration.PropertyListener;
 import org.apache.ignite.configuration.internal.selector.BaseSelectors;
+import org.apache.ignite.configuration.internal.validation.MemberKey;
 import org.apache.ignite.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.configuration.validation.FieldValidator;
-import org.apache.ignite.configuration.internal.validation.MemberKey;
 
 /**
  * Holder for property value. Expected to be used with numbers, strings and other immutable objects, e.g. IP addresses.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
similarity index 86%
rename from modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java
rename to modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
index 1fb8a73..23e84f0 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.configuration.util;
+package org.apache.ignite.configuration.internal.util;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -93,28 +93,25 @@ public class ConfigurationUtil {
     public static Object find(List<String> keys, TraversableTreeNode node) throws KeyNotFoundException {
         assert keys instanceof RandomAccess : keys.getClass();
 
-        var visitor = new ConfigurationVisitor() {
+        var visitor = new ConfigurationVisitor<>() {
             /** */
             private int i;
 
-            /** */
-            private Object res;
-
-            @Override public void visitLeafNode(String key, Serializable val) {
+            @Override public Object visitLeafNode(String key, Serializable val) {
                 if (i != keys.size())
                     throw new KeyNotFoundException("Configuration value '" + join(keys.subList(0, i)) + "' is a leaf");
 
-                res = val;
+                return val;
             }
 
-            @Override public void visitInnerNode(String key, InnerNode node) {
+            @Override public Object visitInnerNode(String key, InnerNode node) {
                 if (i == keys.size())
-                    res = node;
+                    return node;
                 else if (node == null)
                     throw new KeyNotFoundException("Configuration node '" + join(keys.subList(0, i)) + "' is null");
                 else {
                     try {
-                        node.traverseChild(keys.get(i++), this);
+                        return node.traverseChild(keys.get(i++), this);
                     }
                     catch (NoSuchElementException e) {
                         throw new KeyNotFoundException("Configuration '" + join(keys.subList(0, i)) + "' is not found");
@@ -122,20 +119,18 @@ public class ConfigurationUtil {
                 }
             }
 
-            @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+            @Override public <N extends InnerNode> Object visitNamedListNode(String key, NamedListNode<N> node) {
                 if (i == keys.size())
-                    res = node;
+                    return node;
                 else {
                     String name = keys.get(i++);
 
-                    visitInnerNode(name, node.get(name));
+                    return visitInnerNode(name, node.get(name));
                 }
             }
         };
 
-        node.accept(null, visitor);
-
-        return visitor.res;
+        return node.accept(null, visitor);
     }
 
     /**
@@ -225,25 +220,23 @@ public class ConfigurationUtil {
     public static <C extends ConstructableTreeNode> C patch(C root, TraversableTreeNode changes) {
         assert root.getClass() == changes.getClass(); // Yes.
 
-        var scrHolder = new ConfigurationVisitor() {
-            ConfigurationSource src;
-
-            @Override public void visitInnerNode(String key, InnerNode node) {
-                src = new PatchInnerConfigurationSource(node);
+        var scrVisitor = new ConfigurationVisitor<ConfigurationSource>() {
+            @Override public ConfigurationSource visitInnerNode(String key, InnerNode node) {
+                return new PatchInnerConfigurationSource(node);
             }
 
-            @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
-                src = new PatchNamedListConfigurationSource(node);
+            @Override public <N extends InnerNode> ConfigurationSource visitNamedListNode(String key, NamedListNode<N> node) {
+                return new PatchNamedListConfigurationSource(node);
             }
         };
 
-        changes.accept(null, scrHolder);
+        ConfigurationSource src = changes.accept(null, scrVisitor);
 
-        assert scrHolder.src != null;
+        assert src != null;
 
         C copy = (C)root.copy();
 
-        scrHolder.src.descend(copy);
+        src.descend(copy);
 
         return copy;
     }
@@ -294,20 +287,26 @@ public class ConfigurationUtil {
         @Override public void descend(ConstructableTreeNode dstNode) {
             assert srcNode.getClass() == dstNode.getClass();
 
-            srcNode.traverseChildren(new ConfigurationVisitor() {
-                @Override public void visitLeafNode(String key, Serializable val) {
+            srcNode.traverseChildren(new ConfigurationVisitor<>() {
+                @Override public Void visitLeafNode(String key, Serializable val) {
                     if (val != null)
                         dstNode.construct(key, new PatchLeafConfigurationSource(val));
+
+                    return null;
                 }
 
-                @Override public void visitInnerNode(String key, InnerNode node) {
+                @Override public Void visitInnerNode(String key, InnerNode node) {
                     if (node != null)
                         dstNode.construct(key, new PatchInnerConfigurationSource(node));
+
+                    return null;
                 }
 
-                @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+                @Override public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> node) {
                     if (node != null)
                         dstNode.construct(key, new PatchNamedListConfigurationSource(node));
+
+                    return null;
                 }
             });
         }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/KeyNotFoundException.java
similarity index 95%
rename from modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java
rename to modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/KeyNotFoundException.java
index 90c605f..cc3f07a 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/KeyNotFoundException.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.configuration.util;
+package org.apache.ignite.configuration.internal.util;
 
 /** */
 public class KeyNotFoundException extends RuntimeException {
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
index b9c70c8..bf2e697 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
@@ -22,14 +22,15 @@ import java.io.Serializable;
 /**
  * Configuration visitor - callback interface to traverse configuration tree.
  */
-public interface ConfigurationVisitor {
+public interface ConfigurationVisitor<T> {
     /**
      * Invoked on visiting leaf node.
      *
      * @param key Name of the serializable value retrieved from its holder object.
      * @param val Configuration value.
      */
-    default void visitLeafNode(String key, Serializable val) {
+    default T visitLeafNode(String key, Serializable val) {
+        return null;
     }
 
     /**
@@ -38,7 +39,8 @@ public interface ConfigurationVisitor {
      * @param key Name of the node retrieved from its holder object.
      * @param node Inner configuration node.
      */
-    default void visitInnerNode(String key, InnerNode node) {
+    default T visitInnerNode(String key, InnerNode node) {
+        return null;
     }
 
     /**
@@ -47,6 +49,7 @@ public interface ConfigurationVisitor {
      * @param key Name of the node retrieved from its holder object.
      * @param node Named list inner configuration node.
      */
-    default <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+    default <N extends InnerNode> T visitNamedListNode(String key, NamedListNode<N> node) {
+        return null;
     }
 }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
index 84e440c..df45b42 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
@@ -22,8 +22,8 @@ import java.util.NoSuchElementException;
 /** */
 public abstract class InnerNode implements TraversableTreeNode, ConstructableTreeNode, Cloneable {
     /** {@inheritDoc} */
-    @Override public final void accept(String key, ConfigurationVisitor visitor) {
-        visitor.visitInnerNode(key, this);
+    @Override public final <T> T accept(String key, ConfigurationVisitor<T> visitor) {
+        return visitor.visitInnerNode(key, this);
     }
 
     /**
@@ -77,7 +77,7 @@ public abstract class InnerNode implements TraversableTreeNode, ConstructableTre
      * @param visitor Configuration visitor.
      * @throws NoSuchElementException If field {@code key} is not found.
      */
-    public abstract void traverseChild(String key, ConfigurationVisitor visitor) throws NoSuchElementException;
+    public abstract <T> T traverseChild(String key, ConfigurationVisitor<T> visitor) throws NoSuchElementException;
 
     /**
      * Method with auto-generated implementation. Must look like this:
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 b386718..1dc15f6 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
@@ -20,7 +20,7 @@ package org.apache.ignite.configuration.tree;
 import java.util.function.Consumer;
 
 /** */
-public interface NamedListChange<T> {
+public interface NamedListChange<Change, Init> extends NamedListInit<Init> {
     /**
      * Update the value in named list configuration.
      *
@@ -28,16 +28,16 @@ public interface NamedListChange<T> {
      * @param valConsumer Closure to modify value associated with the key. Object of type {@code T},
      *      passed to the closure, must not be reused anywhere else.
      *
-     * @throws IllegalStateException If {@link #remove(String)} has been invoked with the same key previously.
+     * @throws IllegalStateException If {@link #delete(String)} has been invoked with the same key previously.
      */
-    NamedListChange<T> put(String key, Consumer<T> valConsumer) throws IllegalStateException;
+    NamedListChange<Change, Init> update(String key, Consumer<Change> valConsumer) throws IllegalStateException;
 
     /**
      * Remove the value from named list configuration.
      *
      * @param key Key for the value to be removed.
      *
-     * @throws IllegalStateException If {@link #put(String, Consumer)} has been invoked with the same key previously.
+     * @throws IllegalStateException If {@link #update(String, Consumer)} has been invoked with the same key previously.
      */
-    NamedListChange<T> remove(String key) throws IllegalStateException;
+    NamedListChange<Change, Init> delete(String key) throws IllegalStateException;
 }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListInit.java
similarity index 67%
copy from modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java
copy to modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListInit.java
index c234fa9..bfd2ceb 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListInit.java
@@ -17,11 +17,16 @@
 
 package org.apache.ignite.configuration.tree;
 
+import java.util.function.Consumer;
+
 /** */
-public interface TraversableTreeNode {
+public interface NamedListInit<T> {
     /**
-     * @param key Name of the node retrieved from its holder object.
-     * @param visitor Configuration visitor.
+     * Update the value in named list configuration.
+     *
+     * @param key Key for the value to be created.
+     * @param valConsumer Closure to modify value associated with the key. Object of type {@code T},
+     *      passed to the closure, must not be reused anywhere else.
      */
-    void accept(String key, ConfigurationVisitor visitor);
+    NamedListInit<T> create(String key, Consumer<T> 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 648e2cb..41d9061 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
@@ -26,7 +26,7 @@ import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /** */
-public final class NamedListNode<N extends InnerNode> implements NamedListView<N>, NamedListChange<N>, TraversableTreeNode, ConstructableTreeNode {
+public final class NamedListNode<N extends InnerNode> implements NamedListView<N>, NamedListChange<N, N>, TraversableTreeNode, ConstructableTreeNode {
     /** */
     private final Supplier<N> valSupplier;
 
@@ -54,8 +54,8 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
     }
 
     /** {@inheritDoc} */
-    @Override public void accept(String key, ConfigurationVisitor visitor) {
-        visitor.visitNamedListNode(key, this);
+    @Override public <T> T accept(String key, ConfigurationVisitor<T> visitor) {
+        return visitor.visitNamedListNode(key, this);
     }
 
     /** {@inheritDoc} */
@@ -69,7 +69,7 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
     }
 
     /** {@inheritDoc} */
-    @Override public final NamedListChange<N> put(String key, Consumer<N> valConsumer) {
+    @Override public final NamedListChange<N, N> update(String key, Consumer<N> valConsumer) {
         Objects.requireNonNull(valConsumer, "valConsumer");
 
         if (map.containsKey(key) && map.get(key) == null)
@@ -86,7 +86,7 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
     }
 
     /** {@inheritDoc} */
-    @Override public NamedListChange<N> remove(String key) {
+    @Override public NamedListChange<N, N> delete(String key) {
         if (map.containsKey(key) && map.get(key) != null)
             throw new IllegalStateException("You can't add entity that has just been modified [key=" + key + ']');
 
@@ -95,6 +95,19 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
         return this;
     }
 
+    @Override public NamedListInit<N> create(String key, Consumer<N> valConsumer) {
+        Objects.requireNonNull(valConsumer, "valConsumer");
+
+        N val = map.get(key);
+
+        if (val == null)
+            map.put(key, val = valSupplier.get());
+
+        valConsumer.accept(val);
+
+        return this;
+    }
+
     /** {@inheritDoc} */
     @Override public void construct(String key, ConfigurationSource src) {
         if (src == null)
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java
index c234fa9..a52630c 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/TraversableTreeNode.java
@@ -23,5 +23,5 @@ public interface TraversableTreeNode {
      * @param key Name of the node retrieved from its holder object.
      * @param visitor Configuration visitor.
      */
-    void accept(String key, ConfigurationVisitor visitor);
+    <T> T accept(String key, ConfigurationVisitor<T> visitor);
 }
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 4bf88f1..10c5e52 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
@@ -17,14 +17,14 @@
 
 package org.apache.ignite.rest.configuration;
 
-import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
 
 /**
  * Configuration schema for REST endpoint subtree.
  */
 @SuppressWarnings("PMD.UnusedPrivateField")
-@Config(value = "rest", root = true)
+@ConfigurationRoot(rootName = "rest")
 public class RestConfigurationSchema {
     /** */
     @Value
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 a3fac7e..e86df97 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
@@ -23,7 +23,7 @@ import org.apache.ignite.configuration.annotation.Value;
 
 /** */
 @SuppressWarnings("PMD.UnusedPrivateField")
-@Config(value = "auto_adjust")
+@Config
 public class AutoAdjustConfigurationSchema {
     /** */
     @Value
diff --git a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/LocalConfigurationSchema.java b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/LocalConfigurationSchema.java
index e3820ae..bfe9a21 100644
--- a/modules/runner/src/main/java/org/apache/ignite/configuration/extended/LocalConfigurationSchema.java
+++ b/modules/runner/src/main/java/org/apache/ignite/configuration/extended/LocalConfigurationSchema.java
@@ -17,14 +17,14 @@
 
 package org.apache.ignite.configuration.extended;
 
-import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 
 /**
  *
  */
 @SuppressWarnings("PMD.UnusedPrivateField")
-@Config(value = "local", root = true)
+@ConfigurationRoot(rootName = "local")
 public class LocalConfigurationSchema {
     /** */
     @ConfigValue