You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vv...@apache.org on 2022/06/20 11:23:21 UTC

[ignite-3] branch main updated: IGNITE-17148 Support for abstract configuration (#878)

This is an automated email from the ASF dual-hosted git repository.

vveider 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 9c90a8652 IGNITE-17148 Support for abstract configuration (#878)
9c90a8652 is described below

commit 9c90a865246a49b2d6006375d6c0ed09df52e472
Author: Kirill Tkalenko <tk...@yandex.ru>
AuthorDate: Mon Jun 20 14:23:16 2022 +0300

    IGNITE-17148 Support for abstract configuration (#878)
---
 .../processor/AbstractProcessorTest.java           |   8 +-
 ...Test.java => ItConfigurationProcessorTest.java} | 204 ++++++++-
 .../AbstractConfigConfigurationSchema.java}        |  25 +-
 ...ConfigWithInjectedNameConfigurationSchema.java} |  19 +-
 ...ctConfigWithInternalIdConfigurationSchema.java} |  20 +-
 .../AbstractRootConfigConfigurationSchema.java}    |  20 +-
 .../SimpleConfigConfigurationSchema.java}          |  13 +-
 .../SimpleConfigRootConfigurationSchema.java}      |  17 +-
 ...yAbstractConfigurationConfigurationSchema.java} |  16 +-
 ...amesWithAbstractConfigConfigurationSchema.java} |  15 +-
 ...NameWithAbstractConfigConfigurationSchema.java} |  20 +-
 ...alIdWithAbstractConfigConfigurationSchema.java} |  21 +-
 ...yAbstractConfigurationConfigurationSchema.java} |  18 +-
 ...amesWithAbstractConfigConfigurationSchema.java} |  19 +-
 ...edNameInAbstractConfigConfigurationSchema.java} |  19 +-
 ...rnalIdInAbstractConfigConfigurationSchema.java} |  19 +-
 ...ConfigMustContainsNameConfigurationSchema.java} |  22 +-
 ...ibleSchemaAnnotations0ConfigurationSchema.java} |  18 +-
 ...ibleSchemaAnnotations1ConfigurationSchema.java} |  18 +-
 ...ibleSchemaAnnotations2ConfigurationSchema.java} |  18 +-
 ...ibleSchemaAnnotations3ConfigurationSchema.java} |  18 +-
 ...ibleSchemaAnnotations4ConfigurationSchema.java} |  18 +-
 .../MustNotBeSuperClassConfigurationSchema.java}   |  17 +-
 ...otContainPolymorphicIdConfigurationSchema.java} |  19 +-
 .../StaticConstantsConfigurationSchema.java        |   4 +-
 ...{Processor.java => ConfigurationProcessor.java} | 463 ++++++++++++++-------
 ...n.java => ConfigurationProcessorException.java} |  19 +-
 ...Utils.java => ConfigurationProcessorUtils.java} |  59 ++-
 .../services/javax.annotation.processing.Processor |   2 +-
 ...t.java => ConfigurationProcessorUtilsTest.java} |   8 +-
 ...ConfigValue.java => AbstractConfiguration.java} |  21 +-
 .../ignite/configuration/annotation/Config.java    |   2 +-
 .../configuration/annotation/ConfigValue.java      |   2 +-
 .../configuration/annotation/InjectedName.java     |   4 +-
 modules/configuration/README.md                    |  44 +-
 .../asm/ConfigurationAsmGenerator.java             |  40 +-
 .../internal/configuration/tree/InnerNode.java     |   8 +
 .../configuration/util/ConfigurationUtil.java      |   1 -
 .../configuration/validation/ValidationUtil.java   |   2 +-
 .../asm/ConfigurationAsmGeneratorTest.java         |  99 ++++-
 .../ignite/internal/util/CollectionUtils.java      |  62 ++-
 .../ignite/internal/util/CollectionUtilsTest.java  |  90 +++-
 42 files changed, 1088 insertions(+), 463 deletions(-)

diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/AbstractProcessorTest.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/AbstractProcessorTest.java
index 856b13c81..c7a5987be 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/AbstractProcessorTest.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/AbstractProcessorTest.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.internal.configuration.processor;
 
 import static com.google.testing.compile.Compiler.javac;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getChangeName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getViewName;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
@@ -47,7 +49,7 @@ public class AbstractProcessorTest {
 
         List<JavaFileObject> fileObjects = fileNames.stream().map(JavaFileObjects::forResource).collect(Collectors.toList());
 
-        return javac().withProcessors(new Processor()).compile(fileObjects);
+        return javac().withProcessors(new ConfigurationProcessor()).compile(fileObjects);
     }
 
     /**
@@ -68,8 +70,8 @@ public class AbstractProcessorTest {
      * @return ConfigSet.
      */
     protected static ConfigSet getConfigSet(ClassName clazz, final Map<ClassName, JavaFileObject> generatedClasses) {
-        final ClassName viewName = Utils.getViewName(clazz);
-        final ClassName changeName = Utils.getChangeName(clazz);
+        final ClassName viewName = getViewName(clazz);
+        final ClassName changeName = getChangeName(clazz);
 
         final JavaFileObject viewClass = generatedClasses.get(viewName);
         final JavaFileObject changeClass = generatedClasses.get(changeName);
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItProcessorTest.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItConfigurationProcessorTest.java
similarity index 65%
rename from modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItProcessorTest.java
rename to modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItConfigurationProcessorTest.java
index 7d10716c5..11e696531 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItProcessorTest.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ItConfigurationProcessorTest.java
@@ -18,8 +18,12 @@
 package org.apache.ignite.internal.configuration.processor;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getChangeName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getConfigurationInterfaceName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getViewName;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.matchesRegex;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -29,6 +33,7 @@ import com.google.testing.compile.Compilation;
 import com.squareup.javapoet.ClassName;
 import java.util.Arrays;
 import java.util.List;
+import java.util.regex.Pattern;
 import org.apache.ignite.configuration.annotation.InjectedName;
 import org.apache.ignite.configuration.annotation.InternalId;
 import org.apache.ignite.configuration.annotation.Name;
@@ -41,7 +46,7 @@ import org.opentest4j.AssertionFailedError;
 /**
  * Test for basic code generation scenarios.
  */
-public class ItProcessorTest extends AbstractProcessorTest {
+public class ItConfigurationProcessorTest extends AbstractProcessorTest {
     /**
      * The simplest test for code generation.
      */
@@ -255,9 +260,9 @@ public class ItProcessorTest extends AbstractProcessorTest {
         assertThat(compilation).succeededWithoutWarnings();
 
         var generatedClasses = List.of(
-                Utils.getViewName(schema).toString(),
-                Utils.getChangeName(schema).toString(),
-                Utils.getConfigurationInterfaceName(schema).toString()
+                getViewName(schema).toString(),
+                getChangeName(schema).toString(),
+                getConfigurationInterfaceName(schema).toString()
         );
 
         for (String generatedClass : generatedClasses) {
@@ -427,11 +432,186 @@ public class ItProcessorTest extends AbstractProcessorTest {
         assertTrue(batchCompile.getBySchema(cls).allGenerated());
     }
 
+    @Test
+    void testFailValidationForAbstractConfiguration() {
+        String packageName = "org.apache.ignite.internal.configuration.processor.abstractconfig.validation";
+
+        // Let's check the incompatible configuration schema annotations.
+
+        Pattern incompatibleAnotationsPattern = Pattern.compile(
+                ".*ConfigurationProcessorException: Class with @.* is not allowed with @.*"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "IncompatibleSchemaAnnotations0ConfigurationSchema"),
+                incompatibleAnotationsPattern
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "IncompatibleSchemaAnnotations1ConfigurationSchema"),
+                incompatibleAnotationsPattern
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "IncompatibleSchemaAnnotations2ConfigurationSchema"),
+                incompatibleAnotationsPattern
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "IncompatibleSchemaAnnotations3ConfigurationSchema"),
+                incompatibleAnotationsPattern
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "IncompatibleSchemaAnnotations4ConfigurationSchema"),
+                incompatibleAnotationsPattern
+        );
+
+        // Let's check other validations for abstract configuration.
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "MustNotBeSuperClassConfigurationSchema"),
+                "Class with @AbstractConfiguration should not have a superclass"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "MustNotContainPolymorphicIdConfigurationSchema"),
+                "Class with @AbstractConfiguration cannot have a field with @PolymorphicId"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "FieldWithInjectedNameFromAbstractConfigMustContainsNameConfigurationSchema"),
+                "Missing @Name for field"
+        );
+
+        // Let's check the validation of the abstract configuration descendants.
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigRootCanExtendOnlyAbstractConfigurationConfigurationSchema"),
+                "Superclass must have @AbstractConfiguration"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigCanExtendOnlyAbstractConfigurationConfigurationSchema"),
+                "Superclass must have @AbstractConfiguration"
+        );
+
+        // Let's check the search for a conflict of field names.
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigRootConflictFieldNamesWithAbstractConfigConfigurationSchema"),
+                "Duplicate field names are not allowed"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigConflictFieldNamesWithAbstractConfigConfigurationSchema"),
+                "Duplicate field names are not allowed"
+        );
+
+        // Let's check @InjectedName for descendants of abstract configuration.
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigRootMustNotContainInjectedNameInAbstractConfigConfigurationSchema"),
+                "Field with @InjectedName in superclass are not allowed"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigMustNotContainInjectedNameWithAbstractConfigConfigurationSchema"),
+                "Field with @InjectedName is already present in the superclass"
+        );
+
+        // Let's check @InternalId for descendants of abstract configuration.
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigRootMustNotContainInternalIdInAbstractConfigConfigurationSchema"),
+                "Field with @InternalId in superclass are not allowed"
+        );
+
+        assertThrowsEx(
+                IllegalStateException.class,
+                () -> batchCompile(packageName, "ConfigMustNotContainInternalIdWithAbstractConfigConfigurationSchema"),
+                "Field with @InternalId is already present in the superclass"
+        );
+    }
+
+    @Test
+    void testSuccessfulCodeGenerationAbstractConfigurationAndItsDescendants() {
+        String packageName = "org.apache.ignite.internal.configuration.processor.abstractconfig";
+
+        ClassName abstractConfigSchema = ClassName.get(packageName, "AbstractConfigConfigurationSchema");
+        ClassName abstractRootConfigSchema = ClassName.get(packageName, "AbstractRootConfigConfigurationSchema");
+
+        ClassName configSchema = ClassName.get(packageName, "SimpleConfigConfigurationSchema");
+        ClassName configRootSchema = ClassName.get(packageName, "SimpleConfigRootConfigurationSchema");
+
+        BatchCompilation batchCompile = batchCompile(abstractConfigSchema, abstractRootConfigSchema, configSchema, configRootSchema);
+
+        assertThat(batchCompile.getCompilationStatus()).succeededWithoutWarnings();
+
+        assertEquals(4 * 3, batchCompile.generated().size());
+
+        assertTrue(batchCompile.getBySchema(abstractConfigSchema).allGenerated());
+        assertTrue(batchCompile.getBySchema(abstractRootConfigSchema).allGenerated());
+        assertTrue(batchCompile.getBySchema(configSchema).allGenerated());
+        assertTrue(batchCompile.getBySchema(configRootSchema).allGenerated());
+
+        StringSubject configViewInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getViewName(configSchema).toString())
+                .contentsAsUtf8String();
+
+        StringSubject configRootViewInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getViewName(configRootSchema).toString())
+                .contentsAsUtf8String();
+
+        configViewInterfaceContent.contains("extends " + getViewName(abstractConfigSchema).simpleName());
+        configRootViewInterfaceContent.contains("extends " + getViewName(abstractRootConfigSchema).simpleName());
+
+        StringSubject configChangeInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getChangeName(configSchema).toString())
+                .contentsAsUtf8String();
+
+        StringSubject configRootChangeInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getChangeName(configRootSchema).toString())
+                .contentsAsUtf8String();
+
+        configChangeInterfaceContent.contains("extends " + getViewName(configSchema).simpleName()
+                + ", " + getChangeName(abstractConfigSchema).simpleName());
+
+        configRootChangeInterfaceContent.contains("extends " + getViewName(configRootSchema).simpleName()
+                + ", " + getChangeName(abstractRootConfigSchema).simpleName());
+
+        StringSubject configConfigurationInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getConfigurationInterfaceName(configSchema).toString())
+                .contentsAsUtf8String();
+
+        StringSubject configRootConfigurationInterfaceContent = assertThat(batchCompile.getCompilationStatus())
+                .generatedSourceFile(getConfigurationInterfaceName(configRootSchema).toString())
+                .contentsAsUtf8String();
+
+        configConfigurationInterfaceContent.contains("extends " + getConfigurationInterfaceName(abstractConfigSchema).simpleName());
+        configRootConfigurationInterfaceContent.contains("extends " + getConfigurationInterfaceName(abstractRootConfigSchema).simpleName());
+    }
+
     /**
      * Compile set of classes.
      *
      * @param packageName Package names.
-     * @param classNames  Simple class names.
+     * @param classNames Simple class names.
      * @return Result of batch compilation.
      */
     private static BatchCompilation batchCompile(String packageName, String... classNames) {
@@ -457,4 +637,18 @@ public class ItProcessorTest extends AbstractProcessorTest {
             assertThat(t.getMessage(), containsString(expSubStr));
         }
     }
+
+    /**
+     * Extends {@link Assertions#assertThrows(Class, Executable)} to validate an error message against a pattern.
+     *
+     * @param expErrCls Expected error class.
+     * @param exec Supplier.
+     * @param pattern Error message pattern.
+     * @throws AssertionFailedError If failed.
+     */
+    private void assertThrowsEx(Class<? extends Throwable> expErrCls, Executable exec, Pattern pattern) {
+        Throwable t = assertThrows(expErrCls, exec);
+
+        assertThat(t.getMessage().replaceAll("[\\r\\n]", " "), matchesRegex(pattern));
+    }
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigConfigurationSchema.java
similarity index 61%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigConfigurationSchema.java
index 0c05a86fe..1fdfdb483 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigConfigurationSchema.java
@@ -15,21 +15,28 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
 
-import org.apache.ignite.configuration.annotation.Config;
+import java.util.UUID;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.configuration.annotation.InternalId;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * An example of an abstract configuration.
  */
-@Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
+@AbstractConfiguration
+public class AbstractConfigConfigurationSchema {
+    @InjectedName
+    public String name;
 
-    public static final String STRING_CONSTANT = "foobar";
+    @InternalId
+    public UUID id;
 
     @Value
-    public String str;
+    public String strVal;
+
+    @Value
+    public int intVal;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInjectedNameConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInjectedNameConfigurationSchema.java
index 4282aa6ef..b758b9bf2 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInjectedNameConfigurationSchema.java
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.InjectedName;
 
 /**
- * Annotation processing exception.
+ * An example of an abstract configuration with field that contain {@link InjectedName}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+public class AbstractConfigWithInjectedNameConfigurationSchema {
+    @InjectedName
+    public String name;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInternalIdConfigurationSchema.java
similarity index 64%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInternalIdConfigurationSchema.java
index 4282aa6ef..ccdb48db6 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractConfigWithInternalIdConfigurationSchema.java
@@ -15,17 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
+
+import java.util.UUID;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.InternalId;
 
 /**
- * Annotation processing exception.
+ * An example of an abstract configuration with field that contain {@link InternalId}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+public class AbstractConfigWithInternalIdConfigurationSchema {
+    @InternalId
+    public UUID id;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractRootConfigConfigurationSchema.java
similarity index 67%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractRootConfigConfigurationSchema.java
index 4282aa6ef..9de85840a 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/AbstractRootConfigConfigurationSchema.java
@@ -15,17 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.Value;
 
 /**
- * Annotation processing exception.
+ * An example of an abstract root configuration.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
+@AbstractConfiguration
+public class AbstractRootConfigConfigurationSchema {
+    @Value
+    public String strVal;
 
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+    @Value
+    public int intVal;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigConfigurationSchema.java
similarity index 70%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigConfigurationSchema.java
index 0c05a86fe..0ea614c8d 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigConfigurationSchema.java
@@ -15,21 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
 
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * An example of a simple configuration that extends {@link AbstractConfigConfigurationSchema}.
  */
 @Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
+public class SimpleConfigConfigurationSchema extends AbstractConfigConfigurationSchema {
     @Value
-    public String str;
+    public long longVal;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigRootConfigurationSchema.java
similarity index 66%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigRootConfigurationSchema.java
index 0c05a86fe..e735bbf7d 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/SimpleConfigRootConfigurationSchema.java
@@ -15,21 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig;
 
-import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * An example of a simple configuration that extends {@link AbstractConfigConfigurationSchema}.
  */
-@Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
+@ConfigurationRoot(rootName = "test")
+public class SimpleConfigRootConfigurationSchema extends AbstractRootConfigConfigurationSchema {
     @Value
-    public String str;
+    public long longVal;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigCanExtendOnlyAbstractConfigurationConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigCanExtendOnlyAbstractConfigurationConfigurationSchema.java
index 0c05a86fe..b6133667e 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigCanExtendOnlyAbstractConfigurationConfigurationSchema.java
@@ -15,21 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
 
+import java.util.ArrayList;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
-import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Checks that {@link Config} can only extend schema with {@link AbstractConfiguration}.
  */
 @Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
-    @Value
-    public String str;
+public class ConfigCanExtendOnlyAbstractConfigurationConfigurationSchema extends ArrayList {
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigConflictFieldNamesWithAbstractConfigConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigConflictFieldNamesWithAbstractConfigConfigurationSchema.java
index 0c05a86fe..4b0c98a35 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigConflictFieldNamesWithAbstractConfigConfigurationSchema.java
@@ -15,21 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
 
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigConfigurationSchema;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Checks if the {@link Config} conflicts in field names with {@link AbstractConfiguration}.
  */
 @Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
+public class ConfigConflictFieldNamesWithAbstractConfigConfigurationSchema extends AbstractConfigConfigurationSchema {
     @Value
-    public String str;
+    public String name;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInjectedNameWithAbstractConfigConfigurationSchema.java
similarity index 57%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInjectedNameWithAbstractConfigConfigurationSchema.java
index 0c05a86fe..a786713ab 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInjectedNameWithAbstractConfigConfigurationSchema.java
@@ -15,21 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
 
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
-import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigConfigurationSchema;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Checks that {@link Config} must not contain a field with {@link InjectedName} if {@link AbstractConfiguration} already has a field with
+ * {@link InjectedName}.
  */
 @Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
-    @Value
-    public String str;
+public class ConfigMustNotContainInjectedNameWithAbstractConfigConfigurationSchema extends AbstractConfigConfigurationSchema {
+    @InjectedName
+    public String name0;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInternalIdWithAbstractConfigConfigurationSchema.java
similarity index 56%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInternalIdWithAbstractConfigConfigurationSchema.java
index 0c05a86fe..0543c50af 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigMustNotContainInternalIdWithAbstractConfigConfigurationSchema.java
@@ -15,21 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
 
+import java.util.UUID;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
-import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
+import org.apache.ignite.configuration.annotation.InternalId;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigConfigurationSchema;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Checks that {@link Config} must not contain a field with {@link InternalId} if {@link AbstractConfiguration} already has a field with
+ * {@link InternalId}.
  */
 @Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
-    @Value
-    public String str;
+public class ConfigMustNotContainInternalIdWithAbstractConfigConfigurationSchema extends AbstractConfigConfigurationSchema {
+    @InternalId
+    public UUID id0;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootCanExtendOnlyAbstractConfigurationConfigurationSchema.java
similarity index 62%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootCanExtendOnlyAbstractConfigurationConfigurationSchema.java
index 4282aa6ef..13d8f6cb0 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootCanExtendOnlyAbstractConfigurationConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import java.util.ArrayList;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 
 /**
- * Annotation processing exception.
+ * Checks that {@link ConfigurationRoot} can only extend schema with {@link AbstractConfiguration}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@ConfigurationRoot(rootName = "test")
+public class ConfigRootCanExtendOnlyAbstractConfigurationConfigurationSchema extends ArrayList {
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootConflictFieldNamesWithAbstractConfigConfigurationSchema.java
similarity index 58%
copy from modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootConflictFieldNamesWithAbstractConfigConfigurationSchema.java
index 0c05a86fe..075a71f22 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootConflictFieldNamesWithAbstractConfigConfigurationSchema.java
@@ -15,21 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor.internal;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
 
-import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigConfigurationSchema;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Checks if the {@link ConfigurationRoot} conflicts in field names with {@link AbstractConfiguration}.
  */
-@Config
-public class StaticConstantsConfigurationSchema {
-    public static final int INT_CONSTANT = 0;
-
-    public static final String STRING_CONSTANT = "foobar";
-
+@ConfigurationRoot(rootName = "test")
+public class ConfigRootConflictFieldNamesWithAbstractConfigConfigurationSchema extends AbstractConfigConfigurationSchema {
     @Value
-    public String str;
+    public String name;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInjectedNameInAbstractConfigConfigurationSchema.java
similarity index 53%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInjectedNameInAbstractConfigConfigurationSchema.java
index 4282aa6ef..a0702e049 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInjectedNameInAbstractConfigConfigurationSchema.java
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigWithInjectedNameConfigurationSchema;
 
 /**
- * Annotation processing exception.
+ * Checks that {@link ConfigurationRoot} should not extend {@link AbstractConfiguration} which has {@link InjectedName}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@ConfigurationRoot(rootName = "test")
+public class ConfigRootMustNotContainInjectedNameInAbstractConfigConfigurationSchema extends AbstractConfigWithInjectedNameConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInternalIdInAbstractConfigConfigurationSchema.java
similarity index 53%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInternalIdInAbstractConfigConfigurationSchema.java
index 4282aa6ef..7d6294ebf 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/ConfigRootMustNotContainInternalIdInAbstractConfigConfigurationSchema.java
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalId;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.AbstractConfigWithInternalIdConfigurationSchema;
 
 /**
- * Annotation processing exception.
+ * Checks that {@link ConfigurationRoot} should not extend {@link AbstractConfiguration} which has {@link InternalId}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@ConfigurationRoot(rootName = "test")
+public class ConfigRootMustNotContainInternalIdInAbstractConfigConfigurationSchema extends AbstractConfigWithInternalIdConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/FieldWithInjectedNameFromAbstractConfigMustContainsNameConfigurationSchema.java
similarity index 51%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/FieldWithInjectedNameFromAbstractConfigMustContainsNameConfigurationSchema.java
index 4282aa6ef..2f03fcae1 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/FieldWithInjectedNameFromAbstractConfigMustContainsNameConfigurationSchema.java
@@ -15,17 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.configuration.annotation.Name;
+import org.apache.ignite.internal.configuration.processor.abstractconfig.SimpleConfigConfigurationSchema;
 
 /**
- * Annotation processing exception.
+ * Field whose configuration schema (extends abstract configuration) contains {@link InjectedName} must contain {@link Name}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@ConfigurationRoot(rootName = "test")
+public class FieldWithInjectedNameFromAbstractConfigMustContainsNameConfigurationSchema {
+    @ConfigValue
+    public SimpleConfigConfigurationSchema config;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations0ConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations0ConfigurationSchema.java
index 4282aa6ef..4912c7325 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations0ConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
 
 /**
- * Annotation processing exception.
+ * Checks for incompatibility {@link AbstractConfiguration} and {@link InternalConfiguration}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+@InternalConfiguration
+public class IncompatibleSchemaAnnotations0ConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations1ConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations1ConfigurationSchema.java
index 4282aa6ef..6ddaa07fd 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations1ConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.PolymorphicConfig;
 
 /**
- * Annotation processing exception.
+ * Checks for incompatibility {@link AbstractConfiguration} and {@link PolymorphicConfig}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+@PolymorphicConfig
+public class IncompatibleSchemaAnnotations1ConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations2ConfigurationSchema.java
similarity index 64%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations2ConfigurationSchema.java
index 4282aa6ef..a1234a2f7 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations2ConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
 
 /**
- * Annotation processing exception.
+ * Checks for incompatibility {@link AbstractConfiguration} and {@link PolymorphicConfigInstance}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+@PolymorphicConfigInstance("test")
+public class IncompatibleSchemaAnnotations2ConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations3ConfigurationSchema.java
similarity index 67%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations3ConfigurationSchema.java
index 4282aa6ef..a0a9a5a92 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations3ConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.Config;
 
 /**
- * Annotation processing exception.
+ * Checks for incompatibility {@link AbstractConfiguration} and {@link Config}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+@Config
+public class IncompatibleSchemaAnnotations3ConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations4ConfigurationSchema.java
similarity index 64%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations4ConfigurationSchema.java
index 4282aa6ef..51217e841 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/IncompatibleSchemaAnnotations4ConfigurationSchema.java
@@ -15,17 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 
 /**
- * Annotation processing exception.
+ * Checks for incompatibility {@link AbstractConfiguration} and {@link ConfigurationRoot}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+@ConfigurationRoot(rootName = "test")
+public class IncompatibleSchemaAnnotations4ConfigurationSchema {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotBeSuperClassConfigurationSchema.java
similarity index 68%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotBeSuperClassConfigurationSchema.java
index 4282aa6ef..54e039abe 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotBeSuperClassConfigurationSchema.java
@@ -15,17 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import java.util.ArrayList;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 
 /**
- * Annotation processing exception.
+ * Checks that the config schema with {@link AbstractConfiguration} will not extend other classes.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+public class MustNotBeSuperClassConfigurationSchema extends ArrayList {
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotContainPolymorphicIdConfigurationSchema.java
similarity index 65%
copy from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
copy to modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotContainPolymorphicIdConfigurationSchema.java
index 4282aa6ef..66f161248 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/abstractconfig/validation/MustNotContainPolymorphicIdConfigurationSchema.java
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.configuration.processor;
+package org.apache.ignite.internal.configuration.processor.abstractconfig.validation;
+
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.PolymorphicId;
 
 /**
- * Annotation processing exception.
+ * Checks that there should not be a field with {@link PolymorphicId}.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
-        super(message);
-    }
-
-    public ProcessorException(String message, Throwable cause) {
-        super(message, cause);
-    }
+@AbstractConfiguration
+public class MustNotContainPolymorphicIdConfigurationSchema {
+    @PolymorphicId
+    public String type;
 }
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
index 0c05a86fe..76ae2b2dc 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/StaticConstantsConfigurationSchema.java
@@ -19,10 +19,10 @@ package org.apache.ignite.internal.configuration.processor.internal;
 
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.internal.configuration.processor.ItProcessorTest;
+import org.apache.ignite.internal.configuration.processor.ItConfigurationProcessorTest;
 
 /**
- * Test class for {@link ItProcessorTest#testStaticConstants()}.
+ * Test class for {@link ItConfigurationProcessorTest#testStaticConstants()}.
  */
 @Config
 public class StaticConstantsConfigurationSchema {
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessor.java
similarity index 67%
rename from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
rename to modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessor.java
index 7b4bd9cac..f9107e629 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessor.java
@@ -23,9 +23,16 @@ import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PUBLIC;
 import static javax.lang.model.element.Modifier.STATIC;
-import static org.apache.ignite.internal.configuration.processor.Utils.joinSimpleName;
-import static org.apache.ignite.internal.configuration.processor.Utils.simpleName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.collectFieldsWithAnnotation;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.findFirstPresentAnnotation;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getChangeName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getConfigurationInterfaceName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.getViewName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.joinSimpleName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.simpleName;
 import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
+import static org.apache.ignite.internal.util.CollectionUtils.concat;
+import static org.apache.ignite.internal.util.CollectionUtils.difference;
 import static org.apache.ignite.internal.util.CollectionUtils.viewReadOnly;
 
 import com.squareup.javapoet.ClassName;
@@ -42,7 +49,7 @@ import java.io.StringWriter;
 import java.lang.annotation.Annotation;
 import java.util.Collection;
 import java.util.List;
-import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.function.Consumer;
@@ -56,7 +63,6 @@ import javax.lang.model.element.Name;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
@@ -66,6 +72,7 @@ import org.apache.ignite.configuration.NamedListChange;
 import org.apache.ignite.configuration.NamedListView;
 import org.apache.ignite.configuration.PolymorphicChange;
 import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
@@ -82,7 +89,8 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Annotation processor that produces configuration classes.
  */
-public class Processor extends AbstractProcessor {
+// TODO: IGNITE-17166 Split into classes/methods for regular/internal/polymorphic/abstract configuration
+public class ConfigurationProcessor extends AbstractProcessor {
     /** Java file padding. */
     private static final String INDENT = "    ";
 
@@ -143,7 +151,7 @@ public class Processor extends AbstractProcessor {
             // Find all the fields of the schema.
             List<VariableElement> fields = fields(clazz);
 
-            validate(clazz, fields);
+            validateConfigurationSchemaClass(clazz, fields);
 
             // Get package name of the schema class
             String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString();
@@ -151,14 +159,14 @@ public class Processor extends AbstractProcessor {
             ClassName schemaClassName = ClassName.get(packageName, clazz.getSimpleName().toString());
 
             // Get name for generated configuration interface.
-            ClassName configInterface = Utils.getConfigurationInterfaceName(schemaClassName);
+            ClassName configInterface = getConfigurationInterfaceName(schemaClassName);
 
             TypeSpec.Builder configurationInterfaceBuilder = TypeSpec.interfaceBuilder(configInterface)
                     .addModifiers(PUBLIC);
 
             for (VariableElement field : fields) {
                 if (!field.getModifiers().contains(PUBLIC)) {
-                    throw new ProcessorException("Field " + clazz.getQualifiedName() + "." + field + " must be public");
+                    throw new ConfigurationProcessorException("Field " + clazz.getQualifiedName() + "." + field + " must be public");
                 }
 
                 final String fieldName = field.getSimpleName().toString();
@@ -180,7 +188,7 @@ public class Processor extends AbstractProcessor {
                 if (valueAnnotation != null) {
                     // Must be a primitive or an array of the primitives (including java.lang.String)
                     if (!isPrimitiveOrArray(field.asType())) {
-                        throw new ProcessorException(
+                        throw new ConfigurationProcessorException(
                                 "@Value " + clazz.getQualifiedName() + "." + field.getSimpleName() + " field must"
                                         + " have one of the following types: boolean, int, long, double, String or an array of "
                                         + "aforementioned type."
@@ -191,7 +199,7 @@ public class Processor extends AbstractProcessor {
                 PolymorphicId polymorphicId = field.getAnnotation(PolymorphicId.class);
                 if (polymorphicId != null) {
                     if (!isClass(field.asType(), String.class)) {
-                        throw new ProcessorException(String.format(
+                        throw new ConfigurationProcessorException(String.format(
                                 FIELD_MUST_BE_SPECIFIC_CLASS_ERROR_FORMAT,
                                 simpleName(PolymorphicId.class),
                                 clazz.getQualifiedName(),
@@ -203,7 +211,7 @@ public class Processor extends AbstractProcessor {
 
                 if (field.getAnnotation(InternalId.class) != null) {
                     if (!isClass(field.asType(), UUID.class)) {
-                        throw new ProcessorException(String.format(
+                        throw new ConfigurationProcessorException(String.format(
                                 FIELD_MUST_BE_SPECIFIC_CLASS_ERROR_FORMAT,
                                 simpleName(InternalId.class),
                                 clazz.getQualifiedName(),
@@ -256,7 +264,7 @@ public class Processor extends AbstractProcessor {
             ClassName schemaClassName,
             TypeElement realSchemaClass
     ) {
-        ClassName viewClassName = Utils.getViewName(schemaClassName);
+        ClassName viewClassName = getViewName(schemaClassName);
 
         ParameterizedTypeName fieldTypeName = ParameterizedTypeName.get(ROOT_KEY_CLASSNAME, configInterface, viewClassName);
 
@@ -275,8 +283,8 @@ public class Processor extends AbstractProcessor {
      * Create getters for configuration class.
      *
      * @param configurationInterfaceBuilder Interface builder.
-     * @param fieldName                     Field name.
-     * @param interfaceGetMethodType        Return type.
+     * @param fieldName Field name.
+     * @param interfaceGetMethodType Return type.
      */
     private static void createGetters(
             TypeSpec.Builder configurationInterfaceBuilder,
@@ -304,15 +312,15 @@ public class Processor extends AbstractProcessor {
 
         ConfigValue confAnnotation = field.getAnnotation(ConfigValue.class);
         if (confAnnotation != null) {
-            interfaceGetMethodType = Utils.getConfigurationInterfaceName((ClassName) baseType);
+            interfaceGetMethodType = getConfigurationInterfaceName((ClassName) baseType);
         }
 
         NamedConfigValue namedConfigAnnotation = field.getAnnotation(NamedConfigValue.class);
         if (namedConfigAnnotation != null) {
-            ClassName interfaceGetType = Utils.getConfigurationInterfaceName((ClassName) baseType);
+            ClassName interfaceGetType = getConfigurationInterfaceName((ClassName) baseType);
 
-            TypeName viewClassType = Utils.getViewName((ClassName) baseType);
-            TypeName changeClassType = Utils.getChangeName((ClassName) baseType);
+            TypeName viewClassType = getViewName((ClassName) baseType);
+            TypeName changeClassType = getChangeName((ClassName) baseType);
 
             interfaceGetMethodType = ParameterizedTypeName.get(
                     ClassName.get(NamedConfigurationTree.class),
@@ -348,13 +356,13 @@ public class Processor extends AbstractProcessor {
     /**
      * Create VIEW and CHANGE classes and methods.
      *
-     * @param fields                        Collection of configuration fields.
-     * @param schemaClassName               Class name of schema.
+     * @param fields Collection of configuration fields.
+     * @param schemaClassName Class name of schema.
      * @param configurationInterfaceBuilder Configuration interface builder.
-     * @param extendBaseSchema              {@code true} if extending base schema interfaces.
-     * @param realSchemaClass               Class descriptor.
-     * @param isPolymorphicConfig           Is a polymorphic configuration.
-     * @param isPolymorphicInstanceConfig   Is an instance of polymorphic configuration.
+     * @param extendBaseSchema {@code true} if extending base schema interfaces.
+     * @param realSchemaClass Class descriptor.
+     * @param isPolymorphicConfig Is a polymorphic configuration.
+     * @param isPolymorphicInstanceConfig Is an instance of polymorphic configuration.
      */
     private void createPojoBindings(
             Collection<VariableElement> fields,
@@ -365,23 +373,54 @@ public class Processor extends AbstractProcessor {
             boolean isPolymorphicConfig,
             boolean isPolymorphicInstanceConfig
     ) {
-        ClassName viewClsName = Utils.getViewName(schemaClassName);
-        ClassName changeClsName = Utils.getChangeName(schemaClassName);
+        ClassName viewClsName = getViewName(schemaClassName);
+        ClassName changeClsName = getChangeName(schemaClassName);
 
         TypeName configInterfaceType;
         @Nullable TypeName viewBaseSchemaInterfaceType;
         @Nullable TypeName changeBaseSchemaInterfaceType;
 
-        if (extendBaseSchema) {
-            DeclaredType superClassType = (DeclaredType) realSchemaClass.getSuperclass();
-            ClassName superClassSchemaClassName = ClassName.get((TypeElement) superClassType.asElement());
+        TypeElement superClass = superClass(realSchemaClass);
 
-            configInterfaceType = Utils.getConfigurationInterfaceName(superClassSchemaClassName);
-            viewBaseSchemaInterfaceType = Utils.getViewName(superClassSchemaClassName);
-            changeBaseSchemaInterfaceType = Utils.getChangeName(superClassSchemaClassName);
+        boolean isSuperClassAbstractConfiguration = !isClass(superClass.asType(), Object.class)
+                && superClass.getAnnotation(AbstractConfiguration.class) != null;
+
+        if (extendBaseSchema || isSuperClassAbstractConfiguration) {
+            ClassName superClassSchemaClassName = ClassName.get(superClass);
+
+            viewBaseSchemaInterfaceType = getViewName(superClassSchemaClassName);
+            changeBaseSchemaInterfaceType = getChangeName(superClassSchemaClassName);
+
+            if (isSuperClassAbstractConfiguration) {
+                // Example: ExtendedTableConfig extends TableConfig<ExtendedTableView, ExtendedTableChange>
+                configInterfaceType = ParameterizedTypeName.get(
+                        getConfigurationInterfaceName(superClassSchemaClassName),
+                        viewClsName,
+                        changeClsName
+                );
+            } else {
+                // Example: ExtendedTableConfig extends TableConfig
+                configInterfaceType = getConfigurationInterfaceName(superClassSchemaClassName);
+            }
         } else {
             ClassName confTreeInterface = ClassName.get("org.apache.ignite.configuration", "ConfigurationTree");
-            configInterfaceType = ParameterizedTypeName.get(confTreeInterface, viewClsName, changeClsName);
+
+            if (realSchemaClass.getAnnotation(AbstractConfiguration.class) != null) {
+                // Example: TableConfig<VIEWT extends TableView, CHANGET extends TableChange> extends ConfigurationTree<VIEWT, CHANGET>
+                configurationInterfaceBuilder.addTypeVariables(List.of(
+                        TypeVariableName.get("VIEWT", viewClsName),
+                        TypeVariableName.get("CHANGET", changeClsName)
+                ));
+
+                configInterfaceType = ParameterizedTypeName.get(
+                        confTreeInterface,
+                        TypeVariableName.get("VIEWT"),
+                        TypeVariableName.get("CHANGET")
+                );
+            } else {
+                // Example: TableConfig extends ConfigurationTree<TableView, TableChange>
+                configInterfaceType = ParameterizedTypeName.get(confTreeInterface, viewClsName, changeClsName);
+            }
 
             viewBaseSchemaInterfaceType = null;
             changeBaseSchemaInterfaceType = null;
@@ -427,10 +466,10 @@ public class Processor extends AbstractProcessor {
             boolean namedListField = field.getAnnotation(NamedConfigValue.class) != null;
 
             TypeName viewFieldType =
-                    leafField ? schemaFieldTypeName : Utils.getViewName((ClassName) schemaFieldTypeName);
+                    leafField ? schemaFieldTypeName : getViewName((ClassName) schemaFieldTypeName);
 
             TypeName changeFieldType =
-                    leafField ? schemaFieldTypeName : Utils.getChangeName((ClassName) schemaFieldTypeName);
+                    leafField ? schemaFieldTypeName : getChangeName((ClassName) schemaFieldTypeName);
 
             if (namedListField) {
                 changeFieldType = ParameterizedTypeName.get(
@@ -518,7 +557,7 @@ public class Processor extends AbstractProcessor {
                     .build()
                     .writeTo(processingEnv.getFiler());
         } catch (Throwable throwable) {
-            throw new ProcessorException("Failed to generate class " + packageName + "." + cls.name, throwable);
+            throw new ConfigurationProcessorException("Failed to generate class " + packageName + "." + cls.name, throwable);
         }
     }
 
@@ -566,15 +605,13 @@ public class Processor extends AbstractProcessor {
     /**
      * Validate the class.
      *
-     * @param clazz  Class type.
+     * @param clazz Class type.
      * @param fields Class fields.
-     * @throws ProcessorException If the class validation fails.
+     * @throws ConfigurationProcessorException If the class validation fails.
      */
-    private void validate(TypeElement clazz, List<VariableElement> fields) {
-        String simpleName = clazz.getSimpleName().toString();
-
-        if (!simpleName.endsWith(CONFIGURATION_SCHEMA_POSTFIX)) {
-            throw new ProcessorException(
+    private void validateConfigurationSchemaClass(TypeElement clazz, List<VariableElement> fields) {
+        if (!clazz.getSimpleName().toString().endsWith(CONFIGURATION_SCHEMA_POSTFIX)) {
+            throw new ConfigurationProcessorException(
                     String.format("%s must end with '%s'", clazz.getQualifiedName(), CONFIGURATION_SCHEMA_POSTFIX));
         }
 
@@ -584,10 +621,12 @@ public class Processor extends AbstractProcessor {
             validatePolymorphicConfig(clazz, fields);
         } else if (clazz.getAnnotation(PolymorphicConfigInstance.class) != null) {
             validatePolymorphicConfigInstance(clazz, fields);
+        } else if (clazz.getAnnotation(AbstractConfiguration.class) != null) {
+            validateAbstractConfiguration(clazz, fields);
         } else if (clazz.getAnnotation(ConfigurationRoot.class) != null) {
-            checkNotContainsPolymorphicIdField(clazz, ConfigurationRoot.class, fields);
+            validateConfigurationRoot(clazz, fields);
         } else if (clazz.getAnnotation(Config.class) != null) {
-            checkNotContainsPolymorphicIdField(clazz, Config.class, fields);
+            validateConfig(clazz, fields);
         }
 
         validateInjectedNameFields(clazz, fields);
@@ -598,14 +637,14 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks configuration schema with {@link InternalConfiguration}.
      *
-     * @param clazz  type element under validation
-     * @param fields non-static fields of the class under validation
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
      */
     private void validateInternalConfiguration(TypeElement clazz, List<VariableElement> fields) {
         checkIncompatibleClassAnnotations(
                 clazz,
                 InternalConfiguration.class,
-                Config.class, PolymorphicConfig.class, PolymorphicConfigInstance.class
+                incompatibleSchemaClassAnnotations(InternalConfiguration.class, ConfigurationRoot.class)
         );
 
         checkNotContainsPolymorphicIdField(clazz, InternalConfiguration.class, fields);
@@ -618,7 +657,7 @@ public class Processor extends AbstractProcessor {
             TypeElement superClazz = superClass(clazz);
 
             if (superClazz.getAnnotation(InternalConfiguration.class) != null) {
-                throw new ProcessorException(String.format(
+                throw new ConfigurationProcessorException(String.format(
                         "Superclass must not have %s: %s",
                         simpleName(InternalConfiguration.class),
                         clazz.getQualifiedName()
@@ -634,22 +673,22 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks configuration schema with {@link PolymorphicConfig}.
      *
-     * @param clazz  type element under validation
-     * @param fields non-static fields of the class under validation
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
      */
     private void validatePolymorphicConfig(TypeElement clazz, List<VariableElement> fields) {
         checkIncompatibleClassAnnotations(
                 clazz,
                 PolymorphicConfig.class,
-                ConfigurationRoot.class, Config.class, PolymorphicConfigInstance.class
+                incompatibleSchemaClassAnnotations(PolymorphicConfig.class)
         );
 
         checkNotExistSuperClass(clazz, PolymorphicConfig.class);
 
-        List<VariableElement> typeIdFields = collectAnnotatedFields(fields, PolymorphicId.class);
+        List<VariableElement> typeIdFields = collectFieldsWithAnnotation(fields, PolymorphicId.class);
 
         if (typeIdFields.size() != 1 || fields.indexOf(typeIdFields.get(0)) != 0) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Class with %s must contain one field with %s and it should be the first in the schema: %s",
                     simpleName(PolymorphicConfig.class),
                     simpleName(PolymorphicId.class),
@@ -661,14 +700,14 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks configuration schema with {@link PolymorphicConfigInstance}.
      *
-     * @param clazz  type element under validation
-     * @param fields non-static fields of the class under validation
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
      */
     private void validatePolymorphicConfigInstance(TypeElement clazz, List<VariableElement> fields) {
         checkIncompatibleClassAnnotations(
                 clazz,
                 PolymorphicConfigInstance.class,
-                ConfigurationRoot.class, Config.class
+                incompatibleSchemaClassAnnotations(PolymorphicConfigInstance.class)
         );
 
         checkNotContainsPolymorphicIdField(clazz, PolymorphicConfigInstance.class, fields);
@@ -676,7 +715,7 @@ public class Processor extends AbstractProcessor {
         String id = clazz.getAnnotation(PolymorphicConfigInstance.class).value();
 
         if (id == null || id.isBlank()) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     EMPTY_FIELD_ERROR_FORMAT,
                     simpleName(PolymorphicConfigInstance.class) + ".id()",
                     clazz.getQualifiedName()
@@ -705,9 +744,7 @@ public class Processor extends AbstractProcessor {
     }
 
     /**
-     * Returns annotation types supported by this processor.
-     *
-     * @return Annotation types supported by this processor.
+     * Returns immutable set of annotation types supported by this processor.
      */
     private Set<Class<? extends Annotation>> supportedAnnotationTypes() {
         return Set.of(
@@ -715,7 +752,8 @@ public class Processor extends AbstractProcessor {
                 ConfigurationRoot.class,
                 InternalConfiguration.class,
                 PolymorphicConfig.class,
-                PolymorphicConfigInstance.class
+                PolymorphicConfigInstance.class,
+                AbstractConfiguration.class
         );
     }
 
@@ -723,28 +761,11 @@ public class Processor extends AbstractProcessor {
      * Getting a superclass.
      *
      * @param clazz Class type.
-     * @return Superclass type.
      */
     private TypeElement superClass(TypeElement clazz) {
         return processingEnv.getElementUtils().getTypeElement(clazz.getSuperclass().toString());
     }
 
-    /**
-     * Returns the first annotation found for the class.
-     *
-     * @param clazz             Class type.
-     * @param annotationClasses Annotation classes that will be searched for the class.
-     * @return First annotation found.
-     */
-    @SafeVarargs
-    @Nullable
-    private static Annotation findFirst(
-            TypeElement clazz,
-            Class<? extends Annotation>... annotationClasses
-    ) {
-        return Stream.of(annotationClasses).map(clazz::getAnnotation).filter(Objects::nonNull).findFirst().orElse(null);
-    }
-
     /**
      * Search for duplicate class fields by name.
      *
@@ -773,9 +794,9 @@ public class Processor extends AbstractProcessor {
     /**
      * Checking a class field with annotations {@link ConfigValue} or {@link NamedConfigValue}.
      *
-     * @param field           Class field.
+     * @param field Class field.
      * @param annotationClass Field annotation: {@link ConfigValue} or {@link NamedConfigValue}.
-     * @throws ProcessorException If the check is not successful.
+     * @throws ConfigurationProcessorException If the check is not successful.
      */
     private void checkConfigField(
             VariableElement field,
@@ -788,7 +809,7 @@ public class Processor extends AbstractProcessor {
 
         if (fieldTypeElement.getAnnotation(Config.class) == null
                 && fieldTypeElement.getAnnotation(PolymorphicConfig.class) == null) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Class for %s field must be defined as %s: %s.%s",
                     simpleName(annotationClass),
                     joinSimpleName(" or ", Config.class, PolymorphicConfig.class),
@@ -810,27 +831,13 @@ public class Processor extends AbstractProcessor {
         return classType.equals(type);
     }
 
-    /**
-     * Collect fields with annotation.
-     *
-     * @param fields          Fields.
-     * @param annotationClass Annotation class.
-     * @return Fields with annotation.
-     */
-    private static List<VariableElement> collectAnnotatedFields(
-            Collection<VariableElement> fields,
-            Class<? extends Annotation> annotationClass
-    ) {
-        return fields.stream().filter(f -> f.getAnnotation(annotationClass) != null).collect(toList());
-    }
-
     /**
      * Checks for an incompatible class annotation with {@code clazzAnnotation}.
      *
-     * @param clazz                   Class type.
-     * @param clazzAnnotation         Class annotation.
+     * @param clazz Class type.
+     * @param clazzAnnotation Class annotation.
      * @param incompatibleAnnotations Incompatible class annotations with {@code clazzAnnotation}.
-     * @throws ProcessorException If there is an incompatible class annotation with {@code clazzAnnotation}.
+     * @throws ConfigurationProcessorException If there is an incompatible class annotation with {@code clazzAnnotation}.
      */
     @SafeVarargs
     private void checkIncompatibleClassAnnotations(
@@ -841,12 +848,12 @@ public class Processor extends AbstractProcessor {
         assert clazz.getAnnotation(clazzAnnotation) != null : clazz.getQualifiedName();
         assert !nullOrEmpty(incompatibleAnnotations);
 
-        Annotation incompatible = findFirst(clazz, incompatibleAnnotations);
+        Optional<? extends Annotation> incompatible = findFirstPresentAnnotation(clazz, incompatibleAnnotations);
 
-        if (incompatible != null) {
-            throw new ProcessorException(String.format(
+        if (incompatible.isPresent()) {
+            throw new ConfigurationProcessorException(String.format(
                     "Class with %s is not allowed with %s: %s",
-                    simpleName(incompatible.getClass()),
+                    simpleName(incompatible.get().annotationType()),
                     simpleName(clazzAnnotation),
                     clazz.getQualifiedName()
             ));
@@ -856,15 +863,15 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks that the class has a superclass.
      *
-     * @param clazz           Class type.
+     * @param clazz Class type.
      * @param clazzAnnotation Class annotation.
-     * @throws ProcessorException If the class doesn't have a superclass.
+     * @throws ConfigurationProcessorException If the class doesn't have a superclass.
      */
     private void checkExistSuperClass(TypeElement clazz, Class<? extends Annotation> clazzAnnotation) {
         assert clazz.getAnnotation(clazzAnnotation) != null : clazz.getQualifiedName();
 
         if (isClass(clazz.getSuperclass(), Object.class)) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Class with %s should not have a superclass: %s",
                     simpleName(clazzAnnotation),
                     clazz.getQualifiedName()
@@ -875,15 +882,15 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks that the class should not have a superclass.
      *
-     * @param clazz           Class type.
+     * @param clazz Class type.
      * @param clazzAnnotation Class annotation.
-     * @throws ProcessorException If the class have a superclass.
+     * @throws ConfigurationProcessorException If the class have a superclass.
      */
     private void checkNotExistSuperClass(TypeElement clazz, Class<? extends Annotation> clazzAnnotation) {
         assert clazz.getAnnotation(clazzAnnotation) != null : clazz.getQualifiedName();
 
         if (!isClass(clazz.getSuperclass(), Object.class)) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Class with %s should not have a superclass: %s",
                     simpleName(clazzAnnotation),
                     clazz.getQualifiedName()
@@ -894,10 +901,10 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks that the class does not have a field with {@link PolymorphicId}.
      *
-     * @param clazz           Class type.
+     * @param clazz Class type.
      * @param clazzAnnotation Class annotation.
-     * @param clazzfields     Class fields.
-     * @throws ProcessorException If the class has a field with {@link PolymorphicId}.
+     * @param clazzfields Class fields.
+     * @throws ConfigurationProcessorException If the class has a field with {@link PolymorphicId}.
      */
     private void checkNotContainsPolymorphicIdField(
             TypeElement clazz,
@@ -906,8 +913,8 @@ public class Processor extends AbstractProcessor {
     ) {
         assert clazz.getAnnotation(clazzAnnotation) != null : clazz.getQualifiedName();
 
-        if (!collectAnnotatedFields(clazzfields, PolymorphicId.class).isEmpty()) {
-            throw new ProcessorException(String.format(
+        if (!collectFieldsWithAnnotation(clazzfields, PolymorphicId.class).isEmpty()) {
+            throw new ConfigurationProcessorException(String.format(
                     "Class with %s cannot have a field with %s: %s",
                     simpleName(clazzAnnotation),
                     simpleName(PolymorphicId.class),
@@ -919,11 +926,11 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks that there is no conflict of field names between classes.
      *
-     * @param clazz0       First class type.
-     * @param clazz1       Second class type.
+     * @param clazz0 First class type.
+     * @param clazz1 Second class type.
      * @param clazzFields0 First class fields.
      * @param clazzFields1 Second class fields.
-     * @throws ProcessorException If there is a conflict of field names between classes.
+     * @throws ConfigurationProcessorException If there is a conflict of field names between classes.
      */
     private void checkNoConflictFieldNames(
             TypeElement clazz0,
@@ -934,7 +941,7 @@ public class Processor extends AbstractProcessor {
         Collection<Name> duplicateFieldNames = findDuplicates(clazzFields0, clazzFields1);
 
         if (!duplicateFieldNames.isEmpty()) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Duplicate field names are not allowed [class=%s, superClass=%s, fields=%s]",
                     clazz0.getQualifiedName(),
                     clazz1.getQualifiedName(),
@@ -946,10 +953,10 @@ public class Processor extends AbstractProcessor {
     /**
      * Checks if the superclass has at least one annotation from {@code superClazzAnnotations}.
      *
-     * @param clazz                 Class type.
-     * @param superClazz            Superclass type.
+     * @param clazz Class type.
+     * @param superClazz Superclass type.
      * @param superClazzAnnotations Superclass annotations.
-     * @throws ProcessorException If the superclass has none of the annotations from {@code superClazzAnnotations}.
+     * @throws ConfigurationProcessorException If the superclass has none of the annotations from {@code superClazzAnnotations}.
      */
     @SafeVarargs
     private void checkSuperclassContainAnyAnnotation(
@@ -958,7 +965,7 @@ public class Processor extends AbstractProcessor {
             Class<? extends Annotation>... superClazzAnnotations
     ) {
         if (Stream.of(superClazzAnnotations).allMatch(a -> superClazz.getAnnotation(a) == null)) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     SUPERCLASS_MISSING_ANNOTATION_ERROR_FORMAT,
                     joinSimpleName(" or ", superClazzAnnotations),
                     clazz.getQualifiedName()
@@ -969,66 +976,66 @@ public class Processor extends AbstractProcessor {
     /**
      * Validation of class fields with {@link InjectedName} if present.
      *
-     * @param clazz  Class type.
+     * @param clazz Class type.
      * @param fields Class fields.
-     * @throws ProcessorException If the class validation fails.
+     * @throws ConfigurationProcessorException If the class validation fails.
      */
     private void validateInjectedNameFields(TypeElement clazz, List<VariableElement> fields) {
-        List<VariableElement> injectedNameFields = collectAnnotatedFields(fields, InjectedName.class);
+        List<VariableElement> injectedNameFields = collectFieldsWithAnnotation(fields, InjectedName.class);
 
         if (injectedNameFields.isEmpty()) {
             return;
         }
 
         if (injectedNameFields.size() > 1) {
-            throw new ProcessorException(String.format(
-                "%s contains more than one field with %s",
-                clazz.getQualifiedName(),
-                simpleName(InjectedName.class)
+            throw new ConfigurationProcessorException(String.format(
+                    "%s contains more than one field with %s",
+                    clazz.getQualifiedName(),
+                    simpleName(InjectedName.class)
             ));
         }
 
         VariableElement injectedNameField = injectedNameFields.get(0);
 
         if (!isClass(injectedNameField.asType(), String.class)) {
-            throw new ProcessorException(String.format(
-                FIELD_MUST_BE_SPECIFIC_CLASS_ERROR_FORMAT,
-                simpleName(InjectedName.class),
-                clazz.getQualifiedName(),
-                injectedNameField.getSimpleName(),
-                String.class.getSimpleName()
+            throw new ConfigurationProcessorException(String.format(
+                    FIELD_MUST_BE_SPECIFIC_CLASS_ERROR_FORMAT,
+                    simpleName(InjectedName.class),
+                    clazz.getQualifiedName(),
+                    injectedNameField.getSimpleName(),
+                    String.class.getSimpleName()
             ));
         }
 
+        // TODO: IGNITE-17166 Must not contain @Value, @ConfigValue etc
         if (injectedNameField.getAnnotationMirrors().size() > 1) {
-            throw new ProcessorException(String.format(
-                "%s.%s must contain only one %s",
-                clazz.getQualifiedName(),
-                injectedNameField.getSimpleName(),
-                simpleName(InjectedName.class)
+            throw new ConfigurationProcessorException(String.format(
+                    "%s.%s must contain only one %s",
+                    clazz.getQualifiedName(),
+                    injectedNameField.getSimpleName(),
+                    simpleName(InjectedName.class)
             ));
         }
 
-        if (clazz.getAnnotation(Config.class) == null && clazz.getAnnotation(PolymorphicConfig.class) == null) {
-            throw new ProcessorException(String.format(
-                "%s %s.%s can only be present in a class annotated with %s",
-                simpleName(InjectedName.class),
-                clazz.getQualifiedName(),
-                injectedNameField.getSimpleName(),
-                joinSimpleName(" or ", Config.class, PolymorphicConfig.class)
-            ));
-        }
+        findFirstPresentAnnotation(clazz, Config.class, PolymorphicConfig.class, AbstractConfiguration.class)
+                .orElseThrow(() -> new ConfigurationProcessorException(String.format(
+                        "%s %s.%s can only be present in a class annotated with %s",
+                        simpleName(InjectedName.class),
+                        clazz.getQualifiedName(),
+                        injectedNameField.getSimpleName(),
+                        joinSimpleName(" or ", Config.class, PolymorphicConfig.class, AbstractConfiguration.class)
+                )));
     }
 
     /**
      * Validation of class fields with {@link Name} if present.
      *
-     * @param clazz  Class type.
+     * @param clazz Class type.
      * @param fields Class fields.
-     * @throws ProcessorException If the class validation fails.
+     * @throws ConfigurationProcessorException If the class validation fails.
      */
     private void validateNameFields(TypeElement clazz, List<VariableElement> fields) {
-        List<VariableElement> nameFields = collectAnnotatedFields(fields, org.apache.ignite.configuration.annotation.Name.class);
+        List<VariableElement> nameFields = collectFieldsWithAnnotation(fields, org.apache.ignite.configuration.annotation.Name.class);
 
         if (nameFields.isEmpty()) {
             return;
@@ -1036,12 +1043,12 @@ public class Processor extends AbstractProcessor {
 
         for (VariableElement nameField : nameFields) {
             if (nameField.getAnnotation(ConfigValue.class) == null) {
-                throw new ProcessorException(String.format(
-                    "%s annotation can only be used with %s: %s.%s",
-                    simpleName(org.apache.ignite.configuration.annotation.Name.class),
-                    simpleName(ConfigValue.class),
-                    clazz.getQualifiedName(),
-                    nameField.getSimpleName()
+                throw new ConfigurationProcessorException(String.format(
+                        "%s annotation can only be used with %s: %s.%s",
+                        simpleName(org.apache.ignite.configuration.annotation.Name.class),
+                        simpleName(ConfigValue.class),
+                        clazz.getQualifiedName(),
+                        nameField.getSimpleName()
                 ));
             }
         }
@@ -1051,16 +1058,28 @@ public class Processor extends AbstractProcessor {
      * Checks for missing {@link org.apache.ignite.configuration.annotation.Name} for nested schema with {@link InjectedName}.
      *
      * @param field Class field.
-     * @throws ProcessorException If there is no {@link org.apache.ignite.configuration.annotation.Name}
-     *      for the nested schema with {@link InjectedName}.
+     * @throws ConfigurationProcessorException If there is no {@link org.apache.ignite.configuration.annotation.Name} for the nested schema
+     *      with {@link InjectedName}.
      */
     private void checkMissingNameForInjectedName(VariableElement field) {
         TypeElement fieldType = (TypeElement) processingEnv.getTypeUtils().asElement(field.asType());
 
-        List<VariableElement> fields = collectAnnotatedFields(fields(fieldType), InjectedName.class);
+        TypeElement superClassFieldType = superClass(fieldType);
+
+        List<VariableElement> fields;
+
+        if (!isClass(superClassFieldType.asType(), Object.class)
+                && findFirstPresentAnnotation(superClassFieldType, AbstractConfiguration.class).isPresent()) {
+            fields = concat(
+                    collectFieldsWithAnnotation(fields(fieldType), InjectedName.class),
+                    collectFieldsWithAnnotation(fields(superClassFieldType), InjectedName.class)
+            );
+        } else {
+            fields = collectFieldsWithAnnotation(fields(fieldType), InjectedName.class);
+        }
 
         if (!fields.isEmpty() && field.getAnnotation(org.apache.ignite.configuration.annotation.Name.class) == null) {
-            throw new ProcessorException(String.format(
+            throw new ConfigurationProcessorException(String.format(
                     "Missing %s for field: %s.%s",
                     simpleName(org.apache.ignite.configuration.annotation.Name.class),
                     field.getEnclosingElement(),
@@ -1068,4 +1087,124 @@ public class Processor extends AbstractProcessor {
             ));
         }
     }
+
+    /**
+     * Checks configuration schema with {@link AbstractConfiguration}.
+     *
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
+     * @throws ConfigurationProcessorException If validation fails.
+     */
+    private void validateAbstractConfiguration(TypeElement clazz, List<VariableElement> fields) throws ConfigurationProcessorException {
+        checkIncompatibleClassAnnotations(
+                clazz,
+                AbstractConfiguration.class,
+                incompatibleSchemaClassAnnotations(AbstractConfiguration.class)
+        );
+
+        checkNotExistSuperClass(clazz, AbstractConfiguration.class);
+
+        checkNotContainsPolymorphicIdField(clazz, AbstractConfiguration.class, fields);
+    }
+
+    /**
+     * Checks configuration schema with {@link ConfigurationRoot}.
+     *
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
+     * @throws ConfigurationProcessorException If validation fails.
+     */
+    private void validateConfigurationRoot(TypeElement clazz, List<VariableElement> fields) throws ConfigurationProcessorException {
+        checkIncompatibleClassAnnotations(
+                clazz,
+                ConfigurationRoot.class,
+                incompatibleSchemaClassAnnotations(ConfigurationRoot.class)
+        );
+
+        checkNotContainsPolymorphicIdField(clazz, ConfigurationRoot.class, fields);
+
+        TypeElement superClazz = superClass(clazz);
+
+        if (!isClass(superClazz.asType(), Object.class)) {
+            checkSuperclassContainAnyAnnotation(clazz, superClazz, AbstractConfiguration.class);
+
+            List<VariableElement> superClazzFields = fields(superClazz);
+
+            checkNoConflictFieldNames(clazz, superClazz, fields, superClazzFields);
+
+            String invalidFieldInSuperClassFormat = "Field with %s in superclass are not allowed [class=%s, superClass=%s]";
+
+            if (!collectFieldsWithAnnotation(superClazzFields, InjectedName.class).isEmpty()) {
+                throw new ConfigurationProcessorException(String.format(
+                        invalidFieldInSuperClassFormat,
+                        simpleName(InjectedName.class),
+                        clazz.getQualifiedName(),
+                        superClazz.getQualifiedName()
+                ));
+            }
+
+            if (!collectFieldsWithAnnotation(superClazzFields, InternalId.class).isEmpty()) {
+                throw new ConfigurationProcessorException(String.format(
+                        invalidFieldInSuperClassFormat,
+                        simpleName(InternalId.class),
+                        clazz.getQualifiedName(),
+                        superClazz.getQualifiedName()
+                ));
+            }
+        }
+    }
+
+    /**
+     * Checks configuration schema with {@link Config}.
+     *
+     * @param clazz Type element under validation.
+     * @param fields Non-static fields of the class under validation.
+     * @throws ConfigurationProcessorException If validation fails.
+     */
+    private void validateConfig(TypeElement clazz, List<VariableElement> fields) throws ConfigurationProcessorException {
+        checkIncompatibleClassAnnotations(
+                clazz,
+                Config.class,
+                incompatibleSchemaClassAnnotations(Config.class)
+        );
+
+        checkNotContainsPolymorphicIdField(clazz, Config.class, fields);
+
+        TypeElement superClazz = superClass(clazz);
+
+        if (!isClass(superClazz.asType(), Object.class)) {
+            checkSuperclassContainAnyAnnotation(clazz, superClazz, AbstractConfiguration.class);
+
+            List<VariableElement> superClazzFields = fields(superClazz);
+
+            checkNoConflictFieldNames(clazz, superClazz, fields, superClazzFields);
+
+            String fieldAlreadyPresentInSuperClassFormat = "Field with %s is already present in the superclass [class=%s, superClass=%s]";
+
+            if (!collectFieldsWithAnnotation(superClazzFields, InjectedName.class).isEmpty()
+                    && !collectFieldsWithAnnotation(fields, InjectedName.class).isEmpty()) {
+                throw new ConfigurationProcessorException(String.format(
+                        fieldAlreadyPresentInSuperClassFormat,
+                        simpleName(InjectedName.class),
+                        clazz.getQualifiedName(),
+                        superClazz.getQualifiedName()
+                ));
+            }
+
+            if (!collectFieldsWithAnnotation(superClazzFields, InternalId.class).isEmpty()
+                    && !collectFieldsWithAnnotation(fields, InternalId.class).isEmpty()) {
+                throw new ConfigurationProcessorException(String.format(
+                        fieldAlreadyPresentInSuperClassFormat,
+                        simpleName(InternalId.class),
+                        clazz.getQualifiedName(),
+                        superClazz.getQualifiedName()
+                ));
+            }
+        }
+    }
+
+    @SafeVarargs
+    private Class<? extends Annotation>[] incompatibleSchemaClassAnnotations(Class<? extends Annotation>... compatibleAnnotations) {
+        return difference(supportedAnnotationTypes(), Set.of(compatibleAnnotations)).toArray(Class[]::new);
+    }
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorException.java
similarity index 68%
rename from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
rename to modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorException.java
index 4282aa6ef..d21af15a6 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ProcessorException.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorException.java
@@ -17,15 +17,28 @@
 
 package org.apache.ignite.internal.configuration.processor;
 
+import org.jetbrains.annotations.Nullable;
+
 /**
  * Annotation processing exception.
  */
-public class ProcessorException extends RuntimeException {
-    public ProcessorException(String message) {
+public class ConfigurationProcessorException extends RuntimeException {
+    /**
+     * Constructor.
+     *
+     * @param message Error massage.
+     */
+    public ConfigurationProcessorException(String message) {
         super(message);
     }
 
-    public ProcessorException(String message, Throwable cause) {
+    /**
+     * Constructor.
+     *
+     * @param message Error message.
+     * @param cause Cause
+     */
+    public ConfigurationProcessorException(String message, @Nullable Throwable cause) {
         super(message, cause);
     }
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Utils.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtils.java
similarity index 58%
rename from modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Utils.java
rename to modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtils.java
index b5a5565c8..3191ca063 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Utils.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtils.java
@@ -18,24 +18,26 @@
 package org.apache.ignite.internal.configuration.processor;
 
 import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
 
 import com.squareup.javapoet.ClassName;
 import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Stream;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
 
 /**
  * Annotation processing utilities.
  */
-public class Utils {
-    /** Private constructor. */
-    private Utils() {
-    }
-
+class ConfigurationProcessorUtils {
     /**
-     * Get {@link ClassName} for configuration class' public interface.
+     * Returns {@link ClassName} for configuration class public interface.
      *
      * @param schemaClassName Configuration schema ClassName.
-     * @return Configuration's public interface ClassName.
      */
     public static ClassName getConfigurationInterfaceName(ClassName schemaClassName) {
         return ClassName.get(
@@ -45,10 +47,9 @@ public class Utils {
     }
 
     /**
-     * Get {@link ClassName} for configuration VIEW object class.
+     * Returns {@link ClassName} for configuration VIEW object class.
      *
      * @param schemaClassName Configuration schema ClassName.
-     * @return Configuration VIEW object ClassName.
      */
     public static ClassName getViewName(ClassName schemaClassName) {
         return ClassName.get(
@@ -58,10 +59,9 @@ public class Utils {
     }
 
     /**
-     * Get {@link ClassName} for configuration CHANGE object class.
+     * Returns {@link ClassName} for configuration CHANGE object class.
      *
      * @param schemaClassName Configuration schema ClassName.
-     * @return Configuration CHANGE object ClassName.
      */
     public static ClassName getChangeName(ClassName schemaClassName) {
         return ClassName.get(
@@ -71,24 +71,49 @@ public class Utils {
     }
 
     /**
-     * Returns the simple name of the annotation as: @Config.
+     * Returns the simple name of the annotation as: {@code @Config}.
      *
      * @param annotationClass Annotation class.
-     * @return Simple name of the annotation.
      */
     public static String simpleName(Class<? extends Annotation> annotationClass) {
         return '@' + annotationClass.getSimpleName();
     }
 
     /**
-     * Create a string with simple annotation names like: @Config and @PolymorphicConfig.
+     * Creates a string with simple annotation names like: {@code @Config} and {@code @PolymorphicConfig}.
      *
-     * @param delimiter   Delimiter between elements.
+     * @param delimiter Delimiter between elements.
      * @param annotations Annotations.
-     * @return String with simple annotation names.
      */
     @SafeVarargs
     public static String joinSimpleName(String delimiter, Class<? extends Annotation>... annotations) {
-        return Stream.of(annotations).map(Utils::simpleName).collect(joining(delimiter));
+        return Stream.of(annotations).map(ConfigurationProcessorUtils::simpleName).collect(joining(delimiter));
+    }
+
+    /**
+     * Returns the first annotation found for the class.
+     *
+     * @param clazz Class type.
+     * @param annotationClasses Annotation classes that will be searched for the class.
+     */
+    @SafeVarargs
+    public static Optional<? extends Annotation> findFirstPresentAnnotation(
+            TypeElement clazz,
+            Class<? extends Annotation>... annotationClasses
+    ) {
+        return Stream.of(annotationClasses).map(clazz::getAnnotation).filter(Objects::nonNull).findFirst();
+    }
+
+    /**
+     * Collect fields with annotation.
+     *
+     * @param fields Fields.
+     * @param annotationClass Annotation class.
+     */
+    public static List<VariableElement> collectFieldsWithAnnotation(
+            Collection<VariableElement> fields,
+            Class<? extends Annotation> annotationClass
+    ) {
+        return fields.stream().filter(f -> f.getAnnotation(annotationClass) != null).collect(toList());
     }
 }
diff --git a/modules/configuration-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/modules/configuration-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 4f1a31303..d7bf62188 100644
--- a/modules/configuration-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/modules/configuration-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1 @@
-org.apache.ignite.internal.configuration.processor.Processor
\ No newline at end of file
+org.apache.ignite.internal.configuration.processor.ConfigurationProcessor
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/UtilsTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtilsTest.java
similarity index 89%
rename from modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/UtilsTest.java
rename to modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtilsTest.java
index dde3a026b..62d30dc9d 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/UtilsTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/processor/ConfigurationProcessorUtilsTest.java
@@ -17,8 +17,8 @@
 
 package org.apache.ignite.internal.configuration.processor;
 
-import static org.apache.ignite.internal.configuration.processor.Utils.joinSimpleName;
-import static org.apache.ignite.internal.configuration.processor.Utils.simpleName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.joinSimpleName;
+import static org.apache.ignite.internal.configuration.processor.ConfigurationProcessorUtils.simpleName;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import org.apache.ignite.configuration.annotation.Config;
@@ -26,9 +26,9 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.junit.jupiter.api.Test;
 
 /**
- * Class for testing the {@link Utils}.
+ * Class for testing the {@link ConfigurationProcessorUtils}.
  */
-public class UtilsTest {
+public class ConfigurationProcessorUtilsTest {
     @Test
     void testSimpleName() {
         assertEquals("@Config", simpleName(Config.class));
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/AbstractConfiguration.java
similarity index 66%
copy from modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
copy to modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/AbstractConfiguration.java
index cc749cce1..447f63a3a 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/AbstractConfiguration.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.configuration.annotation;
 
-import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
@@ -25,17 +25,16 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /**
- * This annotation marks configuration schema field as a configuration tree node.
- * <pre><code>
- * {@literal @}Config
- *  public class FooConfigurationSchema {
- *      {@literal @}ConfigValue
- *       public SomeOtherConfiguration someOther;
- * }
- * </code></pre>
+ * This annotation marks a class as an abstract configuration schema. Has basically the same properties as a {@link PolymorphicConfig}, but
+ * its type cannot be changed and its inheritors must be annotated with either {@link Config} or {@link ConfigurationRoot}. Configuration
+ * schemas with this annotation cannot be used as a nested (sub)configuration.
+ *
+ * @see Config
+ * @see ConfigurationRoot
+ * @see PolymorphicConfig
  */
-@Target({FIELD})
+@Target(TYPE)
 @Retention(RUNTIME)
 @Documented
-public @interface ConfigValue {
+public @interface AbstractConfiguration {
 }
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
index d17aacc92..08b030a81 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
@@ -56,7 +56,7 @@ import java.lang.annotation.Target;
  *
  * @see ConfigurationRoot
  */
-@Target({TYPE})
+@Target(TYPE)
 @Retention(RUNTIME)
 @Documented
 public @interface Config {
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
index cc749cce1..2a9c38ed1 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
  * }
  * </code></pre>
  */
-@Target({FIELD})
+@Target(FIELD)
 @Retention(RUNTIME)
 @Documented
 public @interface ConfigValue {
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InjectedName.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InjectedName.java
index f1728fbfb..a53745600 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InjectedName.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InjectedName.java
@@ -49,10 +49,12 @@ import java.lang.annotation.Target;
  * </code></pre>
  *
  * <p>NOTE: Field must be a {@link String} and the only one (with this annotation) in the schema, field name is used instead of
- * {@link NamedConfigValue#syntheticKeyName()}, it can be used in schemas with {@link Config} and {@link PolymorphicConfig}.
+ * {@link NamedConfigValue#syntheticKeyName()}, it can be used in schemas with {@link Config}, {@link PolymorphicConfig} and
+ * {@link AbstractConfiguration}.
  *
  * @see Config
  * @see PolymorphicConfig
+ * @see AbstractConfiguration
  * @see ConfigValue
  * @see NamedConfigValue
  * @see Name
diff --git a/modules/configuration/README.md b/modules/configuration/README.md
index dc20e5205..0fffbc359 100644
--- a/modules/configuration/README.md
+++ b/modules/configuration/README.md
@@ -32,16 +32,28 @@ Instances of this interface are generated automatically and are mandatory for re
 An example configuration schema may look like the following:
 
 ```java
-@ConfigurationRoot(rootName = "root", type = ConfigurationType.LOCAL)
+@ConfigurationRoot(rootName = "rootLocal", type = ConfigurationType.LOCAL)
 public static class ParentConfigurationSchema {
     @NamedConfigValue
-    private NamedElementConfigurationSchema elements;
+    public NamedElementConfigurationSchema elements;
 
     @ConfigValue
-    private ChildConfigurationSchema child;
+    public ChildConfigurationSchema child;
+    
+    @ConfigValue
+    public PolymorphicConfigurationSchema polymorphicChild;
+
+    @ConfigValue
+    public SecondChildConfigurationSchema secondChild;
+}
+
+@ConfigurationRoot(rootName = "rootDistributed", type = ConfigurationType.DISTRIBUTED)
+public static class SecondParentConfigurationSchema extends AbstractRootConfigurationSchema { 
+    @ConfigValue
+    public ChildConfigurationSchema child;
 
     @ConfigValue
-    private PolymorphicConfigurationSchema polymorphicChild;
+    public SecondChildConfigurationSchema secondChild;
 }
 
 @Config
@@ -54,6 +66,12 @@ public static class ChildConfigurationSchema {
     public String str2;
 }
 
+@Config
+public static class SecondChildConfigurationSchema extends AbstractConfigurationSchema {
+    @Value(hasDefault = true)
+    public long longVal = 0;
+}
+
 @PolymorphicConfig
 public static class PolymorphicConfigurationSchema {
     @PolymorphicId(hasDefault = true)
@@ -65,6 +83,18 @@ public static class FirstPolymorphicInstanceConfigurationSchema extends Polymorp
     @Value(hasDefault = true)
     public int intVal = 0;
 }
+
+@AbstractConfiguration
+public static class AbstractRootConfigurationSchema {
+    @Value(hasDefault = true)
+    public String strVal = "foobar";
+}
+
+@AbstractConfiguration
+public static class AbstractConfigurationSchema {
+  @Value(hasDefault = true)
+  public int intVal = 0;
+}
 ```
 
 * `@ConfigurationRoot` marks the root schema. It contains the following properties:
@@ -76,7 +106,9 @@ public static class FirstPolymorphicInstanceConfigurationSchema extends Polymorp
 * `@Config` is similar to the `@ConfigurationRoot` but represents an inner configuration node;
 * `@PolymorphicConfig` is similar to the `@Config` and an abstract class in java, i.e. it cannot be instantiated, but it can be subclassed;
 * `@PolymorphicConfigInstance` marks an inheritor of a polymorphic configuration. This annotation has a single property called `value` - 
-   a unique identifier among the inheritors of one polymorphic configuration, used to define the type (schema) of the polymorphic configuration we are dealing with now.
+   a unique identifier among the inheritors of one polymorphic configuration, used to define the type (schema) of the polymorphic configuration we are dealing with now;
+* `@AbstractConfiguration` is similar to `@PolymorphicConfig` but its type cannot be changed and its inheritors must be annotated with
+  either `@Config` or `@ConfigurationRoot`. Configuration schemas with this annotation cannot be used as a nested (sub)configuration;
 * `@ConfigValue` marks a nested schema field. Cyclic dependencies are not allowed;
 * `@NamedConfigValue` is similar to `@ConfigValue`, but such fields represent a collection of properties, not a single
   instance. Every element of the collection will have a `String` name, similar to a `Map`.
@@ -307,4 +339,4 @@ Sometimes it's desirable to have a peek into the future, to read the configurati
 node. There's API for this purpose.
 
 Please refer to `ConfigurationUtil#directProxy(ConfigurationProperty)` for details. There are many usages of this method in tests. It
-should provide the context.
\ No newline at end of file
+should provide the context.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
index a5c951917..590269004 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
@@ -120,6 +120,7 @@ import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdExcept
 import org.apache.ignite.configuration.NamedConfigurationTree;
 import org.apache.ignite.configuration.NamedListView;
 import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.InjectedName;
@@ -154,6 +155,7 @@ import org.objectweb.asm.Type;
  * This class is responsible for generating internal implementation classes for configuration schemas. It uses classes from {@code bytecode}
  * module to achieve this goal, like {@link ClassGenerator}, for examples.
  */
+// TODO: IGNITE-17167 Split into classes/methods for regular/internal/polymorphic/abstract configuration
 public class ConfigurationAsmGenerator {
     /** {@link DynamicConfiguration#DynamicConfiguration} constructor. */
     private static final Constructor<?> DYNAMIC_CONFIGURATION_CTOR;
@@ -432,7 +434,12 @@ public class ConfigurationAsmGenerator {
                         + " is polymorphic but polymorphic extensions are absent");
             }
 
-            List<Field> schemaFields = schemaFields(schemaClass);
+            Class<?> schemaSuperClass = schemaClass.getSuperclass();
+
+            List<Field> schemaFields = schemaSuperClass.isAnnotationPresent(AbstractConfiguration.class)
+                    ? concat(schemaFields(schemaClass), schemaFields(schemaSuperClass))
+                    : schemaFields(schemaClass);
+
             Collection<Field> internalExtensionsFields = extensionsFields(internalExtensions, true);
             Collection<Field> polymorphicExtensionsFields = extensionsFields(polymorphicExtensions, false);
 
@@ -751,6 +758,7 @@ public class ConfigurationAsmGenerator {
 
         // constructDefault
         addNodeConstructDefaultMethod(
+                schemaClass,
                 classDef,
                 specFields,
                 fieldDefs,
@@ -772,6 +780,10 @@ public class ConfigurationAsmGenerator {
             addInternalSchemaTypesMethod(classDef, internalSchemaTypesFieldDef);
         }
 
+        if (schemaClass.getSuperclass().isAnnotationPresent(AbstractConfiguration.class)) {
+            addIsExtendAbstractConfigurationMethod(classDef);
+        }
+
         return classDef;
     }
 
@@ -1541,6 +1553,7 @@ public class ConfigurationAsmGenerator {
     /**
      * Implements {@link InnerNode#constructDefault(String)} method.
      *
+     * @param schemaClass                  Configuration schema class.
      * @param classDef                     Class definition.
      * @param specFields                   Field definitions for the schema and its extensions: {@code _spec#}.
      * @param fieldDefs                    Definitions for all fields in {@code schemaFields}.
@@ -1550,6 +1563,7 @@ public class ConfigurationAsmGenerator {
      * @param polymorphicTypeIdFieldDef    Identification field for the polymorphic configuration instance.
      */
     private static void addNodeConstructDefaultMethod(
+            Class<?> schemaClass,
             ClassDefinition classDef,
             Map<Class<?>, FieldDefinition> specFields,
             Map<String, FieldDefinition> fieldDefs,
@@ -1584,7 +1598,12 @@ public class ConfigurationAsmGenerator {
                     switchBuilder.addCase(fieldName, new BytecodeBlock().ret());
                 } else {
                     FieldDefinition fieldDef = fieldDefs.get(fieldName);
-                    FieldDefinition specFieldDef = specFields.get(schemaField.getDeclaringClass());
+
+                    Class<?> fieldType = schemaField.getDeclaringClass();
+
+                    FieldDefinition specFieldDef = fieldType.isAnnotationPresent(AbstractConfiguration.class)
+                            ? specFields.get(schemaClass)
+                            : specFields.get(fieldType);
 
                     // this.field = spec_#.field;
                     switchBuilder.addCase(
@@ -3406,4 +3425,21 @@ public class ConfigurationAsmGenerator {
                 .append(getThisFieldCode(mtd, internalSchemaTypesFieldDef))
                 .retObject();
     }
+
+    /**
+     * Adds an override for the {@link InnerNode#extendsAbstractConfiguration()} method that returns {@code true}.
+     *
+     * @param innerNodeClassDef {@link InnerNode} class definition.
+     */
+    private static void addIsExtendAbstractConfigurationMethod(ClassDefinition innerNodeClassDef) {
+        MethodDefinition mtd = innerNodeClassDef.declareMethod(
+                of(PUBLIC),
+                "extendsAbstractConfiguration",
+                type(boolean.class)
+        );
+
+        mtd.getBody()
+                .push(true)
+                .retBoolean();
+    }
 }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
index f3efa681f..e2ea27957 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.configuration.tree;
 
 import java.util.NoSuchElementException;
 import java.util.UUID;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 import org.apache.ignite.configuration.annotation.InjectedName;
 import org.apache.ignite.configuration.annotation.InternalConfiguration;
 import org.apache.ignite.configuration.annotation.InternalId;
@@ -245,4 +246,11 @@ public abstract class InnerNode implements TraversableTreeNode, ConstructableTre
     public boolean isPolymorphic() {
         return false;
     }
+
+    /**
+     * Returns {@code true} if the config schema extends {@link AbstractConfiguration}.
+     */
+    public boolean extendsAbstractConfiguration() {
+        return false;
+    }
 }
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
index 850a80df6..c052ad853 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
@@ -630,7 +630,6 @@ public class ConfigurationUtil {
      * PolymorphicId}.
      *
      * @param schemaClass Configuration schema class.
-     * @return Schema fields.
      */
     public static List<Field> schemaFields(Class<?> schemaClass) {
         return Arrays.stream(schemaClass.getDeclaredFields())
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
index 68d02f162..e35285942 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
@@ -191,7 +191,7 @@ public class ValidationUtil {
     private static @Nullable Field findSchemaField(InnerNode innerNode, String schemaFieldName) throws NoSuchFieldException {
         Class<?> schemaType = innerNode.schemaType();
 
-        if (innerNode.isPolymorphic()) {
+        if (innerNode.isPolymorphic() || innerNode.extendsAbstractConfiguration()) {
             // Linear search to not fight with NoSuchFieldException.
             for (Field field : schemaType.getDeclaredFields()) {
                 if (field.getName().equals(schemaFieldName)) {
diff --git a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
index e32ebe6eb..8c7a9a5e6 100644
--- a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
+++ b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
@@ -25,6 +25,7 @@ import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
@@ -39,11 +40,13 @@ import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import org.apache.ignite.configuration.ConfigurationReadOnlyException;
 import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdException;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
 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.InjectedName;
 import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.InternalId;
 import org.apache.ignite.configuration.annotation.Name;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
 import org.apache.ignite.configuration.annotation.PolymorphicConfig;
@@ -103,7 +106,7 @@ public class ConfigurationAsmGeneratorTest {
 
         changer = new TestConfigurationChanger(
                 generator,
-                List.of(TestRootConfiguration.KEY, InjectedNameRootConfiguration.KEY),
+                List.of(TestRootConfiguration.KEY, InjectedNameRootConfiguration.KEY, RootFromAbstractConfiguration.KEY),
                 Map.of(),
                 new TestConfigurationStorage(LOCAL),
                 internalExtensions,
@@ -595,6 +598,54 @@ public class ConfigurationAsmGeneratorTest {
         assertEquals("p4", rootCfg.nestedNamedPoly().get("p4").name().value());
     }
 
+    @Test
+    void testAbstractConfiguration() throws Exception {
+        RootFromAbstractConfiguration rootFromAbstractConfig = (RootFromAbstractConfiguration) generator.instantiateCfg(
+                RootFromAbstractConfiguration.KEY,
+                changer
+        );
+
+        // Checks for default values.
+
+        assertEquals("test", rootFromAbstractConfig.configFromAbstract().name().value());
+
+        assertEquals("strVal", rootFromAbstractConfig.strVal().value());
+        assertEquals(500100, rootFromAbstractConfig.longVal().value());
+
+        assertEquals(100500, rootFromAbstractConfig.configFromAbstract().intVal().value());
+        assertTrue(rootFromAbstractConfig.configFromAbstract().booleanVal().value());
+
+        // Checks for changes to all fields at once.
+
+        rootFromAbstractConfig.change(ch0 -> ch0
+                .changeLongVal(1)
+                .changeConfigFromAbstract(ch1 -> ch1.changeBooleanVal(false).changeIntVal(1))
+                .changeStrVal("1")
+        ).get(1, SECONDS);
+
+        RootFromAbstractView fromAbstractView0 = rootFromAbstractConfig.value();
+
+        assertEquals("1", fromAbstractView0.strVal());
+        assertEquals(1, fromAbstractView0.longVal());
+
+        assertEquals(1, fromAbstractView0.configFromAbstract().intVal());
+        assertFalse(fromAbstractView0.configFromAbstract().booleanVal());
+
+        // Checks for changes to each field.
+
+        rootFromAbstractConfig.longVal().update(2L).get(1, SECONDS);
+        rootFromAbstractConfig.strVal().update("2").get(1, SECONDS);
+
+        rootFromAbstractConfig.configFromAbstract().intVal().update(2).get(1, SECONDS);
+        rootFromAbstractConfig.configFromAbstract().booleanVal().update(true).get(1, SECONDS);
+
+        assertEquals("2", rootFromAbstractConfig.strVal().value());
+        assertEquals(2, rootFromAbstractConfig.longVal().value());
+
+        assertEquals(2, rootFromAbstractConfig.configFromAbstract().intVal().value());
+        assertTrue(rootFromAbstractConfig.configFromAbstract().booleanVal().value());
+    }
+
     /**
      * Test root configuration schema.
      */
@@ -828,4 +879,50 @@ public class ConfigurationAsmGeneratorTest {
     @PolymorphicConfigInstance(PolyInjectedNameConfigurationSchema.SECOND)
     public static class PolyInst1InjectedNameConfigurationSchema extends PolyInjectedNameConfigurationSchema {
     }
+
+    /**
+     * Simple abstract schema configuration for root configurations.
+     */
+    @AbstractConfiguration
+    public static class AbstractRootConfigurationSchema {
+        @Value(hasDefault = true)
+        public String strVal = "strVal";
+    }
+
+    /**
+     * Simple abstract schema configuration for configurations.
+     */
+    @AbstractConfiguration
+    public static class AbstractConfigurationSchema {
+        @InjectedName
+        public String name;
+
+        @InternalId
+        public UUID id;
+
+        @Value(hasDefault = true)
+        public int intVal = 100500;
+    }
+
+    /**
+     * Simple root configuration schema that extends {@link AbstractRootConfigurationSchema}.
+     */
+    @ConfigurationRoot(rootName = "rootFromAbstract")
+    public static class RootFromAbstractConfigurationSchema extends AbstractRootConfigurationSchema {
+        @Value(hasDefault = true)
+        public long longVal = 500100;
+
+        @Name("test")
+        @ConfigValue
+        public ConfigFromAbstractConfigurationSchema configFromAbstract;
+    }
+
+    /**
+     * Simple configuration schema that extends {@link AbstractRootConfigurationSchema}.
+     */
+    @Config
+    public static class ConfigFromAbstractConfigurationSchema extends AbstractConfigurationSchema {
+        @Value(hasDefault = true)
+        public boolean booleanVal = true;
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
index 5002ac5e3..ad0372ddb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
@@ -27,6 +27,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 import it.unimi.dsi.fastutil.ints.IntSet;
 import it.unimi.dsi.fastutil.ints.IntSets;
 import java.util.AbstractCollection;
+import java.util.AbstractList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -184,7 +185,6 @@ public final class CollectionUtils {
 
                 for (int i = 0; i < collections.length; i++) {
                     size += collections[i].size();
-
                 }
 
                 return size;
@@ -335,6 +335,66 @@ public final class CollectionUtils {
         };
     }
 
+    /**
+     * Concatenates lists.
+     *
+     * @param lists Lists.
+     * @param <T> Type of the elements of lists.
+     * @return Immutable list concatenation.
+     */
+    @SafeVarargs
+    public static <T> List<T> concat(List<T>... lists) {
+        if (lists == null || lists.length == 0) {
+            return List.of();
+        }
+
+        return new AbstractList<>() {
+            /** {@inheritDoc} */
+            @Override
+            public T get(int index) {
+                for (List<T> list : lists) {
+                    if (index >= list.size()) {
+                        index -= list.size();
+                    } else {
+                        return list.get(index);
+                    }
+                }
+
+                throw new IndexOutOfBoundsException(index);
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public Iterator<T> iterator() {
+                return concat((Iterable<T>[]) lists).iterator();
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public int size() {
+                int size = 0;
+
+                for (List<T> list : lists) {
+                    size += list.size();
+                }
+
+                return size;
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public boolean contains(Object o) {
+                for (List<T> list : lists) {
+                    if (list.contains(o)) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        };
+    }
+
     /**
      * Create a collection view that can only be read.
      *
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
index 41c1835db..f4f18d600 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
@@ -176,41 +176,41 @@ public class CollectionUtilsTest {
 
     @Test
     void testCollectionUnion() {
-        assertTrue(union().isEmpty());
+        assertTrue(union(new Collection[0]).isEmpty());
         assertTrue(union((Collection<Object>[]) null).isEmpty());
         assertTrue(union(List.of()).isEmpty());
 
-        assertEquals(List.of(1), collect(union(List.of(1), List.of())));
-        assertEquals(List.of(1), collect(union(List.of(), List.of(1))));
+        assertEquals(List.of(1), collect(union(List.of(1), Set.of())));
+        assertEquals(List.of(1), collect(union(List.of(), Set.of(1))));
 
-        assertEquals(List.of(1, 2), collect(union(List.of(1), List.of(2))));
-        assertEquals(List.of(1, 2, 2), collect(union(List.of(1), List.of(2), List.of(2))));
+        assertEquals(List.of(1, 2), collect(union(List.of(1), Set.of(2))));
+        assertEquals(List.of(1, 2, 2), collect(union(List.of(1), List.of(2), Set.of(2))));
 
-        assertFalse(union().contains(0));
+        assertFalse(union(new Collection[0]).contains(0));
         assertFalse(union(List.of()).contains(0));
         assertFalse(union(List.of(1)).contains(0));
-        assertFalse(union(List.of(1), List.of()).contains(0));
-        assertFalse(union(List.of(), List.of(1)).contains(0));
-        assertFalse(union(List.of(1), List.of(2, 3)).contains(0));
+        assertFalse(union(List.of(1), Set.of()).contains(0));
+        assertFalse(union(List.of(), Set.of(1)).contains(0));
+        assertFalse(union(List.of(1), Set.of(2, 3)).contains(0));
 
         assertTrue(union(List.of(0)).contains(0));
-        assertTrue(union(List.of(), List.of(0)).contains(0));
-        assertTrue(union(List.of(0), List.of()).contains(0));
+        assertTrue(union(List.of(), Set.of(0)).contains(0));
+        assertTrue(union(List.of(0), Set.of()).contains(0));
 
-        assertEquals(0, union().size());
+        assertEquals(0, union(new Collection[0]).size());
         assertEquals(0, union(List.of()).size());
         assertEquals(1, union(List.of(1)).size());
-        assertEquals(1, union(List.of(), List.of(1)).size());
-        assertEquals(1, union(List.of(1), List.of()).size());
-        assertEquals(2, union(List.of(1), List.of(2)).size());
-        assertEquals(3, union(List.of(1), List.of(2, 3)).size());
-        assertEquals(5, union(List.of(1, 4, 5), List.of(2, 3)).size());
+        assertEquals(1, union(List.of(), Set.of(1)).size());
+        assertEquals(1, union(List.of(1), Set.of()).size());
+        assertEquals(2, union(List.of(1), Set.of(2)).size());
+        assertEquals(3, union(List.of(1), Set.of(2, 3)).size());
+        assertEquals(5, union(List.of(1, 4, 5), Set.of(2, 3)).size());
 
-        List<Integer> integers = new ArrayList<>(List.of(1, 2, 3));
+        Collection<Integer> integers = new ArrayList<>(List.of(1, 2, 3));
 
         Collection<Integer> union = union(integers);
 
-        integers.remove(0);
+        integers.remove(1);
 
         assertEquals(2, union.size());
     }
@@ -269,6 +269,58 @@ public class CollectionUtilsTest {
         assertEquals(List.of(1, 2, 3), collect(concat(List.of(List.of(1).iterator(), List.of(2, 3).iterator()))));
     }
 
+    @Test
+    void testConcatList() {
+        assertTrue(concat(new List[0]).isEmpty());
+        assertTrue(concat((List<Object>[]) null).isEmpty());
+        assertTrue(concat((List<Object>) List.of()).isEmpty());
+
+        assertEquals(List.of(1), concat(List.of(1), List.of()));
+        assertEquals(List.of(1), concat(List.of(), List.of(1)));
+
+        assertEquals(List.of(1, 2), concat(List.of(1), List.of(2)));
+        assertEquals(List.of(1, 2, 2), concat(List.of(1), List.of(2), List.of(2)));
+
+        assertFalse(concat(new List[0]).contains(0));
+        assertFalse(concat((List<Object>) List.of()).contains(0));
+        assertFalse(concat(List.of(1)).contains(0));
+        assertFalse(concat(List.of(1), List.of()).contains(0));
+        assertFalse(concat(List.of(), List.of(1)).contains(0));
+        assertFalse(concat(List.of(1), List.of(2, 3)).contains(0));
+
+        assertTrue(concat(List.of(0)).contains(0));
+        assertTrue(concat(List.of(), List.of(0)).contains(0));
+        assertTrue(concat(List.of(0), List.of()).contains(0));
+
+        assertEquals(0, concat(new List[0]).size());
+        assertEquals(0, concat((List<Object>) List.of()).size());
+        assertEquals(1, concat(List.of(1)).size());
+        assertEquals(1, concat(List.of(), List.of(1)).size());
+        assertEquals(1, concat(List.of(1), List.of()).size());
+        assertEquals(2, concat(List.of(1), List.of(2)).size());
+        assertEquals(3, concat(List.of(1), List.of(2, 3)).size());
+        assertEquals(5, concat(List.of(1, 4, 5), List.of(2, 3)).size());
+
+        List<Integer> integers = new ArrayList<>(List.of(1, 2, 3));
+
+        List<Integer> union0 = concat(integers);
+
+        integers.remove(1);
+
+        assertEquals(2, union0.size());
+
+        List<Integer> union1 = concat(List.of(0), List.of(1, 2), List.of(3, 4, 5));
+
+        assertEquals(0, union1.get(0));
+        assertEquals(1, union1.get(1));
+        assertEquals(2, union1.get(2));
+        assertEquals(3, union1.get(3));
+        assertEquals(4, union1.get(4));
+        assertEquals(5, union1.get(5));
+
+        assertThrows(IndexOutOfBoundsException.class, () -> union1.get(6));
+    }
+
     /**
      * Collect of elements.
      *