You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2021/05/28 13:23:36 UTC
[ignite-3] branch main updated: IGNITE-14704 Runtime code
generation instead of AOT for "internal" configuration classes. (#122)
This is an automated email from the ASF dual-hosted git repository.
ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 653cd76 IGNITE-14704 Runtime code generation instead of AOT for "internal" configuration classes. (#122)
653cd76 is described below
commit 653cd76a2b58587d86f21c9233b6f14bccd01039
Author: ibessonov <be...@gmail.com>
AuthorDate: Fri May 28 16:23:28 2021 +0300
IGNITE-14704 Runtime code generation instead of AOT for "internal" configuration classes. (#122)
---
.../schemas/runner/NodeConfigurationSchema.java | 1 -
.../processor/internal/AbstractProcessorTest.java | 6 +-
.../processor/internal/ConfigSet.java | 37 +-
.../processor/internal/ITProcessorTest.java | 31 +-
.../internal/ConfigurationDescription.java | 48 -
.../processor/internal/ConfigurationElement.java | 79 --
.../processor/internal/Processor.java | 524 +--------
.../configuration/processor/internal/Utils.java | 58 -
.../configuration/ConfigurationChangerTest.java | 128 ++-
.../internal/util/ConfigurationUtilTest.java | 71 +-
.../internal/validation/ValidationUtilTest.java | 29 +-
.../sample/ConfigurationArrayTest.java | 36 +-
.../sample/ConstructableTreeNodeTest.java | 65 +-
.../sample/TraversableTreeNodeTest.java | 72 +-
.../ignite/configuration/sample/UsageTest.java | 4 +-
modules/configuration/pom.xml | 5 +
.../ignite/configuration/ConfigurationChanger.java | 36 +-
.../configuration/ConfigurationRegistry.java | 38 +-
.../org/apache/ignite/configuration/RootKey.java | 11 +-
.../configuration/annotation/ConfigValue.java | 4 +-
.../configuration/annotation/NamedConfigValue.java | 4 +-
.../ignite/configuration/annotation/Value.java | 4 +-
.../configuration/internal/ConfigurationNode.java | 2 +-
.../internal/DynamicConfiguration.java | 12 +-
.../configuration/internal/DynamicProperty.java | 2 +-
.../ignite/configuration/internal/RootKeyImpl.java | 42 +-
.../ignite/configuration/internal/SuperRoot.java | 40 +-
.../internal/asm/ConfigurationAsmGenerator.java | 1139 ++++++++++++++++++++
.../internal/asm/SchemaClassesInfo.java | 67 ++
.../internal/asm/StringSwitchBuilder.java | 224 ++++
.../internal/util/ConfigurationUtil.java | 2 +-
.../configuration/tree/ConstructableTreeNode.java | 2 +-
.../ignite/configuration/tree/InnerNode.java | 3 +-
.../client/ITMetaStorageServiceTest.java | 4 +-
.../vault/impl/VaultBaseContractsTest.java | 2 +-
35 files changed, 1848 insertions(+), 984 deletions(-)
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
index a79da1f..0d3d9f1 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
@@ -25,7 +25,6 @@ import org.apache.ignite.configuration.storage.ConfigurationType;
/**
* Local node configuration schema.
*/
-@SuppressWarnings("PMD.UnusedPrivateField")
@ConfigurationRoot(rootName = "node", type = ConfigurationType.LOCAL)
public class NodeConfigurationSchema {
/** Uniq local node name. */
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/AbstractProcessorTest.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/AbstractProcessorTest.java
index 80734fb..3ac5ad0 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/AbstractProcessorTest.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/AbstractProcessorTest.java
@@ -62,17 +62,13 @@ public class AbstractProcessorTest {
* @return ConfigSet.
*/
protected static ConfigSet getConfigSet(ClassName clazz, final Map<ClassName, JavaFileObject> generatedClasses) {
- final ClassName configurationName = Utils.getConfigurationName(clazz);
- final ClassName nodeName = Utils.getNodeName(clazz);
final ClassName viewName = Utils.getViewName(clazz);
final ClassName changeName = Utils.getChangeName(clazz);
- final JavaFileObject configurationClass = generatedClasses.get(configurationName);
- final JavaFileObject nodeClass = generatedClasses.get(nodeName);
final JavaFileObject viewClass = generatedClasses.get(viewName);
final JavaFileObject changeClass = generatedClasses.get(changeName);
- return new ConfigSet(configurationClass, nodeClass, viewClass, changeClass);
+ return new ConfigSet(viewClass, changeClass);
}
/**
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
index 08e420d..11511d5 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ConfigSet.java
@@ -32,40 +32,16 @@ import spoon.support.compiler.VirtualFile;
* Wrapper for generated classes of the configuration schema.
*/
public class ConfigSet {
- /** Configuration class. */
- private final JavaFileObject configurationClass;
-
- /** Configuration node class. */
- private final JavaFileObject nodeClass;
-
/** VIEW class. */
private final JavaFileObject viewClass;
/** CHANGE class. */
private final JavaFileObject changeClass;
- /** Parsed configuration class. */
- private final ParsedClass conf;
-
- /** Parsed node class. */
- private final ParsedClass node;
-
/** Constructor. */
- public ConfigSet(JavaFileObject configurationClass, JavaFileObject nodeClass, JavaFileObject viewClass, JavaFileObject changeClass) {
- this.configurationClass = configurationClass;
+ public ConfigSet(JavaFileObject viewClass, JavaFileObject changeClass) {
this.viewClass = viewClass;
this.changeClass = changeClass;
- this.nodeClass = nodeClass;
-
- if (configurationClass != null)
- this.conf = parse(configurationClass);
- else
- this.conf = null;
-
- if (nodeClass != null)
- this.node = parse(nodeClass);
- else
- this.node = null;
}
/**
@@ -88,16 +64,7 @@ public class ConfigSet {
* @return {@code true} if all required classes were generated.
*/
public boolean allGenerated() {
- return configurationClass != null && nodeClass != null && viewClass != null && changeClass != null;
- }
-
- /** */
- public ParsedClass getConfigurationClass() {
- return conf;
- }
-
- public ParsedClass getNodeClass() {
- return node;
+ return viewClass != null && changeClass != null;
}
/**
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ITProcessorTest.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ITProcessorTest.java
index 900e5f2..2bb4b0a 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ITProcessorTest.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/configuration/processor/internal/ITProcessorTest.java
@@ -20,9 +20,6 @@ import com.google.testing.compile.Compilation;
import com.squareup.javapoet.ClassName;
import org.junit.jupiter.api.Test;
-import static org.apache.ignite.configuration.processor.internal.HasFieldMatcher.hasFields;
-import static org.apache.ignite.configuration.processor.internal.HasMethodMatcher.hasMethods;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -46,36 +43,10 @@ public class ITProcessorTest extends AbstractProcessorTest {
assertNotEquals(Compilation.Status.FAILURE, status.status());
- assertEquals(5, batch.generated().size());
+ assertEquals(3, batch.generated().size());
final ConfigSet classSet = batch.getBySchema(testConfigurationSchema);
assertTrue(classSet.allGenerated());
-
- assertThat(
- classSet.getNodeClass(),
- hasFields(
- "value1", String.class.getCanonicalName(),
- "primitiveLong", Long.class.getCanonicalName(),
- "primitiveInt", Integer.class.getCanonicalName(),
- "stringArray", String[].class.getCanonicalName()
- )
- );
-
- String nodeClassName = classSet.getNodeClass().getClassName();
-
- assertThat(
- classSet.getNodeClass(),
- hasMethods(
- "value1()", String.class.getCanonicalName(),
- "primitiveLong()", long.class.getCanonicalName(),
- "primitiveInt()", int.class.getCanonicalName(),
- "stringArray()", String[].class.getCanonicalName(),
- "changeValue1(java.lang.String)", nodeClassName,
- "changePrimitiveLong(long)", nodeClassName,
- "changePrimitiveInt(int)", nodeClassName,
- "changeStringArray(java.lang.String[])", nodeClassName
- )
- );
}
}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationDescription.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationDescription.java
deleted file mode 100644
index 206f4ee..0000000
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationDescription.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.configuration.processor.internal;
-
-import com.squareup.javapoet.TypeName;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Configuration and all it's inner fields.
- */
-public class ConfigurationDescription extends ConfigurationElement {
- /** Inner configuration fields. */
- private List<ConfigurationElement> fields = new ArrayList<>();
-
- /**
- * Constructor.
- * @param type Configuration type.
- * @param name Name of configuration element.
- * @param view Configuration VIEW type.
- * @param change Configuration CHANGE type.
- */
- public ConfigurationDescription(TypeName type, String name, TypeName view, TypeName change) {
- super(type, name, view, change);
- }
-
- /**
- * @return Configuration fields.
- */
- public List<ConfigurationElement> getFields() {
- return fields;
- }
-}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationElement.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationElement.java
deleted file mode 100644
index 809cf5a..0000000
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/ConfigurationElement.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.configuration.processor.internal;
-
-import com.squareup.javapoet.TypeName;
-
-/**
- * Element of configuration.
- */
-public class ConfigurationElement {
- /** Name of configuration element. */
- private final String name;
-
- /** Configuration type. */
- private final TypeName type;
-
- /** Configuration VIEW type. */
- private final TypeName view;
-
- /** Configuration CHANGE type. */
- private final TypeName change;
-
- /**
- * Constructor.
- * @param type Configuration type.
- * @param name Name of configuration element.
- * @param view Configuration VIEW type.
- * @param change Configuration CHANGE type.
- */
- public ConfigurationElement(TypeName type, String name, TypeName view, TypeName change) {
- this.type = type;
- this.name = name;
- this.view = view;
- this.change = change;
- }
-
- /**
- * @return Name of a configuration element.
- */
- public String getName() {
- return name;
- }
-
- /**
- * @return Configuration type.
- */
- public TypeName getType() {
- return type;
- }
-
- /**
- * @return Configuration VIEW type.
- */
- public TypeName getView() {
- return view;
- }
-
- /**
- * @return Configuration CHANGE type.
- */
- public TypeName getChange() {
- return change;
- }
-}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
index 217b3b9..4bfd725 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Processor.java
@@ -20,12 +20,7 @@ package org.apache.ignite.configuration.processor.internal;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -34,7 +29,6 @@ import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -42,14 +36,12 @@ import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import org.apache.ignite.configuration.NamedConfigurationTree;
import org.apache.ignite.configuration.annotation.Config;
@@ -57,21 +49,13 @@ import org.apache.ignite.configuration.annotation.ConfigValue;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.configuration.internal.NamedListConfiguration;
-import org.apache.ignite.configuration.storage.ConfigurationType;
-import org.apache.ignite.configuration.tree.ConfigurationSource;
-import org.apache.ignite.configuration.tree.ConfigurationVisitor;
-import org.apache.ignite.configuration.tree.InnerNode;
import org.apache.ignite.configuration.tree.NamedListChange;
-import org.apache.ignite.configuration.tree.NamedListNode;
import org.apache.ignite.configuration.tree.NamedListView;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
-import static org.apache.ignite.configuration.processor.internal.Utils.suppressWarningsUnchecked;
/**
* Annotation processor that produces configuration classes.
@@ -80,12 +64,6 @@ public class Processor extends AbstractProcessor {
/** Java file padding. */
private static final String INDENT = " ";
- /** Wildcard (?) TypeName. */
- private static final TypeName WILDCARD = WildcardTypeName.subtypeOf(Object.class);
-
- /** Inherit doc javadoc. */
- private static final String INHERIT_DOC = "{@inheritDoc}";
-
/** */
private static final ClassName ROOT_KEY_CLASSNAME = ClassName.get("org.apache.ignite.configuration", "RootKey");
@@ -116,10 +94,6 @@ public class Processor extends AbstractProcessor {
private boolean process0(RoundEnvironment roundEnvironment) {
final Elements elementUtils = processingEnv.getElementUtils();
- Map<TypeName, ConfigurationDescription> props = new HashMap<>();
-
- List<ConfigurationDescription> roots = new ArrayList<>();
-
// All classes annotated with @Config
final List<TypeElement> annotatedConfigs = roundEnvironment
.getElementsAnnotatedWithAny(Set.of(ConfigurationRoot.class, Config.class)).stream()
@@ -145,35 +119,15 @@ public class Processor extends AbstractProcessor {
// Is root of the configuration
final boolean isRoot = rootAnnotation != null;
- // Configuration name
- final String configName = isRoot ? rootAnnotation.rootName() : null;
final ClassName schemaClassName = ClassName.get(packageName, clazz.getSimpleName().toString());
- // Get name for generated configuration class and it's interface
- final ClassName configClass = Utils.getConfigurationName(schemaClassName);
+ // Get name for generated configuration interface.
final ClassName configInterface = Utils.getConfigurationInterfaceName(schemaClassName);
- ConfigurationDescription configDesc = new ConfigurationDescription(
- configClass,
- configName,
- Utils.getViewName(schemaClassName),
- Utils.getChangeName(schemaClassName)
- );
-
- // If root, then use it's package as package for Selectors and Keys
- if (isRoot)
- roots.add(configDesc);
-
- TypeSpec.Builder configurationClassBuilder = TypeSpec.classBuilder(configClass)
- .addSuperinterface(configInterface)
- .addModifiers(FINAL);
-
TypeSpec.Builder configurationInterfaceBuilder = TypeSpec.interfaceBuilder(configInterface)
.addModifiers(PUBLIC);
- CodeBlock.Builder constructorBodyBuilder = CodeBlock.builder();
-
for (VariableElement field : fields) {
if (field.getModifiers().contains(STATIC))
continue;
@@ -188,12 +142,8 @@ public class Processor extends AbstractProcessor {
final String fieldName = field.getSimpleName().toString();
- // Get configuration types (VIEW, INIT, CHANGE and so on)
- final ConfigurationFieldTypes types = getTypes(field);
-
- TypeName fieldType = types.getFieldType();
- TypeName viewClassType = types.getViewClassType();
- TypeName changeClassType = types.getChangeClassType();
+ // Get configuration types (VIEW, CHANGE and so on)
+ TypeName interfaceGetMethodType = getInterfaceGetMethodType(field);
final ConfigValue confAnnotation = field.getAnnotation(ConfigValue.class);
if (confAnnotation != null) {
@@ -203,17 +153,6 @@ public class Processor extends AbstractProcessor {
clazz.getQualifiedName() + "." + field.getSimpleName()
);
}
-
- // Create DynamicConfiguration (descendant) field
- final FieldSpec nestedConfigField =
- FieldSpec
- .builder(fieldType, fieldName, Modifier.PRIVATE, FINAL)
- .build();
-
- configurationClassBuilder.addField(nestedConfigField);
-
- // Constructor statement
- constructorBodyBuilder.addStatement("add($L = new $T(keys, $S, rootKey, changer))", fieldName, fieldType, fieldName);
}
final NamedConfigValue namedConfigAnnotation = field.getAnnotation(NamedConfigValue.class);
@@ -224,25 +163,6 @@ public class Processor extends AbstractProcessor {
clazz.getQualifiedName() + "." + field.getSimpleName()
);
}
-
- // Create NamedListConfiguration<> field
- final FieldSpec nestedConfigField = FieldSpec.builder(
- fieldType,
- fieldName,
- Modifier.PRIVATE,
- FINAL
- ).build();
-
- configurationClassBuilder.addField(nestedConfigField);
-
- // Constructor statement
- constructorBodyBuilder.addStatement(
- "add($L = new $T(keys, $S, rootKey, changer, (p, k) -> new $T(p, k, rootKey, changer)))",
- fieldName,
- fieldType,
- fieldName,
- Utils.getConfigurationName((ClassName) baseType)
- );
}
final Value valueAnnotation = field.getAnnotation(Value.class);
@@ -255,41 +175,19 @@ public class Processor extends AbstractProcessor {
"aforementioned type."
);
}
-
- // Create value (DynamicProperty<>) field
- final FieldSpec generatedField = FieldSpec.builder(fieldType, fieldName, Modifier.PRIVATE, FINAL).build();
-
- configurationClassBuilder.addField(generatedField);
-
- // Constructor statement
- constructorBodyBuilder.addStatement(
- "add($L = new $T(keys, $S, rootKey, changer))",
- fieldName, fieldType, fieldName
- );
}
- configDesc.getFields().add(new ConfigurationElement(fieldType, fieldName, viewClassType, changeClassType));
-
- createGetters(configurationClassBuilder, configurationInterfaceBuilder, fieldName, types);
+ createGetters(configurationInterfaceBuilder, fieldName, interfaceGetMethodType);
}
- props.put(configClass, configDesc);
-
- // Create VIEW, INIT and CHANGE classes
- createPojoBindings(clazz, fields, schemaClassName, configurationClassBuilder, configurationInterfaceBuilder);
-
- if (isRoot) {
- ConfigurationType storageType = rootAnnotation.type();
- createRootKeyField(configInterface, configurationInterfaceBuilder, configDesc, storageType, schemaClassName);
- }
+ // Create VIEW and CHANGE classes
+ createPojoBindings(fields, schemaClassName, configurationInterfaceBuilder);
- // Create constructors for configuration class
- createConstructors(isRoot, configName, configurationClassBuilder, constructorBodyBuilder);
+ if (isRoot)
+ createRootKeyField(configInterface, configurationInterfaceBuilder, schemaClassName, clazz);
// Write configuration interface
buildClass(packageName, configurationInterfaceBuilder.build());
-
- buildClass(packageName, configurationClassBuilder.build());
}
return true;
@@ -299,23 +197,20 @@ public class Processor extends AbstractProcessor {
private void createRootKeyField(
ClassName configInterface,
TypeSpec.Builder configurationClassBuilder,
- ConfigurationDescription configDesc,
- ConfigurationType storageType,
- ClassName schemaClassName
+ ClassName schemaClassName,
+ TypeElement realSchemaClass
) {
ClassName viewClassName = Utils.getViewName(schemaClassName);
ParameterizedTypeName fieldTypeName = ParameterizedTypeName.get(ROOT_KEY_CLASSNAME, configInterface, viewClassName);
- ClassName nodeClassName = Utils.getNodeName(schemaClassName);
-
ClassName cfgRegistryClassName = ClassName.get("org.apache.ignite.configuration", "ConfigurationRegistry");
FieldSpec keyField = FieldSpec.builder(fieldTypeName, "KEY", PUBLIC, STATIC, FINAL)
.initializer(
- "$T.newRootKey($S, $T.$L, $T::new, $T::new)",
- cfgRegistryClassName, configDesc.getName(), ConfigurationType.class, storageType, nodeClassName,
- Utils.getConfigurationName(schemaClassName)
+ "$T.newRootKey($T.class)",
+ cfgRegistryClassName,
+ realSchemaClass
)
.build();
@@ -325,37 +220,21 @@ public class Processor extends AbstractProcessor {
/**
* Create getters for configuration class.
*
- * @param configurationClassBuilder
* @param configurationInterfaceBuilder
* @param fieldName
* @param types
*/
private void createGetters(
- TypeSpec.Builder configurationClassBuilder,
TypeSpec.Builder configurationInterfaceBuilder,
String fieldName,
- ConfigurationFieldTypes types
+ TypeName interfaceGetMethodType
) {
MethodSpec interfaceGetMethod = MethodSpec.methodBuilder(fieldName)
.addModifiers(PUBLIC, ABSTRACT)
- .returns(types.getInterfaceGetMethodType())
+ .returns(interfaceGetMethodType)
.build();
- configurationInterfaceBuilder.addMethod(interfaceGetMethod);
-
- MethodSpec.Builder getMethodBuilder = MethodSpec.methodBuilder(fieldName)
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC, FINAL)
- .returns(types.getInterfaceGetMethodType());
- if (Utils.isNamedConfiguration(types.getFieldType())) {
- getMethodBuilder.addAnnotation(suppressWarningsUnchecked())
- .addStatement("return ($T)$L", NamedConfigurationTree.class, fieldName);
- }
- else
- getMethodBuilder.addStatement("return $L", fieldName);
-
- configurationClassBuilder.addMethod(getMethodBuilder.build());
+ configurationInterfaceBuilder.addMethod(interfaceGetMethod);
}
/**
@@ -363,34 +242,23 @@ public class Processor extends AbstractProcessor {
* @param field
* @return Bundle with all types for configuration
*/
- private ConfigurationFieldTypes getTypes(final VariableElement field) {
- TypeName fieldType = null;
+ private TypeName getInterfaceGetMethodType(final VariableElement field) {
TypeName interfaceGetMethodType = null;
final TypeName baseType = TypeName.get(field.asType());
- TypeName unwrappedType = baseType;
- TypeName viewClassType = baseType;
- TypeName changeClassType = baseType;
-
final ConfigValue confAnnotation = field.getAnnotation(ConfigValue.class);
if (confAnnotation != null) {
- fieldType = Utils.getConfigurationName((ClassName) baseType);
interfaceGetMethodType = Utils.getConfigurationInterfaceName((ClassName) baseType);
-
- unwrappedType = fieldType;
- viewClassType = Utils.getViewName((ClassName) baseType);
- changeClassType = Utils.getChangeName((ClassName) baseType);
}
final NamedConfigValue namedConfigAnnotation = field.getAnnotation(NamedConfigValue.class);
if (namedConfigAnnotation != null) {
ClassName interfaceGetType = Utils.getConfigurationInterfaceName((ClassName) baseType);
- viewClassType = Utils.getViewName((ClassName) baseType);
- changeClassType = Utils.getChangeName((ClassName) baseType);
+ TypeName viewClassType = Utils.getViewName((ClassName) baseType);
+ TypeName changeClassType = Utils.getChangeName((ClassName) baseType);
- fieldType = ParameterizedTypeName.get(ClassName.get(NamedListConfiguration.class), interfaceGetType, viewClassType, changeClassType);
interfaceGetMethodType = ParameterizedTypeName.get(ClassName.get(NamedConfigurationTree.class), interfaceGetType, viewClassType, changeClassType);
}
@@ -398,7 +266,6 @@ public class Processor extends AbstractProcessor {
if (valueAnnotation != null) {
// It is necessary to use class names without loading classes so that we won't
// accidentally get NoClassDefFoundError
- ClassName dynPropClass = ClassName.get("org.apache.ignite.configuration.internal", "DynamicProperty");
ClassName confValueClass = ClassName.get("org.apache.ignite.configuration", "ConfigurationValue");
TypeName genericType = baseType;
@@ -407,126 +274,25 @@ public class Processor extends AbstractProcessor {
genericType = genericType.box();
}
- fieldType = ParameterizedTypeName.get(dynPropClass, genericType);
interfaceGetMethodType = ParameterizedTypeName.get(confValueClass, genericType);
}
- return new ConfigurationFieldTypes(fieldType, unwrappedType, viewClassType, changeClassType, interfaceGetMethodType);
- }
-
- /**
- * Wrapper for configuration schema types.
- */
- private static class ConfigurationFieldTypes {
- /** Field get method type. */
- private final TypeName fieldType;
-
- /** Configuration type (if marked with @ConfigValue or @NamedConfig), or original type (if marked with @Value) */
- private final TypeName unwrappedType;
-
- /** VIEW object type. */
- private final TypeName viewClassType;
-
- /** CHANGE object type. */
- private final TypeName changeClassType;
-
- /** Get method type for public interface. */
- private final TypeName interfaceGetMethodType;
-
- private ConfigurationFieldTypes(TypeName fieldType, TypeName unwrappedType, TypeName viewClassType, TypeName changeClassType, TypeName interfaceGetMethodType) {
- this.fieldType = fieldType;
- this.unwrappedType = unwrappedType;
- this.viewClassType = viewClassType;
- this.changeClassType = changeClassType;
- this.interfaceGetMethodType = interfaceGetMethodType;
- }
-
- /** */
- public TypeName getInterfaceGetMethodType() {
- return interfaceGetMethodType;
- }
-
- /** */
- public TypeName getFieldType() {
- return fieldType;
- }
-
- /** */
- public TypeName getUnwrappedType() {
- return unwrappedType;
- }
-
- /** */
- public TypeName getViewClassType() {
- return viewClassType;
- }
-
- /** */
- public TypeName getChangeClassType() {
- return changeClassType;
- }
+ return interfaceGetMethodType;
}
/**
- * Create configuration class constructors.
- *
- * @param isRoot Flag that indincates whether current configuration is root or not.
- * @param configName Name of the root if configuration is root, {@code null} otherwise.
- * @param configurationClassBuilder Configuration class builder.
- * @param constructorBodyBuilder Constructor body.
- */
- private void createConstructors(
- boolean isRoot,
- String configName,
- TypeSpec.Builder configurationClassBuilder,
- CodeBlock.Builder constructorBodyBuilder
- ) {
- MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(PUBLIC);
-
- if (!isRoot) {
- builder
- .addParameter(ParameterizedTypeName.get(List.class, String.class), "prefix")
- .addParameter(String.class, "key");
- }
-
- builder
- .addParameter(ParameterizedTypeName.get(ROOT_KEY_CLASSNAME, WILDCARD, WILDCARD), "rootKey")
- .addParameter(ClassName.get("org.apache.ignite.configuration", "ConfigurationChanger"), "changer");
-
- if (isRoot)
- builder.addStatement("super($T.emptyList(), $S, rootKey, changer)", Collections.class, configName);
- else
- builder.addStatement("super(prefix, key, rootKey, changer)");
-
- MethodSpec constructorWithName = builder
- .addCode(constructorBodyBuilder.build())
- .build();
-
- configurationClassBuilder.addMethod(constructorWithName);
- }
-
- /**
- * Create VIEW, INIT and CHANGE classes and methods.
- * @param clazz Original class for the schema.
+ * Create VIEW and CHANGE classes and methods.
* @param fields List of configuration fields.
* @param schemaClassName Class name of schema.
- * @param configurationClassBuilder Configuration class builder.
*/
private void createPojoBindings(
- TypeElement clazz,
List<VariableElement> fields,
ClassName schemaClassName,
- TypeSpec.Builder configurationClassBuilder,
TypeSpec.Builder configurationInterfaceBuilder
) {
final ClassName viewClassTypeName = Utils.getViewName(schemaClassName);
final ClassName changeClassName = Utils.getChangeName(schemaClassName);
- ClassName dynConfClass = ClassName.get("org.apache.ignite.configuration.internal", "DynamicConfiguration");
- TypeName dynConfViewClassType = ParameterizedTypeName.get(dynConfClass, viewClassTypeName, changeClassName);
-
- configurationClassBuilder.superclass(dynConfViewClassType);
-
ClassName confTreeInterface = ClassName.get("org.apache.ignite.configuration", "ConfigurationTree");
TypeName confTreeParameterized = ParameterizedTypeName.get(confTreeInterface, viewClassTypeName, changeClassName);
@@ -539,72 +305,12 @@ public class Processor extends AbstractProcessor {
ClassName changeClsName = Utils.getChangeName(schemaClassName);
- ClassName nodeClsName = Utils.getNodeName(schemaClassName);
-
TypeSpec.Builder viewClsBuilder = TypeSpec.interfaceBuilder(viewClsName)
.addModifiers(PUBLIC);
TypeSpec.Builder changeClsBuilder = TypeSpec.interfaceBuilder(changeClsName)
.addModifiers(PUBLIC);
- TypeSpec.Builder nodeClsBuilder = TypeSpec.classBuilder(nodeClsName)
- .addModifiers(FINAL)
- .superclass(ClassName.get(InnerNode.class))
- .addSuperinterface(viewClsName)
- .addSuperinterface(changeClsName)
- // Cannot use "schemaClassName" here because it can't handle inner static classes.
- .addField(FieldSpec.builder(ClassName.get(clazz), "_spec", PRIVATE, FINAL)
- .initializer("new $T()", ClassName.get(clazz))
- .build()
- );
-
- TypeVariableName t = TypeVariableName.get("T");
-
- MethodSpec.Builder traverseChildrenBuilder = MethodSpec.methodBuilder("traverseChildren")
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC)
- .addTypeVariable(t)
- .returns(TypeName.VOID)
- .addParameter(ParameterizedTypeName.get(ClassName.get(ConfigurationVisitor.class), t), "visitor");
-
- MethodSpec.Builder traverseChildBuilder = MethodSpec.methodBuilder("traverseChild")
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC)
- .addTypeVariable(t)
- .returns(t)
- .addException(NoSuchElementException.class)
- .addParameter(ClassName.get(String.class), "key")
- .addParameter(ParameterizedTypeName.get(ClassName.get(ConfigurationVisitor.class), t), "visitor")
- .beginControlFlow("switch (key)");
-
- MethodSpec.Builder constructBuilder = MethodSpec.methodBuilder("construct")
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC)
- .returns(TypeName.VOID)
- .addException(NoSuchElementException.class)
- .addParameter(ClassName.get(String.class), "key")
- .addParameter(ClassName.get(ConfigurationSource.class), "src")
- .beginControlFlow("switch (key)");
-
- MethodSpec.Builder constructDefaultBuilder = MethodSpec.methodBuilder("constructDefault")
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC)
- .returns(TypeName.BOOLEAN)
- .addException(NoSuchElementException.class)
- .addParameter(ClassName.get(String.class), "key")
- .beginControlFlow("switch (key)");
-
- MethodSpec.Builder schemaTypeBuilder = MethodSpec.methodBuilder("schemaType")
- .addAnnotation(Override.class)
- .addJavadoc(INHERIT_DOC)
- .addModifiers(PUBLIC)
- .returns(ParameterizedTypeName.get(ClassName.get(Class.class), WILDCARD))
- .addStatement("return _spec.getClass()");
-
ClassName consumerClsName = ClassName.get(Consumer.class);
for (VariableElement field : fields) {
@@ -613,8 +319,6 @@ public class Processor extends AbstractProcessor {
String fieldName = field.getSimpleName().toString();
TypeName schemaFieldType = TypeName.get(field.asType());
- boolean isArray = schemaFieldType instanceof ArrayTypeName;
-
boolean leafField = isPrimitiveOrArrayOfPrimitives(schemaFieldType)
|| !((ClassName)schemaFieldType).simpleName().contains("ConfigurationSchema");
@@ -624,52 +328,18 @@ public class Processor extends AbstractProcessor {
TypeName changeFieldType = leafField ? schemaFieldType : Utils.getChangeName((ClassName)schemaFieldType);
- TypeName nodeFieldType = leafField ? schemaFieldType.box() : Utils.getNodeName((ClassName)schemaFieldType);
-
- TypeName namedListParamType = nodeFieldType;
-
if (namedListField) {
viewFieldType = ParameterizedTypeName.get(ClassName.get(NamedListView.class), WildcardTypeName.subtypeOf(viewFieldType));
- nodeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListNode.class), nodeFieldType);
-
changeFieldType = ParameterizedTypeName.get(ClassName.get(NamedListChange.class), changeFieldType);
}
{
- FieldSpec.Builder nodeFieldBuilder = FieldSpec.builder(nodeFieldType, fieldName, PRIVATE);
-
- if (namedListField)
- nodeFieldBuilder.initializer("new $T<>($T::new)", NamedListNode.class, namedListParamType);
-
- nodeClsBuilder.addField(nodeFieldBuilder.build());
- }
-
- {
- {
- MethodSpec.Builder getMtdBuilder = MethodSpec.methodBuilder(fieldName)
- .addModifiers(PUBLIC, ABSTRACT)
- .returns(viewFieldType);
-
- viewClsBuilder.addMethod(getMtdBuilder.build());
- }
-
- {
- CodeBlock getStatement;
+ MethodSpec.Builder getMtdBuilder = MethodSpec.methodBuilder(fieldName)
+ .addModifiers(PUBLIC, ABSTRACT)
+ .returns(viewFieldType);
- if (isArray)
- getStatement = CodeBlock.builder().add("return $L.clone()", fieldName).build();
- else
- getStatement = CodeBlock.builder().add("return $L", fieldName).build();
-
- MethodSpec.Builder nodeGetMtdBuilder = MethodSpec.methodBuilder(fieldName)
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(leafField ? viewFieldType : nodeFieldType)
- .addStatement(getStatement);
-
- nodeClsBuilder.addMethod(nodeGetMtdBuilder.build());
- }
+ viewClsBuilder.addMethod(getMtdBuilder.build());
}
{
@@ -680,159 +350,25 @@ public class Processor extends AbstractProcessor {
.addModifiers(PUBLIC, ABSTRACT)
.returns(changeClsName);
- if (valAnnotation != null)
+ if (valAnnotation != null) {
+ if (schemaFieldType instanceof ArrayTypeName)
+ changeMtdBuilder.varargs(true);
+
changeMtdBuilder.addParameter(changeFieldType, fieldName);
+ }
else
changeMtdBuilder.addParameter(ParameterizedTypeName.get(consumerClsName, changeFieldType), fieldName);
changeClsBuilder.addMethod(changeMtdBuilder.build());
}
-
- {
- MethodSpec.Builder nodeChangeMtdBuilder = MethodSpec.methodBuilder(changeMtdName)
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(nodeClsName);
-
- if (valAnnotation != null) {
- CodeBlock changeStatement;
-
- if (isArray)
- changeStatement = CodeBlock.builder().add("this.$L = $L.clone()", fieldName, fieldName).build();
- else
- changeStatement = CodeBlock.builder().add("this.$L = $L", fieldName, fieldName).build();
-
- nodeChangeMtdBuilder
- .addParameter(changeFieldType, fieldName)
- .addStatement(changeStatement);
- }
- else {
- String paramName = fieldName + "Consumer";
- nodeChangeMtdBuilder.addParameter(ParameterizedTypeName.get(consumerClsName, changeFieldType), paramName);
-
- if (!namedListField) {
- nodeChangeMtdBuilder.addStatement(
- "if ($L == null) $L = new $T()",
- fieldName,
- fieldName,
- nodeFieldType
- );
- nodeChangeMtdBuilder.addStatement("$L.accept($L)", paramName, fieldName);
- }
- else {
- nodeChangeMtdBuilder.addAnnotation(suppressWarningsUnchecked());
-
- nodeChangeMtdBuilder.addStatement("$L.accept((NamedListChange)$L)", paramName, fieldName);
- }
- }
-
- nodeChangeMtdBuilder.addStatement("return this");
-
- nodeClsBuilder.addMethod(nodeChangeMtdBuilder.build());
- }
- }
-
- {
- if (leafField) {
- traverseChildrenBuilder.addStatement("visitor.visitLeafNode($S, $L)", fieldName, fieldName);
-
- traverseChildBuilder
- .addStatement("case $S: return visitor.visitLeafNode(key, $L)", fieldName, fieldName);
- }
- else if (namedListField) {
- traverseChildrenBuilder.addStatement("visitor.visitNamedListNode($S, $L)", fieldName, fieldName);
-
- traverseChildBuilder
- .addStatement("case $S: return visitor.visitNamedListNode(key, $L)", fieldName, fieldName);
- }
- else {
- traverseChildrenBuilder.addStatement("visitor.visitInnerNode($S, $L)", fieldName, fieldName);
-
- traverseChildBuilder
- .addStatement("case $S: return visitor.visitInnerNode(key, $L)", fieldName, fieldName);
- }
- }
-
- {
- if (leafField) {
- constructBuilder.addStatement(
- "case $S: $L = src == null ? null : src.unwrap($T.class)",
- fieldName,
- fieldName,
- schemaFieldType.box()
- )
- .addStatement(INDENT + "break");
-
- if (valAnnotation.hasDefault()) {
- constructDefaultBuilder
- .addStatement("case $S: $L = _spec.$L" + (isArray ? ".clone()" : ""), fieldName, fieldName, fieldName)
- .addStatement(INDENT + "return true");
- }
- else
- constructDefaultBuilder.addStatement("case $S: return false", fieldName);
- }
- else if (namedListField) {
- constructBuilder
- .addStatement(
- "case $S: if (src == null) $L = new $T<>($T::new)",
- fieldName,
- fieldName,
- NamedListNode.class,
- namedListParamType
- )
- .addStatement(
- INDENT + "else src.descend($L = $L.copy())",
- fieldName,
- fieldName
- )
- .addStatement(INDENT + "break");
- }
- else {
- constructBuilder
- .addStatement(
- "case $S: if (src == null) $L = null",
- fieldName,
- fieldName
- )
- .addStatement(
- INDENT + "else src.descend($L = ($L == null ? new $T() : ($T)$L.copy()))",
- fieldName,
- fieldName,
- nodeFieldType,
- nodeFieldType,
- fieldName
- )
- .addStatement(INDENT + "break");
- }
}
}
- traverseChildBuilder
- .addStatement("default: throw new $T(key)", NoSuchElementException.class)
- .endControlFlow();
-
- constructBuilder
- .addStatement("default: throw new $T(key)", NoSuchElementException.class)
- .endControlFlow();
-
- constructDefaultBuilder
- .addStatement("default: throw new $T(key)", NoSuchElementException.class)
- .endControlFlow();
-
- nodeClsBuilder
- .addMethod(traverseChildrenBuilder.build())
- .addMethod(traverseChildBuilder.build())
- .addMethod(constructBuilder.build())
- .addMethod(constructDefaultBuilder.build())
- .addMethod(schemaTypeBuilder.build());
-
TypeSpec viewCls = viewClsBuilder.build();
TypeSpec changeCls = changeClsBuilder.build();
- TypeSpec nodeCls = nodeClsBuilder.build();
buildClass(viewClsName.packageName(), viewCls);
buildClass(changeClsName.packageName(), changeCls);
- buildClass(nodeClsName.packageName(), nodeCls);
}
/** */
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
index aa221eb..d1869d8 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/configuration/processor/internal/Utils.java
@@ -16,37 +16,17 @@
*/
package org.apache.ignite.configuration.processor.internal;
-import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import org.apache.ignite.configuration.internal.NamedListConfiguration;
/**
* Annotation processing utilities.
*/
public class Utils {
- /** */
- private static final ClassName NAMED_LIST_CFG_CLASSNAME = ClassName.get("org.apache.ignite.configuration.internal", "NamedListConfiguration");
-
/** Private constructor. */
private Utils() {
}
/**
- * Get {@link ClassName} for configuration class.
- *
- * @param schemaClassName Configuration schema ClassName.
- * @return Configuration ClassName.
- */
- public static ClassName getConfigurationName(ClassName schemaClassName) {
- return ClassName.get(
- schemaClassName.packageName(),
- schemaClassName.simpleName().replaceAll("Schema$", "Impl")
- );
- }
-
- /**
* Get {@link ClassName} for configuration class' public interface.
*
* @param schemaClassName Configuration schema ClassName.
@@ -60,19 +40,6 @@ public class Utils {
}
/**
- * Get {@link ClassName} for a configuration node object class.
- *
- * @param schemaClassName Configuration schema ClassName.
- * @return Configuration node object ClassName.
- */
- public static ClassName getNodeName(ClassName schemaClassName) {
- return ClassName.get(
- schemaClassName.packageName(),
- schemaClassName.simpleName().replace("ConfigurationSchema", "Node")
- );
- }
-
- /**
* Get {@link ClassName} for configuration VIEW object class.
*
* @param schemaClassName Configuration schema ClassName.
@@ -97,29 +64,4 @@ public class Utils {
schemaClassName.simpleName().replace("ConfigurationSchema", "Change")
);
}
-
- /**
- * Check whether type is {@link NamedListConfiguration}.
- *
- * @param type Type.
- * @return {@code true} if type is {@link NamedListConfiguration}.
- */
- public static boolean isNamedConfiguration(TypeName type) {
- if (type instanceof ParameterizedTypeName) {
- ParameterizedTypeName parameterizedTypeName = (ParameterizedTypeName) type;
-
- if (parameterizedTypeName.rawType.equals(NAMED_LIST_CFG_CLASSNAME))
- return true;
- }
- return false;
- }
-
- /**
- * @return {@code @SuppressWarnings("unchecked")} annotation spec object.
- */
- public static AnnotationSpec suppressWarningsUnchecked() {
- return AnnotationSpec.builder(SuppressWarnings.class)
- .addMember("value", "$S", "unchecked")
- .build();
- }
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/ConfigurationChangerTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/ConfigurationChangerTest.java
index 79cc097..7e22e2e 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/ConfigurationChangerTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/ConfigurationChangerTest.java
@@ -20,7 +20,6 @@ import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -29,17 +28,21 @@ import org.apache.ignite.configuration.annotation.ConfigValue;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.storage.ConfigurationType;
import org.apache.ignite.configuration.storage.Data;
import org.apache.ignite.configuration.storage.TestConfigurationStorage;
+import org.apache.ignite.configuration.tree.InnerNode;
import org.apache.ignite.configuration.validation.Immutable;
import org.apache.ignite.configuration.validation.ValidationContext;
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.configuration.validation.Validator;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static java.util.Collections.singletonMap;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.ignite.configuration.AConfiguration.KEY;
@@ -91,6 +94,13 @@ public class ConfigurationChangerTest {
public String strCfg;
}
+ private static ConfigurationAsmGenerator cgen = new ConfigurationAsmGenerator();
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
/**
* Test simple change of configuration.
*/
@@ -98,17 +108,17 @@ public class ConfigurationChangerTest {
public void testSimpleConfigurationChange() throws Exception {
final TestConfigurationStorage storage = new TestConfigurationStorage();
- ANode data = new ANode()
- .changeChild(change -> change.changeIntCfg(1).changeStrCfg("1"))
- .changeElements(change -> change.create("a", element -> element.changeStrCfg("1")));
-
- ConfigurationChanger changer = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
+ ConfigurationChanger changer = new TestConfigurationChanger();
changer.addRootKey(KEY);
changer.register(storage);
- changer.change(Collections.singletonMap(KEY, data)).get(1, SECONDS);
+ AChange data = ((AChange)changer.createRootNode(KEY))
+ .changeChild(change -> change.changeIntCfg(1).changeStrCfg("1"))
+ .changeElements(change -> change.create("a", element -> element.changeStrCfg("1")));
+
+ changer.change(singletonMap(KEY, (InnerNode)data)).get(1, SECONDS);
- ANode newRoot = (ANode)changer.getRootNode(KEY);
+ AView newRoot = (AView)changer.getRootNode(KEY);
assertEquals(1, newRoot.child().intCfg());
assertEquals("1", newRoot.child().strCfg());
@@ -122,36 +132,37 @@ public class ConfigurationChangerTest {
public void testModifiedFromAnotherStorage() throws Exception {
final TestConfigurationStorage storage = new TestConfigurationStorage();
- ANode data1 = new ANode()
+ ConfigurationChanger changer1 = new TestConfigurationChanger();
+ changer1.addRootKey(KEY);
+ changer1.register(storage);
+
+ ConfigurationChanger changer2 = new TestConfigurationChanger();
+ changer2.addRootKey(KEY);
+ changer2.register(storage);
+
+ AChange data1 = ((AChange)changer1.createRootNode(KEY))
.changeChild(change -> change.changeIntCfg(1).changeStrCfg("1"))
.changeElements(change -> change.create("a", element -> element.changeStrCfg("1")));
- ANode data2 = new ANode()
+ changer1.change(singletonMap(KEY, (InnerNode)data1)).get(1, SECONDS);
+
+ AChange data2 = ((AChange)changer2.createRootNode(KEY))
.changeChild(change -> change.changeIntCfg(2).changeStrCfg("2"))
.changeElements(change -> change
.create("a", element -> element.changeStrCfg("2"))
.create("b", element -> element.changeStrCfg("2"))
);
- ConfigurationChanger changer1 = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
- changer1.addRootKey(KEY);
- changer1.register(storage);
-
- ConfigurationChanger changer2 = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
- changer2.addRootKey(KEY);
- changer2.register(storage);
-
- changer1.change(Collections.singletonMap(KEY, data1)).get(1, SECONDS);
- changer2.change(Collections.singletonMap(KEY, data2)).get(1, SECONDS);
+ changer2.change(singletonMap(KEY, (InnerNode)data2)).get(1, SECONDS);
- ANode newRoot1 = (ANode)changer1.getRootNode(KEY);
+ AView newRoot1 = (AView)changer1.getRootNode(KEY);
assertEquals(2, newRoot1.child().intCfg());
assertEquals("2", newRoot1.child().strCfg());
assertEquals("2", newRoot1.elements().get("a").strCfg());
assertEquals("2", newRoot1.elements().get("b").strCfg());
- ANode newRoot2 = (ANode)changer2.getRootNode(KEY);
+ AView newRoot2 = (AView)changer2.getRootNode(KEY);
assertEquals(2, newRoot2.child().intCfg());
assertEquals("2", newRoot2.child().strCfg());
@@ -166,26 +177,19 @@ public class ConfigurationChangerTest {
public void testModifiedFromAnotherStorageWithIncompatibleChanges() throws Exception {
final TestConfigurationStorage storage = new TestConfigurationStorage();
- ANode data1 = new ANode()
- .changeChild(change -> change.changeIntCfg(1).changeStrCfg("1"))
- .changeElements(change -> change.create("a", element -> element.changeStrCfg("1")));
-
- ANode data2 = new ANode()
- .changeChild(change -> change.changeIntCfg(2).changeStrCfg("2"))
- .changeElements(change -> change
- .create("a", element -> element.changeStrCfg("2"))
- .create("b", element -> element.changeStrCfg("2"))
- );
-
- ConfigurationChanger changer1 = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
+ ConfigurationChanger changer1 = new TestConfigurationChanger();
changer1.addRootKey(KEY);
changer1.register(storage);
- ConfigurationChanger changer2 = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
+ ConfigurationChanger changer2 = new TestConfigurationChanger();
changer2.addRootKey(KEY);
changer2.register(storage);
- changer1.change(Collections.singletonMap(KEY, data1)).get(1, SECONDS);
+ AChange data1 = ((AChange)changer1.createRootNode(KEY))
+ .changeChild(change -> change.changeIntCfg(1).changeStrCfg("1"))
+ .changeElements(change -> change.create("a", element -> element.changeStrCfg("1")));
+
+ changer1.change(singletonMap(KEY, (InnerNode)data1)).get(1, SECONDS);
changer2.addValidator(MaybeInvalid.class, new Validator<MaybeInvalid, Object>() {
@Override public void validate(MaybeInvalid annotation, ValidationContext<Object> ctx) {
@@ -193,9 +197,16 @@ public class ConfigurationChangerTest {
}
});
- assertThrows(ExecutionException.class, () -> changer2.change(Collections.singletonMap(KEY, data2)).get(1, SECONDS));
+ AChange data2 = ((AChange)changer2.createRootNode(KEY))
+ .changeChild(change -> change.changeIntCfg(2).changeStrCfg("2"))
+ .changeElements(change -> change
+ .create("a", element -> element.changeStrCfg("2"))
+ .create("b", element -> element.changeStrCfg("2"))
+ );
+
+ assertThrows(ExecutionException.class, () -> changer2.change(singletonMap(KEY, (InnerNode)data2)).get(1, SECONDS));
- ANode newRoot = (ANode)changer2.getRootNode(KEY);
+ AView newRoot = (AView)changer2.getRootNode(KEY);
assertEquals(1, newRoot.child().intCfg());
assertEquals("1", newRoot.child().strCfg());
@@ -209,9 +220,7 @@ public class ConfigurationChangerTest {
public void testFailedToWrite() {
final TestConfigurationStorage storage = new TestConfigurationStorage();
- ANode data = new ANode().changeChild(child -> child.changeIntCfg(1));
-
- ConfigurationChanger changer = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
+ ConfigurationChanger changer = new TestConfigurationChanger();
changer.addRootKey(KEY);
storage.fail(true);
@@ -224,7 +233,9 @@ public class ConfigurationChangerTest {
storage.fail(true);
- assertThrows(ExecutionException.class, () -> changer.change(Collections.singletonMap(KEY, data)).get(1, SECONDS));
+ AChange data = ((AChange)changer.createRootNode(KEY)).changeChild(child -> child.changeIntCfg(1));
+
+ assertThrows(ExecutionException.class, () -> changer.change(singletonMap(KEY, (InnerNode)data)).get(1, SECONDS));
storage.fail(false);
@@ -233,7 +244,7 @@ public class ConfigurationChangerTest {
assertEquals(0, dataMap.size());
- ANode newRoot = (ANode)changer.getRootNode(KEY);
+ AView newRoot = (AView)changer.getRootNode(KEY);
assertNull(newRoot.child());
}
@@ -267,7 +278,7 @@ public class ConfigurationChangerTest {
@Test
public void defaultsOnInit() throws Exception {
- var changer = new ConfigurationChanger((oldRoot, newRoot, revision) -> completedFuture(null));
+ var changer = new TestConfigurationChanger();
changer.addRootKey(DefaultsConfiguration.KEY);
@@ -277,19 +288,38 @@ public class ConfigurationChangerTest {
changer.initialize(storage.type());
- DefaultsNode root = (DefaultsNode)changer.getRootNode(DefaultsConfiguration.KEY);
+ DefaultsView root = (DefaultsView)changer.getRootNode(DefaultsConfiguration.KEY);
assertEquals("foo", root.defStr());
assertEquals("bar", root.child().defStr());
assertEquals(List.of("xyz"), Arrays.asList(root.child().arr()));
- // This is not init, move it to another test =(
- changer.change(Map.of(DefaultsConfiguration.KEY, new DefaultsNode().changeChildsList(childs ->
+ DefaultsChange change = ((DefaultsChange)changer.createRootNode(DefaultsConfiguration.KEY)).changeChildsList(childs ->
childs.create("name", child -> {})
- ))).get(1, SECONDS);
+ );
- root = (DefaultsNode)changer.getRootNode(DefaultsConfiguration.KEY);
+ changer.change(Map.of(DefaultsConfiguration.KEY, (InnerNode)change)).get(1, SECONDS);
+
+ root = (DefaultsView)changer.getRootNode(DefaultsConfiguration.KEY);
assertEquals("bar", root.childsList().get("name").defStr());
}
+
+ private static class TestConfigurationChanger extends ConfigurationChanger {
+ TestConfigurationChanger() {
+ super((oldRoot, newRoot, revision) -> completedFuture(null));
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addRootKey(RootKey<?, ?> rootKey) {
+ super.addRootKey(rootKey);
+
+ cgen.compileRootSchema(rootKey.schemaClass());
+ }
+
+ /** {@inheritDoc} */
+ @Override public InnerNode createRootNode(RootKey<?, ?> rootKey) {
+ return cgen.instantiateNode(rootKey.schemaClass());
+ }
+ }
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
index 1255534..48051c3 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/util/ConfigurationUtilTest.java
@@ -25,7 +25,12 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.configuration.internal.SuperRoot;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.storage.ConfigurationType;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.TraversableTreeNode;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static java.util.Collections.emptyMap;
@@ -39,6 +44,24 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/** */
public class ConfigurationUtilTest {
+ private static ConfigurationAsmGenerator cgen;
+
+ @BeforeAll
+ public static void beforeAll() {
+ cgen = new ConfigurationAsmGenerator();
+
+ cgen.compileRootSchema(ParentConfigurationSchema.class);
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
+ public static <P extends InnerNode & ParentView & ParentChange> P newParentInstance() {
+ return (P)cgen.instantiateNode(ParentConfigurationSchema.class);
+ }
+
/** */
@Test
public void escape() {
@@ -102,7 +125,9 @@ public class ConfigurationUtilTest {
/** */
@Test
public void findSuccessfully() {
- var parent = new ParentNode().changeElements(elements ->
+ var parent = newParentInstance();
+
+ parent.changeElements(elements ->
elements.update("name", element ->
element.changeChild(child ->
child.changeStr("value")
@@ -139,7 +164,7 @@ public class ConfigurationUtilTest {
/** */
@Test
public void findNulls() {
- var parent = new ParentNode();
+ var parent = newParentInstance();
assertNull(ConfigurationUtil.find(List.of("elements", "name"), parent));
@@ -147,7 +172,7 @@ public class ConfigurationUtilTest {
assertNull(ConfigurationUtil.find(List.of("elements", "name", "child"), parent));
- parent.elements().get("name").changeChild(child -> {});
+ ((NamedElementChange)parent.elements().get("name")).changeChild(child -> {});
assertNull(ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent));
}
@@ -155,7 +180,7 @@ public class ConfigurationUtilTest {
/** */
@Test
public void findUnsuccessfully() {
- var parent = new ParentNode();
+ var parent = newParentInstance();
assertThrows(
KeyNotFoundException.class,
@@ -169,7 +194,7 @@ public class ConfigurationUtilTest {
() -> ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent)
);
- parent.elements().get("name").changeChild(child -> child.changeStr("value"));
+ ((NamedElementChange)parent.elements().get("name")).changeChild(child -> child.changeStr("value"));
assertThrows(
KeyNotFoundException.class,
@@ -204,7 +229,7 @@ public class ConfigurationUtilTest {
/** */
@Test
public void fillFromPrefixMapSuccessfully() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
ConfigurationUtil.fillFromPrefixMap(parentNode, Map.of(
"elements", Map.of(
@@ -224,7 +249,9 @@ public class ConfigurationUtilTest {
/** */
@Test
public void fillFromPrefixMapSuccessfullyWithRemove() {
- var parentNode = new ParentNode().changeElements(elements ->
+ var parentNode = newParentInstance();
+
+ parentNode.changeElements(elements ->
elements.update("name", element ->
element.changeChild(child -> {})
)
@@ -240,9 +267,9 @@ public class ConfigurationUtilTest {
/** */
@Test
public void nodeToFlatMap() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
- var parentSuperRoot = new SuperRoot(emptyMap(), Map.of(
+ var parentSuperRoot = new SuperRoot(key -> null, Map.of(
ParentConfiguration.KEY,
parentNode
));
@@ -268,9 +295,9 @@ public class ConfigurationUtilTest {
assertEquals(
emptyMap(),
- ConfigurationUtil.nodeToFlatMap(parentSuperRoot, new SuperRoot(emptyMap(), singletonMap(
+ ConfigurationUtil.nodeToFlatMap(parentSuperRoot, new SuperRoot(key -> null, singletonMap(
ParentConfiguration.KEY,
- new ParentNode().changeElements(elements ->
+ (InnerNode)newParentInstance().changeElements(elements ->
elements.delete("void")
)
)))
@@ -278,9 +305,9 @@ public class ConfigurationUtilTest {
assertEquals(
singletonMap("root.elements.name.child.str", null),
- ConfigurationUtil.nodeToFlatMap(parentSuperRoot, new SuperRoot(emptyMap(), singletonMap(
+ ConfigurationUtil.nodeToFlatMap(parentSuperRoot, new SuperRoot(key -> null, singletonMap(
ParentConfiguration.KEY,
- new ParentNode().changeElements(elements ->
+ (InnerNode)newParentInstance().changeElements(elements ->
elements.delete("name")
)
)))
@@ -290,14 +317,16 @@ public class ConfigurationUtilTest {
/** */
@Test
public void patch() {
- var originalRoot = new ParentNode().changeElements(elements ->
+ var originalRoot = newParentInstance();
+
+ originalRoot.changeElements(elements ->
elements.create("name1", element ->
element.changeChild(child -> child.changeStr("value1"))
)
);
// Updating config.
- ParentNode updatedRoot = ConfigurationUtil.patch(originalRoot, new ParentNode().changeElements(elements ->
+ ParentView updatedRoot = ConfigurationUtil.patch(originalRoot, (TraversableTreeNode)newParentInstance().changeElements(elements ->
elements.update("name1", element ->
element.changeChild(child -> child.changeStr("value2"))
)
@@ -312,7 +341,7 @@ public class ConfigurationUtilTest {
assertEquals("value2", updatedRoot.elements().get("name1").child().str());
// Expanding config.
- ParentNode expandedRoot = ConfigurationUtil.patch(originalRoot, new ParentNode().changeElements(elements ->
+ ParentView expandedRoot = ConfigurationUtil.patch(originalRoot, (TraversableTreeNode)newParentInstance().changeElements(elements ->
elements.update("name2", element ->
element.changeChild(child -> child.changeStr("value2"))
)
@@ -328,7 +357,7 @@ public class ConfigurationUtilTest {
assertEquals("value2", expandedRoot.elements().get("name2").child().str());
// Shrinking config.
- ParentNode shrinkedRoot = ConfigurationUtil.patch(expandedRoot, new ParentNode().changeElements(elements ->
+ ParentView shrinkedRoot = (ParentView)ConfigurationUtil.patch((InnerNode)expandedRoot, (TraversableTreeNode)newParentInstance().changeElements(elements ->
elements.delete("name1")
));
@@ -343,13 +372,17 @@ public class ConfigurationUtilTest {
/** */
@Test
public void cleanupMatchingValues() {
- var curParent = new ParentNode().changeElements(elements -> elements
+ var curParent = newParentInstance();
+
+ curParent.changeElements(elements -> elements
.create("missing", element -> {})
.create("match", element -> element.changeChild(child -> child.changeStr("match")))
.create("mismatch", element -> element.changeChild(child -> child.changeStr("foo")))
);
- var newParent = new ParentNode().changeElements(elements -> elements
+ var newParent = newParentInstance();
+
+ newParent.changeElements(elements -> elements
.create("extra", element -> {})
.create("match", element -> element.changeChild(child -> child.changeStr("match")))
.create("mismatch", element -> element.changeChild(child -> child.changeStr("bar")))
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/validation/ValidationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/validation/ValidationUtilTest.java
index b97a3cb..ef370c8 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/validation/ValidationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/internal/validation/ValidationUtilTest.java
@@ -30,23 +30,40 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.configuration.internal.SuperRoot;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
import org.apache.ignite.configuration.storage.ConfigurationType;
+import org.apache.ignite.configuration.tree.InnerNode;
import org.apache.ignite.configuration.tree.NamedListView;
import org.apache.ignite.configuration.validation.ValidationContext;
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.configuration.validation.Validator;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static org.junit.jupiter.api.Assertions.assertEquals;
/** */
public class ValidationUtilTest {
+ private static ConfigurationAsmGenerator cgen;
+
+ @BeforeAll
+ public static void beforeAll() {
+ cgen = new ConfigurationAsmGenerator();
+
+ cgen.compileRootSchema(ValidatedRootConfigurationSchema.class);
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
/** */
@Target(FIELD)
@Retention(RUNTIME)
@@ -89,18 +106,20 @@ public class ValidationUtilTest {
}
/** */
- private final ValidatedRootNode root = new ValidatedRootNode();
+ private InnerNode root;
/** */
@BeforeEach
public void before() {
+ root = cgen.instantiateNode(ValidatedRootConfigurationSchema.class);
+
ConfigurationUtil.addDefaults(root, root);
}
/** */
@Test
public void validateLeafNode() throws Exception {
- var rootsNode = new SuperRoot(emptyMap(), Map.of(ValidatedRootConfiguration.KEY, root));
+ var rootsNode = new SuperRoot(key -> null, Map.of(ValidatedRootConfiguration.KEY, root));
Validator<LeafValidation, String> validator = new Validator<>() {
@Override public void validate(LeafValidation annotation, ValidationContext<String> ctx) {
@@ -125,7 +144,7 @@ public class ValidationUtilTest {
/** */
@Test
public void validateInnerNode() throws Exception {
- var rootsNode = new SuperRoot(emptyMap(), Map.of(ValidatedRootConfiguration.KEY, root));
+ var rootsNode = new SuperRoot(key -> null, Map.of(ValidatedRootConfiguration.KEY, root));
Validator<InnerValidation, ValidatedChildView> validator = new Validator<>() {
@Override public void validate(InnerValidation annotation, ValidationContext<ValidatedChildView> ctx) {
@@ -150,7 +169,7 @@ public class ValidationUtilTest {
/** */
@Test
public void validateNamedListNode() throws Exception {
- var rootsNode = new SuperRoot(emptyMap(), Map.of(ValidatedRootConfiguration.KEY, root));
+ var rootsNode = new SuperRoot(key -> null, Map.of(ValidatedRootConfiguration.KEY, root));
Validator<NamedListValidation, NamedListView<?>> validator = new Validator<>() {
@Override public void validate(NamedListValidation annotation, ValidationContext<NamedListView<?>> ctx) {
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
index 3e3a1b3..ad2e7c5 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConfigurationArrayTest.java
@@ -20,6 +20,10 @@ package org.apache.ignite.configuration.sample;
import java.util.function.Supplier;
import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.apache.ignite.configuration.internal.util.ConfigurationUtil.leafNodeVisitor;
@@ -39,28 +43,42 @@ public class ConfigurationArrayTest {
public String[] array;
}
+ private static ConfigurationAsmGenerator cgen;
+
+ @BeforeAll
+ public static void beforeAll() {
+ cgen = new ConfigurationAsmGenerator();
+
+ cgen.compileRootSchema(TestArrayConfigurationSchema.class);
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
/**
* Test array node init operation.
*/
@Test
public void testInit() {
- var arrayNode = new TestArrayNode();
+ InnerNode arrayNode = cgen.instantiateNode(TestArrayConfigurationSchema.class);
Supplier<String[]> initialSupplier = () -> {
return new String[]{"test1", "test2"};
};
final String[] initialValue = initialSupplier.get();
- arrayNode.changeArray(initialValue);
+ ((TestArrayChange)arrayNode).changeArray(initialValue);
// test that field is not the same as initialValue
assertNotSame(getArray(arrayNode), initialValue);
// test that init method set values successfully
- assertThat(arrayNode.array(), is(initialSupplier.get()));
+ assertThat(((TestArrayView)arrayNode).array(), is(initialSupplier.get()));
// test that returned array is a copy of the field
- assertNotSame(getArray(arrayNode), arrayNode.array());
+ assertNotSame(getArray(arrayNode), ((TestArrayView)arrayNode).array());
}
/**
@@ -68,23 +86,23 @@ public class ConfigurationArrayTest {
*/
@Test
public void testChange() {
- var arrayNode = new TestArrayNode();
+ InnerNode arrayNode = cgen.instantiateNode(TestArrayConfigurationSchema.class);
Supplier<String[]> changeSupplier = () -> {
return new String[]{"foo", "bar"};
};
final String[] changeValue = changeSupplier.get();
- arrayNode.changeArray(changeValue);
+ ((TestArrayChange)arrayNode).changeArray(changeValue);
// test that field is not the same as initialValue
assertNotSame(getArray(arrayNode), changeValue);
// test that change method set values successfully
- assertThat(arrayNode.array(), is(changeSupplier.get()));
+ assertThat(((TestArrayView)arrayNode).array(), is(changeSupplier.get()));
// test that returned array is a copy of the field
- assertNotSame(getArray(arrayNode), arrayNode.array());
+ assertNotSame(getArray(arrayNode), ((TestArrayView)arrayNode).array());
}
/**
@@ -92,7 +110,7 @@ public class ConfigurationArrayTest {
* @param arrayNode ArrayNode.
* @return Array field value.
*/
- private String[] getArray(TestArrayNode arrayNode) {
+ private String[] getArray(InnerNode arrayNode) {
return (String[])arrayNode.traverseChild("array", leafNodeVisitor());
}
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
index 2867397..0b50243 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/ConstructableTreeNodeTest.java
@@ -19,25 +19,48 @@ package org.apache.ignite.configuration.sample;
import java.util.Collections;
import java.util.NoSuchElementException;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.tree.ConfigurationSource;
import org.apache.ignite.configuration.tree.ConstructableTreeNode;
-import org.apache.ignite.configuration.tree.NamedListNode;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
/** */
public class ConstructableTreeNodeTest {
+ private static ConfigurationAsmGenerator cgen;
+
+ @BeforeAll
+ public static void beforeAll() {
+ cgen = new ConfigurationAsmGenerator();
+
+ cgen.compileRootSchema(TraversableTreeNodeTest.ParentConfigurationSchema.class);
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
+ public static <P extends InnerNode & ParentView & ParentChange> P newParentInstance() {
+ return (P)cgen.instantiateNode(TraversableTreeNodeTest.ParentConfigurationSchema.class);
+ }
+
+ public static <C extends InnerNode & ChildView & ChildChange> C newChildInstance() {
+ return (C)cgen.instantiateNode(TraversableTreeNodeTest.ChildConfigurationSchema.class);
+ }
+
/** */
@Test
public void noKey() {
- var childNode = new ChildNode();
+ var childNode = newChildInstance();
assertThrows(NoSuchElementException.class, () -> childNode.construct("foo", null));
}
@@ -45,7 +68,9 @@ public class ConstructableTreeNodeTest {
/** */
@Test
public void nullSource() {
- var parentNode = new ParentNode().changeChild(child ->
+ var parentNode = newParentInstance();
+
+ parentNode.changeChild(child ->
child.changeStrCfg("value")
)
.changeElements(elements ->
@@ -53,7 +78,7 @@ public class ConstructableTreeNodeTest {
);
// Named list node.
- NamedListNode<NamedElementNode> elements = parentNode.elements();
+ var elements = parentNode.elements();
parentNode.construct("elements", null);
@@ -62,14 +87,14 @@ public class ConstructableTreeNodeTest {
assertEquals(Collections.emptySet(), parentNode.elements().namedListKeys());
// Inner node.
- NamedElementNode element = elements.get("name");
+ NamedElementView element = elements.get("name");
- elements.construct("name", null);
+ ((ConstructableTreeNode)elements).construct("name", null);
assertNull(elements.get("name"));
// Leaf.
- element.construct("strCfg", null);
+ ((ConstructableTreeNode)element).construct("strCfg", null);
assertNull(element.strCfg());
}
@@ -95,7 +120,7 @@ public class ConstructableTreeNodeTest {
/** */
@Test
public void unwrap() {
- var childNode = new ChildNode();
+ var childNode = newChildInstance();
childNode.construct("strCfg", new ConstantConfigurationSource("value"));
@@ -114,7 +139,7 @@ public class ConstructableTreeNodeTest {
@Test
public void descend() {
// Inner node.
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
parentNode.construct("child", new ConfigurationSource() {
@Override public <T> T unwrap(Class<T> clazz) {
@@ -129,9 +154,9 @@ public class ConstructableTreeNodeTest {
assertEquals("value", parentNode.child().strCfg());
// Named list node.
- NamedListNode<NamedElementNode> elementsNode = parentNode.elements();
+ var elementsNode = parentNode.elements();
- elementsNode.construct("name", new ConfigurationSource() {
+ ((ConstructableTreeNode)elementsNode).construct("name", new ConfigurationSource() {
@Override public <T> T unwrap(Class<T> clazz) {
throw new UnsupportedOperationException("unwrap");
}
@@ -148,7 +173,7 @@ public class ConstructableTreeNodeTest {
@Test
public void constructDefault() {
// Inner node with no leaves.
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
assertThrows(NoSuchElementException.class, () -> parentNode.constructDefault("child"));
assertThrows(NoSuchElementException.class, () -> parentNode.constructDefault("elements"));
@@ -156,20 +181,22 @@ public class ConstructableTreeNodeTest {
// Inner node with a leaf.
parentNode.changeElements(elements -> elements.create("name", element -> {}));
- NamedElementNode elementNode = parentNode.elements().get("name");
+ var elementNode = parentNode.elements().get("name");
+
+ ((InnerNode)elementNode).constructDefault("strCfg");
- assertFalse(elementNode.constructDefault("strCfg"));
+ assertNull(elementNode.strCfg());
// Another inner node with leaves.
parentNode.changeChild(child -> {});
- ChildNode child = parentNode.child();
+ var child = parentNode.child();
- assertFalse(child.constructDefault("strCfg"));
+ ((InnerNode)child).constructDefault("strCfg");
assertThrows(NullPointerException.class, () -> child.intCfg());
- assertTrue(child.constructDefault("intCfg"));
+ ((InnerNode)child).constructDefault("intCfg");
assertEquals(99, child.intCfg());
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
index 30cce72..fafcaf8 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/TraversableTreeNodeTest.java
@@ -25,10 +25,16 @@ import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.ConfigValue;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.tree.ConfigurationVisitor;
import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.NamedListChange;
import org.apache.ignite.configuration.tree.NamedListNode;
+import org.apache.ignite.configuration.tree.NamedListView;
+import org.apache.ignite.configuration.tree.TraversableTreeNode;
import org.apache.ignite.configuration.validation.Immutable;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static java.util.Collections.emptySet;
@@ -43,6 +49,28 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/** */
public class TraversableTreeNodeTest {
+ private static ConfigurationAsmGenerator cgen;
+
+ @BeforeAll
+ public static void beforeAll() {
+ cgen = new ConfigurationAsmGenerator();
+
+ cgen.compileRootSchema(ParentConfigurationSchema.class);
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ cgen = null;
+ }
+
+ public static <P extends InnerNode & ParentView & ParentChange> P newParentInstance() {
+ return (P)cgen.instantiateNode(ParentConfigurationSchema.class);
+ }
+
+ public static <C extends InnerNode & ChildView & ChildChange> C newChildInstance() {
+ return (C)cgen.instantiateNode(ChildConfigurationSchema.class);
+ }
+
/** */
@Config
public static class ParentConfigurationSchema {
@@ -83,21 +111,21 @@ public class TraversableTreeNodeTest {
}
/**
- * Test that generated node classes implement generated VIEW, CHANGE and INIT interfaces.
+ * Test that generated node classes implement generated VIEW and CHANGE interfaces.
*/
@Test
public void nodeClassesImplementRequiredInterfaces() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
assertThat(parentNode, instanceOf(ParentView.class));
assertThat(parentNode, instanceOf(ParentChange.class));
- var namedElementNode = new NamedElementNode();
+ var namedElementNode = cgen.instantiateNode(NamedElementConfigurationSchema.class);
assertThat(namedElementNode, instanceOf(NamedElementView.class));
assertThat(namedElementNode, instanceOf(NamedElementChange.class));
- var childNode = new ChildNode();
+ var childNode = newChildInstance();
assertThat(childNode, instanceOf(ChildView.class));
assertThat(childNode, instanceOf(ChildChange.class));
@@ -108,7 +136,7 @@ public class TraversableTreeNodeTest {
*/
@Test
public void changeLeaf() {
- var childNode = new ChildNode();
+ var childNode = newChildInstance();
assertNull(childNode.strCfg());
@@ -122,13 +150,13 @@ public class TraversableTreeNodeTest {
*/
@Test
public void changeInnerChild() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
assertNull(parentNode.child());
parentNode.changeChild(child -> {});
- ChildNode childNode = parentNode.child();
+ ChildView childNode = parentNode.child();
assertNotNull(childNode);
@@ -143,9 +171,9 @@ public class TraversableTreeNodeTest {
*/
@Test
public void changeNamedChild() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
- NamedListNode<NamedElementNode> elementsNode = parentNode.elements();
+ NamedListView<? extends NamedElementView> elementsNode = parentNode.elements();
// Named list node must always be instantiated.
assertNotNull(elementsNode);
@@ -161,21 +189,21 @@ public class TraversableTreeNodeTest {
*/
@Test
public void putRemoveNamedConfiguration() {
- var elementsNode = new NamedListNode<>(NamedElementNode::new);
+ var elementsNode = newParentInstance().elements();
assertEquals(emptySet(), elementsNode.namedListKeys());
- elementsNode.update("keyPut", element -> {});
+ ((NamedListChange<?>)elementsNode).update("keyPut", element -> {});
assertThat(elementsNode.namedListKeys(), hasItem("keyPut"));
- NamedElementNode elementNode = elementsNode.get("keyPut");
+ NamedElementView elementNode = elementsNode.get("keyPut");
assertNotNull(elementNode);
assertNull(elementNode.strCfg());
- elementsNode.update("keyPut", element -> element.changeStrCfg("val"));
+ ((NamedListChange<NamedElementChange>)elementsNode).update("keyPut", element -> element.changeStrCfg("val"));
// Assert that consecutive put methods don't create new object every time.
assertSame(elementNode, elementsNode.get("keyPut"));
@@ -183,9 +211,9 @@ public class TraversableTreeNodeTest {
assertEquals("val", elementNode.strCfg());
// Assert that once you put something into list, removing it makes no sense and hence prohibited.
- assertThrows(IllegalStateException.class, () -> elementsNode.delete("keyPut"));
+ assertThrows(IllegalStateException.class, () -> ((NamedListChange<?>)elementsNode).delete("keyPut"));
- elementsNode.delete("keyRemove");
+ ((NamedListChange<?>)elementsNode).delete("keyRemove");
// Assert that "remove" method creates null element inside of the node.
assertThat(elementsNode.namedListKeys(), hasItem("keyRemove"));
@@ -193,7 +221,7 @@ public class TraversableTreeNodeTest {
assertNull(elementsNode.get("keyRemove"));
// Assert that once you remove something from list, you can't put it back again with different set of fields.
- assertThrows(IllegalStateException.class, () -> elementsNode.update("keyRemove", element -> {}));
+ assertThrows(IllegalStateException.class, () -> ((NamedListChange<?>)elementsNode).update("keyRemove", element -> {}));
}
/**
@@ -201,7 +229,7 @@ public class TraversableTreeNodeTest {
*/
@Test
public void innerNodeAcceptVisitor() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
assertThrows(VisitException.class, () ->
parentNode.accept("root", new ConfigurationVisitor<Void>() {
@@ -217,7 +245,7 @@ public class TraversableTreeNodeTest {
*/
@Test
public void namedListNodeAcceptVisitor() {
- var elementsNode = new NamedListNode<>(NamedElementNode::new);
+ var elementsNode = (TraversableTreeNode)newParentInstance().elements();
assertThrows(VisitException.class, () ->
elementsNode.accept("root", new ConfigurationVisitor<Void>() {
@@ -233,7 +261,7 @@ public class TraversableTreeNodeTest {
*/
@Test
public void traverseChildren() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
List<String> keys = new ArrayList<>(2);
@@ -258,7 +286,7 @@ public class TraversableTreeNodeTest {
keys.clear();
- ChildNode childNode = new ChildNode();
+ var childNode = newChildInstance();
childNode.traverseChildren(new ConfigurationVisitor<Object>() {
@Override public Object visitLeafNode(String key, Serializable val) {
@@ -275,7 +303,7 @@ public class TraversableTreeNodeTest {
*/
@Test
public void traverseSingleChild() {
- var parentNode = new ParentNode();
+ var parentNode = newParentInstance();
// Assert that proper method has been invoked.
assertThrows(VisitException.class, () ->
@@ -300,7 +328,7 @@ public class TraversableTreeNodeTest {
})
);
- var childNode = new ChildNode();
+ var childNode = newChildInstance();
// Assert that proper method has been invoked.
assertThrows(VisitException.class, () ->
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
index 55dff07..bab8049 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/sample/UsageTest.java
@@ -128,12 +128,12 @@ public class UsageTest {
assertEquals(
failureDetectionTimeout,
- registry.getConfiguration(NetworkConfigurationImpl.KEY).discovery().failureDetectionTimeout().value()
+ registry.getConfiguration(NetworkConfiguration.KEY).discovery().failureDetectionTimeout().value()
);
assertEquals(
autoAdjustTimeout,
- registry.getConfiguration(LocalConfigurationImpl.KEY).baseline().autoAdjust().timeout().value()
+ registry.getConfiguration(LocalConfiguration.KEY).baseline().autoAdjust().timeout().value()
);
}
}
diff --git a/modules/configuration/pom.xml b/modules/configuration/pom.xml
index c4e0b6f..6b3fc61 100644
--- a/modules/configuration/pom.xml
+++ b/modules/configuration/pom.xml
@@ -35,6 +35,11 @@
<dependencies>
<dependency>
<groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-bytecode</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
</dependency>
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
index 2bd1094..344a9ab 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
@@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.ignite.configuration.internal.SuperRoot;
import org.apache.ignite.configuration.internal.validation.MemberKey;
@@ -57,7 +58,7 @@ import static org.apache.ignite.configuration.internal.util.ConfigurationUtil.to
/**
* Class that handles configuration changes, by validating them, passing to storage and listening to storage updates.
*/
-public final class ConfigurationChanger {
+public abstract class ConfigurationChanger {
/** */
private final ForkJoinPool pool = new ForkJoinPool(2);
@@ -154,6 +155,25 @@ public final class ConfigurationChanger {
}
/**
+ * Created new {@code Node} object that corresponds to passed root keys root configuration node.
+ * @param rootKey Root key.
+ * @return New {@link InnerNode} instance that represents root.
+ */
+ public abstract InnerNode createRootNode(RootKey<?, ?> rootKey);
+
+ /**
+ * Utility method to create {@link SuperRoot} parameter value.
+ * @return Function that creates root node by root name or returns {@code null} if root name is not found.
+ */
+ @NotNull private Function<String, InnerNode> rootCreator() {
+ return key -> {
+ RootKey<?, ?> rootKey = rootKeys.get(key);
+
+ return rootKey == null ? null : createRootNode(rootKey);
+ };
+ }
+
+ /**
* Registers a storage.
* @param configurationStorage Configuration storage instance.
*/
@@ -174,14 +194,14 @@ public final class ConfigurationChanger {
throw new ConfigurationChangeException("Failed to initialize configuration: " + e.getMessage(), e);
}
- SuperRoot superRoot = new SuperRoot(rootKeys);
+ SuperRoot superRoot = new SuperRoot(rootCreator());
Map<String, ?> dataValuesPrefixMap = toPrefixMap(data.values());
for (RootKey<?, ?> rootKey : storageRootKeys) {
Map<String, ?> rootPrefixMap = (Map<String, ?>)dataValuesPrefixMap.get(rootKey.key());
- InnerNode rootNode = rootKey.createRootNode();
+ InnerNode rootNode = createRootNode(rootKey);
if (rootPrefixMap != null)
fillFromPrefixMap(rootNode, rootPrefixMap);
@@ -211,7 +231,7 @@ public final class ConfigurationChanger {
StorageRoots storageRoots = storagesRootsMap.get(storageType);
SuperRoot superRoot = storageRoots.roots;
- SuperRoot defaultsNode = new SuperRoot(rootKeys);
+ SuperRoot defaultsNode = new SuperRoot(rootCreator());
addDefaults(superRoot, defaultsNode);
@@ -247,7 +267,7 @@ public final class ConfigurationChanger {
ConfigurationSource source,
@Nullable ConfigurationStorage storage
) {
- SuperRoot superRoot = new SuperRoot(rootKeys);
+ SuperRoot superRoot = new SuperRoot(rootCreator());
source.descend(superRoot);
@@ -318,14 +338,14 @@ public final class ConfigurationChanger {
);
}
- return changeInternally(new SuperRoot(rootKeys, changes), storageInstances.get(storagesTypes.iterator().next()));
+ return changeInternally(new SuperRoot(rootCreator(), changes), storageInstances.get(storagesTypes.iterator().next()));
}
/**
* @return Super root chat contains roots belonging to all storages.
*/
public SuperRoot mergedSuperRoot() {
- return new SuperRoot(rootKeys, storagesRootsMap.values().stream().map(roots -> roots.roots).collect(toList()));
+ return new SuperRoot(rootCreator(), storagesRootsMap.values().stream().map(roots -> roots.roots).collect(toList()));
}
/**
@@ -349,7 +369,7 @@ public final class ConfigurationChanger {
// different set of values and different dynamic defaults at the same time.
SuperRoot patchedSuperRoot = patch(curRoots, changes);
- SuperRoot defaultsNode = new SuperRoot(rootKeys);
+ SuperRoot defaultsNode = new SuperRoot(rootCreator());
addDefaults(patchedSuperRoot, defaultsNode);
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
index a1a913e..c79610e 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
@@ -26,9 +26,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.function.BiFunction;
import java.util.function.Function;
-import java.util.function.Supplier;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@@ -36,6 +34,7 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.internal.DynamicConfiguration;
import org.apache.ignite.configuration.internal.RootKeyImpl;
import org.apache.ignite.configuration.internal.SuperRoot;
+import org.apache.ignite.configuration.internal.asm.ConfigurationAsmGenerator;
import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
import org.apache.ignite.configuration.internal.util.KeyNotFoundException;
import org.apache.ignite.configuration.internal.validation.ImmutableValidator;
@@ -64,7 +63,15 @@ public class ConfigurationRegistry {
private final Map<String, DynamicConfiguration<?, ?>> configs = new HashMap<>();
/** */
- private final ConfigurationChanger changer = new ConfigurationChanger(this::notificator);
+ private final ConfigurationChanger changer = new ConfigurationChanger(this::notificator) {
+ /** {@inheritDoc} */
+ @Override public InnerNode createRootNode(RootKey<?, ?> rootKey) {
+ return cgen.instantiateNode(rootKey.schemaClass());
+ }
+ };
+
+ /** */
+ private final ConfigurationAsmGenerator cgen = new ConfigurationAsmGenerator();
{
// Default vaildators implemented in current module.
@@ -85,10 +92,14 @@ public class ConfigurationRegistry {
Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators,
Collection<ConfigurationStorage> configurationStorages
) {
- rootKeys.forEach(rootKey ->
- {
+ rootKeys.forEach(rootKey -> {
+ cgen.compileRootSchema(rootKey.schemaClass());
+
changer.addRootKey(rootKey);
- configs.put(rootKey.key(), (DynamicConfiguration<?, ?>)rootKey.createPublicRoot(changer));
+
+ DynamicConfiguration<?, ?> cfg = cgen.instantiateCfg(rootKey, changer);
+
+ configs.put(rootKey.key(), cfg);
});
validators.forEach(changer::addValidators);
@@ -195,21 +206,12 @@ public class ConfigurationRegistry {
/**
* Method to instantiate a new {@link RootKey} for your configuration root. Invoked in generated code only.
* Does not register this root anywhere, used for static object initialization only.
- *
- * @param rootName Name of the root as described in {@link ConfigurationRoot#rootName()}.
- * @param storageType Storage class as described in {@link ConfigurationRoot#type()}.
- * @param rootSupplier Closure to instantiate internal configuration tree roots.
- * @param publicRootCreator Function to create public user-facing tree instance.
+ * @param schemaClass Class of Configuration Schema that was annotated with {@link ConfigurationRoot}.
* @param <T> Type of public configuration tree.
* @param <V> View type for the root.
* @return Root key instance.
*/
- public static <T extends ConfigurationTree<V, ?>, V> RootKey<T, V> newRootKey(
- String rootName,
- ConfigurationType storageType,
- Supplier<InnerNode> rootSupplier,
- BiFunction<RootKey<T, V>, ConfigurationChanger, T> publicRootCreator
- ) {
- return new RootKeyImpl<>(rootName, storageType, rootSupplier, publicRootCreator);
+ public static <T extends ConfigurationTree<V, ?>, V> RootKey<T, V> newRootKey(Class<?> schemaClass) {
+ return new RootKeyImpl<>(schemaClass);
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/RootKey.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/RootKey.java
index b4ff0cf..29157d4 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/RootKey.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/RootKey.java
@@ -18,7 +18,6 @@
package org.apache.ignite.configuration;
import org.apache.ignite.configuration.storage.ConfigurationType;
-import org.apache.ignite.configuration.tree.InnerNode;
/** */
public abstract class RootKey<T extends ConfigurationTree<VIEW, ?>, VIEW> {
@@ -33,13 +32,7 @@ public abstract class RootKey<T extends ConfigurationTree<VIEW, ?>, VIEW> {
protected abstract ConfigurationType type();
/**
- * @return New instance of the inner node for the root.
+ * @return Schema class for the root.
*/
- protected abstract InnerNode createRootNode();
-
- /**
- * @param changer Configuration changer instance.
- * @return New instance of the public tree for the root.
- */
- protected abstract T createPublicRoot(ConfigurationChanger changer);
+ public abstract Class<?> schemaClass();
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
index 9ba0d80..79efb43 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* This annotation marks configuration schema field as a configuration tree node.
@@ -37,7 +37,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
* </code></pre>
*/
@Target({ FIELD })
-@Retention(SOURCE)
+@Retention(RUNTIME)
@Documented
public @interface ConfigValue {
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
index e8a5e86..d5864c1 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/NamedConfigValue.java
@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
import org.apache.ignite.configuration.internal.NamedListConfiguration;
import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* This annotation denotes configuration schema fields that are dynamically created and mapped by name.
@@ -43,7 +43,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
* </code></pre>
*/
@Target({ FIELD })
-@Retention(SOURCE)
+@Retention(RUNTIME)
@Documented
public @interface NamedConfigValue {
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
index a1b04e7..c6f15fd 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/Value.java
@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
import org.apache.ignite.configuration.internal.DynamicProperty;
import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* This annotation marks configuration schema field as a configuration tree leaf.
@@ -40,7 +40,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
* </ul>
*/
@Target(FIELD)
-@Retention(SOURCE)
+@Retention(RUNTIME)
@Documented
public @interface Value {
/**
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
index 96fbb16..9b4f39a 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/ConfigurationNode.java
@@ -33,7 +33,7 @@ import org.apache.ignite.configuration.tree.TraversableTreeNode;
/**
* Super class for dynamic configuration tree nodes. Has all common data and value retrieving algorithm in it.
*/
-abstract class ConfigurationNode<VIEW, CHANGE> implements ConfigurationProperty<VIEW, CHANGE> {
+public abstract class ConfigurationNode<VIEW, CHANGE> implements ConfigurationProperty<VIEW, CHANGE> {
/** Listeners of property update. */
protected final List<ConfigurationListener<VIEW>> updateListeners = new CopyOnWriteArrayList<>();
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
index 3177d4a..03073ae 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicConfiguration.java
@@ -67,21 +67,11 @@ public abstract class DynamicConfiguration<VIEW, CHANGE> extends ConfigurationNo
members.put(member.key(), member);
}
- /**
- * Add new configuration member.
- *
- * @param member Configuration member (leaf or node).
- * @param <M> Type of member.
- */
- protected final <M extends DynamicProperty<?>> void add(M member) {
- members.put(member.key(), member);
- }
-
/** {@inheritDoc} */
@Override public final Future<Void> change(Consumer<CHANGE> change) {
Objects.requireNonNull(change, "Configuration consumer cannot be null.");
- InnerNode rootNodeChange = ((RootKeyImpl)rootKey).createRootNode();
+ InnerNode rootNodeChange = changer.createRootNode(rootKey);
if (keys.size() == 1) {
// Current node is a root.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
index b163501..1b5bcb7 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/DynamicProperty.java
@@ -60,7 +60,7 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
@Override public Future<Void> update(T newValue) throws ConfigurationValidationException {
Objects.requireNonNull(newValue, "Configuration value cannot be null.");
- InnerNode rootNodeChange = ((RootKeyImpl)rootKey).createRootNode();
+ InnerNode rootNodeChange = changer.createRootNode(rootKey);
assert keys instanceof RandomAccess;
assert !keys.isEmpty();
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/RootKeyImpl.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/RootKeyImpl.java
index f8e04f4..69a2ec4 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/RootKeyImpl.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/RootKeyImpl.java
@@ -17,14 +17,10 @@
package org.apache.ignite.configuration.internal;
-import java.util.function.BiFunction;
-import java.util.function.Supplier;
-import org.apache.ignite.configuration.ConfigurationChanger;
import org.apache.ignite.configuration.ConfigurationTree;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.storage.ConfigurationType;
-import org.apache.ignite.configuration.tree.InnerNode;
/** */
public class RootKeyImpl<T extends ConfigurationTree<VIEW, ?>, VIEW> extends RootKey<T, VIEW> {
@@ -35,27 +31,20 @@ public class RootKeyImpl<T extends ConfigurationTree<VIEW, ?>, VIEW> extends Roo
private final ConfigurationType storageType;
/** */
- private final Supplier<InnerNode> rootSupplier;
-
- /** */
- private final BiFunction<RootKey<T, VIEW>, ConfigurationChanger, T> publicRootCreator;
+ private final Class<?> schemaClass;
/**
- * @param rootName Name of the root as described in {@link ConfigurationRoot#rootName()}.
- * @param storageType Storage class as described in {@link ConfigurationRoot#type()}.
- * @param rootSupplier Closure to instantiate internal configuration tree roots.
- * @param publicRootCreator Function to create a public user-facing tree instance.
+ * @param schemaClass Class of the configuration schema.
*/
- public RootKeyImpl(
- String rootName,
- ConfigurationType storageType,
- Supplier<InnerNode> rootSupplier,
- BiFunction<RootKey<T, VIEW>, ConfigurationChanger, T> publicRootCreator
- ) {
- this.rootName = rootName;
- this.storageType = storageType;
- this.rootSupplier = rootSupplier;
- this.publicRootCreator = publicRootCreator;
+ public RootKeyImpl(Class<?> schemaClass) {
+ this.schemaClass = schemaClass;
+
+ ConfigurationRoot rootAnnotation = schemaClass.getAnnotation(ConfigurationRoot.class);
+
+ assert rootAnnotation != null;
+
+ this.rootName = rootAnnotation.rootName();
+ this.storageType = rootAnnotation.type();
}
/** {@inheritDoc} */
@@ -69,12 +58,7 @@ public class RootKeyImpl<T extends ConfigurationTree<VIEW, ?>, VIEW> extends Roo
}
/** {@inheritDoc} */
- @Override protected InnerNode createRootNode() {
- return rootSupplier.get();
- }
-
- /** {@inheritDoc} */
- @Override protected T createPublicRoot(ConfigurationChanger changer) {
- return publicRootCreator.apply(this, changer);
+ @Override public Class<?> schemaClass() {
+ return schemaClass;
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
index 991cbd0..dbea55b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
@@ -22,6 +22,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.function.Function;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.tree.ConfigurationSource;
import org.apache.ignite.configuration.tree.ConfigurationVisitor;
@@ -33,39 +34,39 @@ public final class SuperRoot extends InnerNode {
private final SortedMap<String, InnerNode> roots = new TreeMap<>();
/** */
- private final Map<String, RootKey<?, ?>> allRootKeys;
+ private final Function<String, InnerNode> nodeCreator;
/** Copy constructor. */
private SuperRoot(SuperRoot superRoot) {
roots.putAll(superRoot.roots);
- allRootKeys = superRoot.allRootKeys;
+ nodeCreator = superRoot.nodeCreator;
}
/**
- * @param rootKeys Shared map of all available root keys.
+ * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found
*/
- public SuperRoot(Map<String, RootKey<?, ?>> rootKeys) {
- allRootKeys = rootKeys;
+ public SuperRoot(Function<String, InnerNode> nodeCreator) {
+ this.nodeCreator = nodeCreator;
}
/**
- * @param rootKeys Shared map of all available root keys.
+ * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found
* @param roots Map of roots belonging to this super root.
*/
- public SuperRoot(Map<String, RootKey<?, ?>> rootKeys, Map<RootKey<?, ?>, InnerNode> roots) {
- allRootKeys = rootKeys;
+ public SuperRoot(Function<String, InnerNode> nodeCreator, Map<RootKey<?, ?>, InnerNode> roots) {
+ this.nodeCreator = nodeCreator;
for (Map.Entry<RootKey<?, ?>, InnerNode> entry : roots.entrySet())
this.roots.put(entry.getKey().key(), entry.getValue());
}
/**
- * @param rootKeys Shared map of all available root keys.
+ * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found
* @param superRoots List of super roots to merge into even more superior root.
*/
- public SuperRoot(Map<String, RootKey<?, ?>> rootKeys, List<SuperRoot> superRoots) {
- this(rootKeys);
+ public SuperRoot(Function<String, InnerNode> nodeCreator, List<SuperRoot> superRoots) {
+ this(nodeCreator);
for (SuperRoot superRoot : superRoots)
roots.putAll(superRoot.roots);
@@ -78,7 +79,6 @@ public final class SuperRoot extends InnerNode {
*/
public void addRoot(RootKey<?, ?> rootKey, InnerNode root) {
assert !roots.containsKey(rootKey.key()) : rootKey.key() + " : " + roots;
- assert allRootKeys.get(rootKey.key()) == rootKey : rootKey.key() + " : " + allRootKeys;
roots.put(rootKey.key(), root);
}
@@ -112,14 +112,16 @@ public final class SuperRoot extends InnerNode {
@Override public void construct(String key, ConfigurationSource src) throws NoSuchElementException {
assert src != null;
- RootKeyImpl<?, ?> rootKey = (RootKeyImpl<?, ?>)allRootKeys.get(key);
-
- if (rootKey == null)
- throw new NoSuchElementException(key);
-
InnerNode root = roots.get(key);
- root = root == null ? rootKey.createRootNode() : root.copy();
+ if (root == null) {
+ root = nodeCreator.apply(key);
+
+ if (root == null)
+ throw new NoSuchElementException(key);
+ }
+ else
+ root = root.copy();
roots.put(key, root);
@@ -127,7 +129,7 @@ public final class SuperRoot extends InnerNode {
}
/** {@inheritDoc} */
- @Override public boolean constructDefault(String key) throws NoSuchElementException {
+ @Override public void constructDefault(String key) throws NoSuchElementException {
throw new NoSuchElementException(key);
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/ConfigurationAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/ConfigurationAsmGenerator.java
new file mode 100644
index 0000000..8854545
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/ConfigurationAsmGenerator.java
@@ -0,0 +1,1139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.internal.asm;
+
+import java.io.Serializable;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.ClassDefinition;
+import com.facebook.presto.bytecode.ClassGenerator;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodDefinition;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.Variable;
+import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import org.apache.ignite.configuration.ConfigurationChanger;
+import org.apache.ignite.configuration.ConfigurationProperty;
+import org.apache.ignite.configuration.ConfigurationValue;
+import org.apache.ignite.configuration.NamedConfigurationTree;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.internal.DynamicConfiguration;
+import org.apache.ignite.configuration.internal.DynamicProperty;
+import org.apache.ignite.configuration.internal.NamedListConfiguration;
+import org.apache.ignite.configuration.tree.ConfigurationSource;
+import org.apache.ignite.configuration.tree.ConfigurationVisitor;
+import org.apache.ignite.configuration.tree.ConstructableTreeNode;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.NamedListNode;
+import org.apache.ignite.configuration.tree.NamedListView;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static com.facebook.presto.bytecode.Access.FINAL;
+import static com.facebook.presto.bytecode.Access.PRIVATE;
+import static com.facebook.presto.bytecode.Access.PUBLIC;
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.Access.SYNTHETIC;
+import static com.facebook.presto.bytecode.Parameter.arg;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromJavaClassName;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantClass;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantNull;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantString;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.inlineIf;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeDynamic;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeStatic;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.isNull;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newInstance;
+import static java.lang.invoke.MethodType.methodType;
+import static java.util.Collections.singleton;
+import static java.util.EnumSet.of;
+import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
+import static org.objectweb.asm.Type.getMethodDescriptor;
+import static org.objectweb.asm.Type.getMethodType;
+import static org.objectweb.asm.Type.getType;
+
+/**
+ * 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.
+ */
+public class ConfigurationAsmGenerator {
+ /** {@link LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)} */
+ private static final Method LAMBDA_METAFACTORY;
+
+ /** {@link Consumer#accept(Object)}*/
+ private static final Method ACCEPT;
+
+ /** {@link ConfigurationVisitor#visitLeafNode(String, Serializable)} */
+ private static final Method VISIT_LEAF;
+
+ /** {@link ConfigurationVisitor#visitInnerNode(String, InnerNode)} */
+ private static final Method VISIT_INNER;
+
+ /** {@link ConfigurationVisitor#visitNamedListNode(String, NamedListNode)} */
+ private static final Method VISIT_NAMED;
+
+ /** {@link ConfigurationSource#unwrap(Class)} */
+ private static final Method UNWRAP;
+
+ /** {@link ConfigurationSource#descend(ConstructableTreeNode)} */
+ private static final Method DESCEND;
+
+ /** {@link ConstructableTreeNode#copy()} */
+ private static final Method COPY;
+
+ /** {@link DynamicConfiguration#DynamicConfiguration(List, String, RootKey, ConfigurationChanger)} */
+ private static final Constructor DYNAMIC_CONFIGURATION_CTOR;
+
+ /** {@link DynamicConfiguration#add(ConfigurationProperty)} */
+ private static final Method DYNAMIC_CONFIGURATION_ADD;
+
+ static {
+ try {
+ LAMBDA_METAFACTORY = LambdaMetafactory.class.getDeclaredMethod(
+ "metafactory",
+ Lookup.class,
+ String.class,
+ MethodType.class,
+ MethodType.class,
+ MethodHandle.class,
+ MethodType.class
+ );
+
+ ACCEPT = Consumer.class.getDeclaredMethod("accept", Object.class);
+
+ VISIT_LEAF = ConfigurationVisitor.class.getDeclaredMethod("visitLeafNode", String.class, Serializable.class);
+
+ VISIT_INNER = ConfigurationVisitor.class.getDeclaredMethod("visitInnerNode", String.class, InnerNode.class);
+
+ VISIT_NAMED = ConfigurationVisitor.class.getDeclaredMethod("visitNamedListNode", String.class, NamedListNode.class);
+
+ UNWRAP = ConfigurationSource.class.getDeclaredMethod("unwrap", Class.class);
+
+ DESCEND = ConfigurationSource.class.getDeclaredMethod("descend", ConstructableTreeNode.class);
+
+ COPY = ConstructableTreeNode.class.getDeclaredMethod("copy");
+
+ DYNAMIC_CONFIGURATION_CTOR = DynamicConfiguration.class.getDeclaredConstructor(
+ List.class,
+ String.class,
+ RootKey.class,
+ ConfigurationChanger.class
+ );
+
+ DYNAMIC_CONFIGURATION_ADD = DynamicConfiguration.class.getDeclaredMethod(
+ "add",
+ ConfigurationProperty.class
+ );
+ }
+ catch (NoSuchMethodException nsme) {
+ throw new ExceptionInInitializerError(nsme);
+ }
+ }
+
+ /** Information about schema classes - bunch of names and dynamically compiled internal classes. */
+ private final Map<Class<?>, SchemaClassesInfo> schemasInfo = new HashMap<>();
+
+ /** Class generator instance. */
+ private final ClassGenerator generator = ClassGenerator.classGenerator(this.getClass().getClassLoader());
+
+ /**
+ * Creates new instance of {@code *Node} class corresponding to the given Configuration Schema.
+ * @param schemaClass Configuration Schema class.
+ * @return Node instance.
+ */
+ public synchronized InnerNode instantiateNode(Class<?> schemaClass) {
+ SchemaClassesInfo info = schemasInfo.get(schemaClass);
+
+ assert info != null && info.nodeClass != null : schemaClass;
+
+ try {
+ Constructor<? extends InnerNode> constructor = info.nodeClass.getConstructor();
+
+ assert constructor.canAccess(null);
+
+ return constructor.newInstance();
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Creates new instance of {@code *Configuration} class corresponding to the given Configuration Schema.
+ * @param rootKey Root key of the configuration root.
+ * @param changer Configuration changer instance to pass into constructor.
+ * @return Configuration instance.
+ */
+ public synchronized DynamicConfiguration<?, ?> instantiateCfg(
+ RootKey<?, ?> rootKey,
+ ConfigurationChanger changer
+ ) {
+ SchemaClassesInfo info = schemasInfo.get(rootKey.schemaClass());
+
+ assert info != null && info.cfgImplClass != null;
+
+ try {
+ Constructor<? extends DynamicConfiguration<?, ?>> constructor = info.cfgImplClass.getConstructor(
+ List.class,
+ String.class,
+ RootKey.class,
+ ConfigurationChanger.class
+ );
+
+ assert constructor.canAccess(null);
+
+ return constructor.newInstance(Collections.emptyList(), rootKey.key(), rootKey, changer);
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Generates, defines, loads and initializes all dynamic classes required for the given Configuration Schema.
+ * @param rootSchemaClass Class of the root Configuration Schema.
+ */
+ public synchronized void compileRootSchema(Class<?> rootSchemaClass) {
+ if (schemasInfo.containsKey(rootSchemaClass))
+ return; // Already compiled.
+
+ Queue<Class<?>> compileQueue = new LinkedList<>();
+ compileQueue.add(rootSchemaClass);
+
+ schemasInfo.put(rootSchemaClass, new SchemaClassesInfo(rootSchemaClass));
+
+ Set<Class<?>> schemas = new HashSet<>();
+ List<ClassDefinition> definitions = new ArrayList<>();
+
+ while (!compileQueue.isEmpty()) {
+ Class<?> schemaClass = compileQueue.poll();
+
+ assert schemaClass.isAnnotationPresent(ConfigurationRoot.class)
+ || schemaClass.isAnnotationPresent(Config.class)
+ : schemaClass + " is not properly annotated";
+
+ assert schemasInfo.containsKey(schemaClass);
+
+ for (Field field : schemaClass.getDeclaredFields()) {
+ if (isConfigValue(field) || isNamedConfigValue(field)) {
+ Class<?> subSchemaClass = field.getType();
+
+ if (!schemasInfo.containsKey(subSchemaClass)) {
+ compileQueue.offer(subSchemaClass);
+ schemasInfo.put(subSchemaClass, new SchemaClassesInfo(subSchemaClass));
+ }
+ }
+ }
+
+ schemas.add(schemaClass);
+ definitions.add(createNodeClass(schemaClass));
+ definitions.add(createCfgImplClass(schemaClass));
+ }
+
+ Map<String, Class<?>> definedClasses = generator.defineClasses(definitions);
+
+ for (Class<?> schemaClass : schemas) {
+ SchemaClassesInfo info = schemasInfo.get(schemaClass);
+
+ info.nodeClass = (Class<? extends InnerNode>)definedClasses.get(info.nodeClassName);
+ info.cfgImplClass = (Class<? extends DynamicConfiguration<?, ?>>)definedClasses.get(info.cfgImplClassName);
+ }
+ }
+
+ private ClassDefinition createNodeClass(Class<?> schemaClass) {
+ SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
+
+ // Node class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ of(PUBLIC, FINAL),
+ internalName(schemaClassInfo.nodeClassName),
+ type(InnerNode.class),
+ typeFromJavaClassName(schemaClassInfo.viewClassName),
+ typeFromJavaClassName(schemaClassInfo.changeClassName)
+ );
+
+ // Spec field.
+ FieldDefinition specField = classDef.declareField(of(PRIVATE, FINAL), "_spec", schemaClass);
+
+ // org.apache.ignite.configuration.tree.InnerNode#schemaType
+ addNodeSchemaTypeMethod(classDef, specField);
+
+ // Cached array of reflected fields. Helps to avoid unnecessary clonings.
+ Field[] schemaFields = schemaClass.getDeclaredFields();
+
+ // Define the rest of the fields.
+ Map<String, FieldDefinition> fieldDefs = new HashMap<>();
+
+ for (Field schemaField : schemaFields) {
+ assert isValue(schemaField) || isConfigValue(schemaField) || isNamedConfigValue(schemaField) : schemaField;
+
+ fieldDefs.put(schemaField.getName(), addNodeField(classDef, schemaField));
+ }
+
+ // Constructor.
+ addNodeConstructor(classDef, schemaClass, specField, schemaFields, fieldDefs);
+
+ // VIEW and CHANGE methods.
+ for (Field schemaField : schemaFields) {
+ FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+
+ addNodeViewMethod(classDef, schemaField, fieldDef);
+
+ addNodeChangeMethod(classDef, schemaField, fieldDef, schemaClassInfo);
+ }
+
+ // traverseChildren
+ addNodeTraverseChildrenMethod(classDef, schemaFields, fieldDefs);
+
+ // traverseChild
+ addNodeTraverseChildMethod(classDef, schemaFields, fieldDefs);
+
+ // construct
+ addNodeConstructMethod(classDef, schemaFields, fieldDefs);
+
+ // constructDefault
+ addNodeConstructDefaultMethod(classDef, specField, schemaFields, fieldDefs);
+
+ return classDef;
+ }
+
+ /**
+ * Checks whether configuration schema field represents primitive configuration value.
+ * @param schemaField Configuration Schema class field.
+ * @return {@code true} if field represents primitive configuration.
+ */
+ private static boolean isValue(Field schemaField) {
+ return schemaField.isAnnotationPresent(Value.class);
+ }
+
+ /**
+ * Checks whether configuration schema field represents regular configuration value.
+ * @param schemaField Configuration Schema class field.
+ * @return {@code true} if field represents regular configuration.
+ */
+ private static boolean isConfigValue(Field schemaField) {
+ return schemaField.isAnnotationPresent(ConfigValue.class);
+ }
+
+ /**
+ * Checks whether configuration schema field represents named list configuration value.
+ * @param schemaField Configuration Schema class field.
+ * @return {@code true} if field represents named list configuration.
+ */
+ private static boolean isNamedConfigValue(Field schemaField) {
+ return schemaField.isAnnotationPresent(NamedConfigValue.class);
+ }
+
+ /**
+ * Add {@link InnerNode#schemaType()} method implementation to the class. It looks like the following code:
+ * <pre><code>
+ * public Class schemaType() {
+ * return this._spec.getClass();
+ * }
+ * </code></pre>
+ * @param classDef Class definition.
+ * @param specField Field definition of the {@code _spec} field.
+ */
+ private void addNodeSchemaTypeMethod(ClassDefinition classDef, FieldDefinition specField) {
+ MethodDefinition schemaTypeMtd = classDef.declareMethod(of(PUBLIC), "schemaType", type(Class.class));
+
+ schemaTypeMtd.getBody().append(
+ schemaTypeMtd.getThis().getField(specField).invoke("getClass", Class.class)
+ ).retObject();
+ }
+
+ /**
+ * Declares field that corresponds to configuration value. Depending on the schema, 3 options possible:
+ * <ul>
+ * <li>
+ * {@code @Value public type fieldName}<br/>becomes<br/>
+ * {@code private BoxedType fieldName}
+ * </li>
+ * <li>
+ * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
+ * {@code private MyNode fieldName}
+ * </li>
+ * <li>
+ * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
+ * {@code private NamedListNode fieldName}
+ * </li>
+ * </ul>
+ * @param classDef Node class definition.
+ * @param schemaField Configuration Schema class field.
+ * @return Declared field definition.
+ */
+ private FieldDefinition addNodeField(ClassDefinition classDef, Field schemaField) {
+ Class<?> schemaFieldClass = schemaField.getType();
+
+ ParameterizedType nodeFieldType;
+
+ if (isValue(schemaField))
+ nodeFieldType = type(box(schemaFieldClass));
+ else if (isConfigValue(schemaField))
+ nodeFieldType = typeFromJavaClassName(schemasInfo.get(schemaFieldClass).nodeClassName);
+ else
+ nodeFieldType = type(NamedListNode.class);
+
+ return classDef.declareField(of(PRIVATE), schemaField.getName(), nodeFieldType);
+ }
+
+ /**
+ * Implements default constructor for the node class. It initializes {@code _spec} field and every other field
+ * that represents named list configuration.
+ * @param classDef Node class definition.
+ * @param schemaClass Configuration Schema class.
+ * @param specField Field definition for the {@code _spec} field of the node class.
+ * @param schemaFields Configuration Schema class fields.
+ * @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
+ */
+ private void addNodeConstructor(
+ ClassDefinition classDef,
+ Class<?> schemaClass,
+ FieldDefinition specField,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition ctor = classDef.declareConstructor(of(PUBLIC));
+
+ // super();
+ ctor.getBody().append(ctor.getThis()).invokeConstructor(InnerNode.class);
+
+ // this._spec = new MyConfigurationSchema();
+ ctor.getBody().append(ctor.getThis().setField(specField, newInstance(schemaClass)));
+
+ for (Field schemaField : schemaFields) {
+ if (!isNamedConfigValue(schemaField))
+ continue;
+
+ SchemaClassesInfo fieldClassNames = new SchemaClassesInfo(schemaField.getType());
+
+ // this.values = new NamedListNode<>(ValueNode::new);
+ ctor.getBody().append(ctor.getThis().setField(
+ fieldDefs.get(schemaField.getName()),
+ newInstance(NamedListNode.class, newNamedListElementLambda(fieldClassNames.nodeClassName))
+ ));
+ }
+
+ // return;
+ ctor.getBody().ret();
+ }
+
+ /**
+ * Implements getter method from {@code VIEW} interface. It returns field value, possibly unboxed or cloned,
+ * depending on type.
+ * @param classDef Node class definition.
+ * @param schemaField Configuration Schema class field.
+ * @param fieldDefs Field definition.
+ */
+ private void addNodeViewMethod(
+ ClassDefinition classDef,
+ Field schemaField,
+ FieldDefinition fieldDef
+ ) {
+ Class<?> schemaFieldType = schemaField.getType();
+
+ ParameterizedType returnType;
+
+ // Return type is either corresponding VIEW type or the same type as declared in schema.
+ if (isConfigValue(schemaField))
+ returnType = typeFromJavaClassName(schemasInfo.get(schemaFieldType).viewClassName);
+ else if (isNamedConfigValue(schemaField))
+ returnType = type(NamedListView.class);
+ else
+ returnType = type(schemaFieldType);
+
+ String fieldName = schemaField.getName();
+
+ MethodDefinition viewMtd = classDef.declareMethod(
+ of(PUBLIC),
+ fieldName,
+ returnType
+ );
+
+ // result = this.field;
+ viewMtd.getBody().append(viewMtd.getThis().getField(fieldDef));
+
+ // result = Box.boxValue(result); // Unboxing.
+ if (schemaFieldType.isPrimitive()) {
+ viewMtd.getBody().invokeVirtual(
+ box(schemaFieldType),
+ schemaFieldType.getSimpleName() + "Value",
+ schemaFieldType
+ );
+ }
+
+ // retuls = result.clone();
+ if (schemaFieldType.isArray())
+ viewMtd.getBody().invokeVirtual(schemaFieldType, "clone", Object.class).checkCast(schemaFieldType);
+
+ // return result;
+ viewMtd.getBody().ret(schemaFieldType);
+ }
+
+ /**
+ * Implements changer method from {@code CHANGE} interface.
+ * @param classDef Node class definition.
+ * @param schemaField Configuration Schema class field.
+ * @param fieldDef Field definition.
+ * @param schemaClassInfo {@link SchemaClassesInfo} for Configuration Schema that contains original field.
+ */
+ private void addNodeChangeMethod(
+ ClassDefinition classDef,
+ Field schemaField,
+ FieldDefinition fieldDef,
+ SchemaClassesInfo schemaClassInfo
+ ) {
+ Class<?> schemaFieldType = schemaField.getType();
+
+ MethodDefinition changeMtd = classDef.declareMethod(
+ of(PUBLIC),
+ "change" + capitalize(schemaField.getName()),
+ typeFromJavaClassName(schemaClassInfo.changeClassName),
+ // Change argument type is a Consumer for all inner or named fields.
+ arg("change", isValue(schemaField) ? type(schemaFieldType) : type(Consumer.class))
+ );
+
+ BytecodeBlock changeBody = changeMtd.getBody();
+
+ if (isValue(schemaField)) {
+ // newValue = change;
+ BytecodeExpression newValue = changeMtd.getScope().getVariable("change");
+
+ // newValue = Box.valueOf(newValue); // Boxing.
+ if (schemaFieldType.isPrimitive())
+ newValue = invokeStatic(fieldDef.getType(), "valueOf", fieldDef.getType(), singleton(newValue));
+
+ // newValue = newValue.clone();
+ if (schemaFieldType.isArray())
+ newValue = newValue.invoke("clone", Object.class).cast(schemaFieldType);
+
+ // this.field = newValue;
+ changeBody.append(changeMtd.getThis().setField(fieldDef, newValue));
+ }
+ else {
+ // @ConfigValue fields might be null and have to be initialized before passing into closure.
+ if (isConfigValue(schemaField)) {
+ // if (this.field == null) this.field = new ValueNode();
+ changeBody.append(new IfStatement()
+ .condition(isNull(changeMtd.getThis().getField(fieldDef)))
+ .ifTrue(changeMtd.getThis().setField(fieldDef, newInstance(fieldDef.getType())))
+ );
+ }
+
+ // change.accept(this.field);
+ changeBody.append(changeMtd.getScope().getVariable("change").invoke(
+ ACCEPT,
+ changeMtd.getThis().getField(fieldDef)
+ ));
+ }
+
+ // return this;
+ changeBody.append(changeMtd.getThis()).retObject();
+ }
+
+ /**
+ * Implements {@link InnerNode#traverseChildren(ConfigurationVisitor)} method.
+ * @param classDef Class definition.
+ * @param schemaFields Array of all schema fields that represent configurations.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ */
+ private void addNodeTraverseChildrenMethod(
+ ClassDefinition classDef,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition traverseChildrenMtd = classDef.declareMethod(
+ of(PUBLIC),
+ "traverseChildren",
+ type(void.class),
+ arg("visitor", type(ConfigurationVisitor.class))
+ ).addException(NoSuchElementException.class);
+
+ BytecodeBlock mtdBody = traverseChildrenMtd.getBody();
+
+ for (Field schemaField : schemaFields) {
+ FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+
+ // Visit every field. Returned value isn't used so we pop it off the stack.
+ mtdBody.append(invokeVisit(traverseChildrenMtd, schemaField, fieldDef).pop());
+ }
+
+ mtdBody.ret();
+ }
+
+ /**
+ * Implements {@link InnerNode#traverseChild(String, ConfigurationVisitor)} method.
+ * @param classDef Class definition.
+ * @param schemaFields Array of all schema fields that represent configurations.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ */
+ private void addNodeTraverseChildMethod(
+ ClassDefinition classDef,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition traverseChildMtd = classDef.declareMethod(
+ of(PUBLIC),
+ "traverseChild",
+ type(Object.class),
+ arg("key", type(String.class)),
+ arg("visitor", type(ConfigurationVisitor.class))
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = traverseChildMtd.getScope().getVariable("key");
+
+ // Here we have a switch by passed kay parameter.
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(traverseChildMtd.getScope())
+ .expression(keyVar);
+
+ for (Field schemaField : schemaFields) {
+ String fieldName = schemaField.getName();
+
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ // Visit result should be immediately returned.
+ switchBuilder.addCase(fieldName, invokeVisit(traverseChildMtd, schemaField, fieldDef).retObject());
+ }
+
+ // Default option is to throw "NoSuchElementException(key)".
+ switchBuilder.defaultCase(new BytecodeBlock()
+ .append(newInstance(NoSuchElementException.class, keyVar))
+ .throwObject()
+ );
+
+ // No default "return" statement required.
+ traverseChildMtd.getBody().append(switchBuilder.build());
+ }
+
+ /**
+ * Creates bytecode block that invokes one of {@link ConfigurationVisitor}'s methods.
+ * @param mtd Method definition, either {@link InnerNode#traverseChildren(ConfigurationVisitor)} or
+ * {@link InnerNode#traverseChild(String, ConfigurationVisitor)} defined in {@code *Node} class.
+ * @param schemaField Configuration Schema field to visit.
+ * @param fieldDef Field definition from current class.
+ * @return Bytecode block that invokes "visit*" method.
+ */
+ private BytecodeBlock invokeVisit(MethodDefinition mtd, Field schemaField, FieldDefinition fieldDef) {
+ Method visitMethod;
+
+ if (isValue(schemaField))
+ visitMethod = VISIT_LEAF;
+ else if (isConfigValue(schemaField))
+ visitMethod = VISIT_INNER;
+ else
+ visitMethod = VISIT_NAMED;
+
+ return new BytecodeBlock().append(mtd.getScope().getVariable("visitor").invoke(
+ visitMethod,
+ constantString(fieldDef.getName()),
+ mtd.getThis().getField(fieldDef)
+ ));
+ }
+
+ /**
+ * Implements {@link ConstructableTreeNode#construct(String, ConfigurationSource)} method.
+ * @param classDef Class definition.
+ * @param schemaFields Array of all schema fields that represent configurations.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ */
+ private void addNodeConstructMethod(
+ ClassDefinition classDef,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition constructMtd = classDef.declareMethod(
+ of(PUBLIC),
+ "construct",
+ type(void.class),
+ arg("key", String.class),
+ arg("src", ConfigurationSource.class)
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = constructMtd.getScope().getVariable("key");
+ Variable srcVar = constructMtd.getScope().getVariable("src");
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructMtd.getScope())
+ .expression(keyVar);
+
+ for (Field schemaField : schemaFields) {
+ FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+
+ BytecodeBlock caseClause = new BytecodeBlock();
+
+ switchBuilder.addCase(schemaField.getName(), caseClause);
+
+ // this.field = src == null ? null : src.unwrap(FieldType.class);
+ if (isValue(schemaField)) {
+ caseClause.append(constructMtd.getThis().setField(fieldDef, inlineIf(
+ isNull(srcVar),
+ constantNull(fieldDef.getType()),
+ srcVar.invoke(UNWRAP, constantClass(fieldDef.getType())).cast(fieldDef.getType())
+ )));
+ }
+ // this.field = src == null ? null : src.descend(field = (field == null ? new FieldType() : field.copy()));
+ else if (isConfigValue(schemaField)) {
+ caseClause.append(new IfStatement()
+ .condition(isNull(srcVar))
+ .ifTrue(constructMtd.getThis().setField(fieldDef, constantNull(fieldDef.getType())))
+ .ifFalse(new BytecodeBlock()
+ .append(constructMtd.getThis().setField(fieldDef, inlineIf(
+ isNull(constructMtd.getThis().getField(fieldDef)),
+ newInstance(fieldDef.getType()),
+ constructMtd.getThis().getField(fieldDef).invoke(COPY).cast(fieldDef.getType())
+ )))
+ .append(srcVar.invoke(DESCEND, constructMtd.getThis().getField(fieldDef)))
+ )
+ );
+ }
+ // this.field = src == null ? new NamedListNode(ValueNode::new) : src.descend(field = field.copy()));
+ else {
+ String fieldNodeClassName = schemasInfo.get(schemaField.getType()).nodeClassName;
+
+ caseClause.append(new IfStatement()
+ .condition(isNull(srcVar))
+ .ifTrue(constructMtd.getThis().setField(
+ fieldDef,
+ newInstance(NamedListNode.class, newNamedListElementLambda(fieldNodeClassName))
+ ))
+ .ifFalse(new BytecodeBlock()
+ .append(constructMtd.getThis().setField(
+ fieldDef,
+ constructMtd.getThis().getField(fieldDef).invoke(COPY).cast(fieldDef.getType())
+ ))
+ .append(srcVar.invoke(DESCEND, constructMtd.getThis().getField(fieldDef)))
+ )
+ );
+ }
+ }
+
+ // Default option is to throw "NoSuchElementException(key)".
+ switchBuilder.defaultCase(new BytecodeBlock()
+ .append(newInstance(NoSuchElementException.class, keyVar))
+ .throwObject()
+ );
+
+ constructMtd.getBody().append(switchBuilder.build()).ret();
+ }
+
+ /**
+ * Implements {@link InnerNode#constructDefault(String)} method.
+ * @param classDef Node class definition.
+ * @param specField Field definition for the {@code _spec} field of the node class.
+ * @param schemaFields Configuration Schema class fields.
+ * @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
+ */
+ private void addNodeConstructDefaultMethod(
+ ClassDefinition classDef,
+ FieldDefinition specField,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition constructDfltMtd = classDef.declareMethod(
+ of(PUBLIC),
+ "constructDefault",
+ type(void.class),
+ arg("key", String.class)
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = constructDfltMtd.getScope().getVariable("key");
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructDfltMtd.getScope())
+ .expression(keyVar);
+
+ for (Field schemaField : schemaFields) {
+ if (!isValue(schemaField))
+ continue;
+
+ if (!schemaField.getAnnotation(Value.class).hasDefault()) {
+ switchBuilder.addCase(schemaField.getName(), new BytecodeBlock());
+
+ continue;
+ }
+
+ FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+
+ Class<?> schemaFieldType = schemaField.getType();
+
+ // defaultValue = _spec.field;
+ BytecodeExpression defaultValue = constructDfltMtd.getThis().getField(specField).getField(schemaField);
+
+ // defaultValue = Box.valueOf(defaultValue); // Boxing.
+ if (schemaFieldType.isPrimitive()) {
+ defaultValue = invokeStatic(
+ fieldDef.getType(),
+ "valueOf",
+ fieldDef.getType(),
+ singleton(defaultValue)
+ );
+ }
+
+ // defaultValue = defaultValue.clone();
+ if (schemaFieldType.isArray())
+ defaultValue = defaultValue.invoke("clone", Object.class).cast(schemaFieldType);
+
+ // this.field = defaultValue;
+ BytecodeBlock caseClause = new BytecodeBlock()
+ .append(constructDfltMtd.getThis().setField(fieldDef, defaultValue));
+
+ switchBuilder.addCase(schemaField.getName(), caseClause);
+ }
+
+ // Default option is to throw "NoSuchElementException(key)".
+ switchBuilder.defaultCase(new BytecodeBlock()
+ .append(newInstance(NoSuchElementException.class, keyVar))
+ .throwObject()
+ );
+
+ constructDfltMtd.getBody().append(switchBuilder.build()).ret();
+ }
+
+ /**
+ * Creates {@code *Node::new} lambda expression with {@link Supplier} type.
+ * @param nodeClassName Name of the {@code *Node} class.
+ * @return InvokeDynamic bytecode expression.
+ */
+ @NotNull private BytecodeExpression newNamedListElementLambda(String nodeClassName) {
+ return invokeDynamic(
+ LAMBDA_METAFACTORY,
+ Arrays.asList(
+ getMethodType(getType(Object.class)),
+ new Handle(
+ H_NEWINVOKESPECIAL,
+ internalName(nodeClassName),
+ "<init>",
+ getMethodDescriptor(Type.VOID_TYPE),
+ false
+ ),
+ getMethodType(typeFromJavaClassName(nodeClassName).getAsmType())
+ ),
+ "get",
+ methodType(Supplier.class)
+ );
+ }
+
+ private ClassDefinition createCfgImplClass(Class<?> schemaClass) {
+ SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
+
+ // Configuration impl class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ of(PUBLIC, FINAL),
+ internalName(schemaClassInfo.cfgImplClassName),
+ type(DynamicConfiguration.class),
+ typeFromJavaClassName(schemaClassInfo.cfgClassName)
+ );
+
+ // Cached array of reflected fields. Helps to avoid unnecessary clonings.
+ Field[] schemaFields = schemaClass.getDeclaredFields();
+
+ // Fields.
+ Map<String, FieldDefinition> fieldDefs = new HashMap<>();
+
+ for (Field schemaField : schemaFields)
+ fieldDefs.put(schemaField.getName(), addConfigurationImplField(classDef, schemaField));
+
+ // Constructor
+ addConfigurationImplConstructor(classDef, schemaClassInfo, schemaFields, fieldDefs);
+
+ for (Field schemaField : schemaFields) {
+ addConfigurationImplGetMethod(classDef, schemaClass, fieldDefs, schemaField);
+ }
+
+ return classDef;
+ }
+
+ /**
+ * Declares field that corresponds to configuration value. Depending on the schema, 3 options possible:
+ * <ul>
+ * <li>
+ * {@code @Value public type fieldName}<br/>becomes<br/>
+ * {@code private DynamicProperty fieldName}
+ * </li>
+ * <li>
+ * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
+ * {@code private MyConfiguration fieldName}
+ * </li>
+ * <li>
+ * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
+ * {@code private NamedListConfiguration fieldName}
+ * </li>
+ * </ul>
+ * @param classDef Configuration impl class definition.
+ * @param schemaField Configuration Schema class field.
+ * @return Declared field definition.
+ */
+ private FieldDefinition addConfigurationImplField(ClassDefinition classDef, Field schemaField) {
+ ParameterizedType fieldType;
+
+ if (isConfigValue(schemaField))
+ fieldType = typeFromJavaClassName(schemasInfo.get(schemaField.getType()).cfgClassName);
+ else if (isNamedConfigValue(schemaField))
+ fieldType = type(NamedListConfiguration.class);
+ else
+ fieldType = type(DynamicProperty.class);
+
+ return classDef.declareField(of(PRIVATE), schemaField.getName(), fieldType);
+ }
+
+ /**
+ * Implements default constructor for the configuration class. It initializes all fields and adds them to members
+ * collection.
+ * @param classDef Configuration impl class definition.
+ * @param schemaClassInfo Configuration Schema class info.
+ * @param schemaFields Configuration Schema class fields.
+ * @param fieldDefs Field definitions for all fields of configuration impl class.
+ */
+ private void addConfigurationImplConstructor(
+ ClassDefinition classDef,
+ SchemaClassesInfo schemaClassInfo,
+ Field[] schemaFields,
+ Map<String, FieldDefinition> fieldDefs
+ ) {
+ MethodDefinition ctor = classDef.declareConstructor(
+ of(PUBLIC),
+ arg("prefix", List.class),
+ arg("key", String.class),
+ arg("rootKey", RootKey.class),
+ arg("changer", ConfigurationChanger.class)
+ );
+
+ BytecodeBlock ctorBody = ctor.getBody()
+ .append(ctor.getThis())
+ .append(ctor.getScope().getVariable("prefix"))
+ .append(ctor.getScope().getVariable("key"))
+ .append(ctor.getScope().getVariable("rootKey"))
+ .append(ctor.getScope().getVariable("changer"))
+ .invokeConstructor(DYNAMIC_CONFIGURATION_CTOR);
+
+ int newIdx = 0;
+ for (Field schemaField : schemaFields) {
+ FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+
+ BytecodeExpression newValue;
+
+ if (isValue(schemaField)) {
+ // newValue = new DynamicProperty(super.keys, fieldName, rootKey, changer);
+ newValue = newInstance(
+ DynamicProperty.class,
+ ctor.getThis().getField("keys", List.class),
+ constantString(schemaField.getName()),
+ ctor.getScope().getVariable("rootKey"),
+ ctor.getScope().getVariable("changer")
+ );
+ }
+ else {
+ SchemaClassesInfo fieldInfo = schemasInfo.get(schemaField.getType());
+
+ ParameterizedType cfgImplParameterizedType = typeFromJavaClassName(fieldInfo.cfgImplClassName);
+
+ if (isConfigValue(schemaField)) {
+ // newValue = new MyConfigurationImpl(super.keys, fieldName, rootKey, changer);
+ newValue = newInstance(
+ cfgImplParameterizedType,
+ ctor.getThis().getField("keys", List.class),
+ constantString(schemaField.getName()),
+ ctor.getScope().getVariable("rootKey"),
+ ctor.getScope().getVariable("changer")
+ );
+ }
+ else {
+ // We have to create method "$new$<idx>" to reference it in lambda expression. That's the way it
+ // works, it'll invoke constructor with all 4 arguments, not just 2 as in BiFunction.
+ MethodDefinition newMtd = classDef.declareMethod(
+ of(PRIVATE, STATIC, SYNTHETIC),
+ "$new$" + newIdx++,
+ typeFromJavaClassName(fieldInfo.cfgClassName),
+ arg("rootKey", RootKey.class),
+ arg("changer", ConfigurationChanger.class),
+ arg("prefix", List.class),
+ arg("key", String.class)
+ );
+
+ // newValue = new NamedListConfiguration(super.keys, fieldName, rootKey, changer, (p, k) ->
+ // new ValueConfigurationImpl(p, k, rootKey, changer)
+ // );
+ newValue = newInstance(
+ NamedListConfiguration.class,
+ ctor.getThis().getField("keys", List.class),
+ constantString(schemaField.getName()),
+ ctor.getScope().getVariable("rootKey"),
+ ctor.getScope().getVariable("changer"),
+ invokeDynamic(
+ LAMBDA_METAFACTORY,
+ Arrays.asList(
+ getMethodType(getType(Object.class), getType(Object.class), getType(Object.class)),
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ internalName(schemaClassInfo.cfgImplClassName),
+ newMtd.getName(),
+ newMtd.getMethodDescriptor(),
+ false
+ ),
+ getMethodType(
+ typeFromJavaClassName(fieldInfo.cfgClassName).getAsmType(),
+ getType(List.class),
+ getType(String.class)
+ )
+ ),
+ "apply",
+ BiFunction.class,
+ ctor.getScope().getVariable("rootKey"),
+ ctor.getScope().getVariable("changer")
+ )
+ );
+
+ newMtd.getBody()
+ .append(newInstance(
+ cfgImplParameterizedType,
+ newMtd.getScope().getVariable("prefix"),
+ newMtd.getScope().getVariable("key"),
+ newMtd.getScope().getVariable("rootKey"),
+ newMtd.getScope().getVariable("changer")
+ ))
+ .retObject();
+ }
+ }
+
+ // this.field = newValue;
+ ctorBody.append(ctor.getThis().setField(fieldDef, newValue));
+
+ // add(this.field);
+ ctorBody.append(ctor.getThis().invoke(DYNAMIC_CONFIGURATION_ADD, ctor.getThis().getField(fieldDef)));
+ }
+
+ ctorBody.ret();
+ }
+
+ /**
+ * Implements accessor method in configuration impl class.
+ * @param classDef Configuration impl class definition.
+ * @param schemaClass Configuration Schema class.
+ * @param fieldDefs Field definitions for all fields of configuration impl class.
+ * @param schemaField Configuration Schema class field.
+ */
+ private void addConfigurationImplGetMethod(
+ ClassDefinition classDef,
+ Class<?> schemaClass,
+ Map<String, FieldDefinition> fieldDefs,
+ Field schemaField
+ ) {
+ Class<?> schemaFieldType = schemaField.getType();
+
+ String fieldName = schemaField.getName();
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ ParameterizedType returnType;
+
+ if (isConfigValue(schemaField))
+ returnType = typeFromJavaClassName(new SchemaClassesInfo(schemaFieldType).cfgClassName);
+ else if (isNamedConfigValue(schemaField))
+ returnType = type(NamedConfigurationTree.class);
+ else {
+ assert isValue(schemaField) : schemaClass;
+
+ returnType = type(ConfigurationValue.class);
+ }
+
+ MethodDefinition viewMtd = classDef.declareMethod(
+ of(PUBLIC),
+ fieldName,
+ returnType
+ );
+
+ BytecodeBlock viewBody = viewMtd.getBody();
+
+ viewBody
+ .append(viewMtd.getThis())
+ .getField(fieldDef)
+ .retObject();
+ }
+
+ /**
+ * Replaces first letter in string with its upper-cased variant.
+ * @param name Some string.
+ * @return Capitalized version of passed string.
+ */
+ private static String capitalize(String name) {
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }
+
+ /**
+ * Returns internalized version of class name, replacing dots with slashes.
+ * @param className Class name (with package).
+ * @return Internal class name.
+ * @see Type#getInternalName(java.lang.Class)
+ */
+ @NotNull private static String internalName(String className) {
+ return className.replace('.', '/');
+ }
+
+ /**
+ * Creates boxed version of the class. Types that it can box: {@code boolean}, {@code int}, {@code long} and
+ * {@code double}. Other primitive types are not supported by configuration framework.
+ * @param clazz Maybe primitive class.
+ * @return Not primitive class that represents parameter class.
+ */
+ private static Class<?> box(Class<?> clazz) {
+ if (!clazz.isPrimitive())
+ return clazz;
+
+ switch (clazz.getName()) {
+ case "boolean":
+ return Boolean.class;
+
+ case "int":
+ return Integer.class;
+
+ case "long":
+ return Long.class;
+
+ default:
+ assert clazz == double.class;
+
+ return Double.class;
+ }
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/SchemaClassesInfo.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/SchemaClassesInfo.java
new file mode 100644
index 0000000..1edb423
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/SchemaClassesInfo.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.internal.asm;
+
+import org.apache.ignite.configuration.internal.DynamicConfiguration;
+import org.apache.ignite.configuration.tree.InnerNode;
+
+/**
+ * Class to cache compiled classes and hold precalculated names to reference other existing classes.
+ */
+class SchemaClassesInfo {
+ /** Configuration Schema class. */
+ public final Class<?> schemaClass;
+
+ /** Class name for the VIEW class. */
+ public final String viewClassName;
+
+ /** Class name for the CHANGE class. */
+ public final String changeClassName;
+
+ /** Class name for the Configuration class. */
+ public final String cfgClassName;
+
+ /** Class name for the Node class. */
+ public final String nodeClassName;
+
+ /** Class name for the Configuration Impl class. */
+ public final String cfgImplClassName;
+
+ /** Node class instance. */
+ public Class<? extends InnerNode> nodeClass;
+
+ /** Configuration Impl class instance. */
+ public Class<? extends DynamicConfiguration<?, ?>> cfgImplClass;
+
+ /**
+ * @param schemaClass Configuration Schema class instance.
+ */
+ SchemaClassesInfo(Class<?> schemaClass) {
+ this.schemaClass = schemaClass;
+ String schemaClassName = schemaClass.getPackageName() + "." + schemaClass.getSimpleName(); // Support inner classes.
+
+ String prefix = schemaClassName.replaceAll("ConfigurationSchema$", "");
+
+ viewClassName = prefix + "View";
+ changeClassName = prefix + "Change";
+ cfgClassName = prefix + "Configuration";
+
+ nodeClassName = prefix + "Node";
+ cfgImplClassName = prefix + "ConfigurationImpl";
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/StringSwitchBuilder.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/StringSwitchBuilder.java
new file mode 100644
index 0000000..67cab54
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/asm/StringSwitchBuilder.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.internal.asm;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.Scope;
+import com.facebook.presto.bytecode.Variable;
+import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.control.SwitchStatement.SwitchBuilder;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.facebook.presto.bytecode.BytecodeUtils.checkState;
+import static com.facebook.presto.bytecode.control.SwitchStatement.switchBuilder;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantString;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.IntStream.range;
+
+/**
+ * Class similar to {@link SwitchBuilder} but it allows to use {@link String}s as switch keys.
+ */
+class StringSwitchBuilder {
+ /** {@link Object#equals(Object)} */
+ private static final Method EQUALS;
+
+ /** {@link Object#hashCode()} */
+ private static final Method HASH_CODE;
+
+ static {
+ try {
+ EQUALS = Object.class.getDeclaredMethod("equals", Object.class);
+
+ HASH_CODE = Object.class.getDeclaredMethod("hashCode");
+ }
+ catch (NoSuchMethodException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ /** Expression to be used for matching. */
+ private BytecodeExpression expression;
+
+ /** Set of case statements in order of appearance. */
+ private final Set<CaseStatement> cases = new LinkedHashSet<>();
+
+ /** Default section for the switch. */
+ private BytecodeNode defaultBody;
+
+ /** Scope to create temp variables. */
+ private final Scope scope;
+
+ /**
+ * @param scope Scope to create temp variables.
+ */
+ StringSwitchBuilder(Scope scope) {
+ this.scope = scope;
+ }
+
+ /**
+ * @param expression Expression to be used for matching.
+ * @return {@code this} for chaining.
+ */
+ public StringSwitchBuilder expression(BytecodeExpression expression) {
+ this.expression = expression;
+ return this;
+ }
+
+ /**
+ * @param key Key for matching.
+ * @param body Case statement.
+ * @return {@code this} for chaining.
+ */
+ public StringSwitchBuilder addCase(String key, BytecodeNode body) {
+ CaseStatement statement = new CaseStatement(key, body);
+
+ checkState(cases.add(statement), "case already exists for value [%s]", key);
+
+ return this;
+ }
+
+ /**
+ * @param body Default statement.
+ * @return {@code this} for chaining.
+ */
+ public StringSwitchBuilder defaultCase(BytecodeNode body) {
+ checkState(defaultBody == null, "default case already set");
+ this.defaultBody = requireNonNull(body, "body is null");
+ return this;
+ }
+
+ /**
+ * Produces "compiled" switch statement, ready to be inserted into method.
+ * @return Bytecode block.
+ */
+ public BytecodeBlock build() {
+ checkState(expression != null, "expression is not set");
+
+ // Variable to cache the expression string.
+ Variable expVar = scope.createTempVariable(String.class);
+
+ // Variable to store integer index corresponding to the matched string.
+ Variable idxVar = scope.createTempVariable(int.class);
+
+ BytecodeBlock res = new BytecodeBlock()
+ .append(expVar.set(expression)) // expVar = evaluate(expression);
+ .append(idxVar.set(constantInt(-1))); // idxVar = -1;
+
+ BytecodeNode[] caseBodies = new BytecodeNode[cases.size()];
+
+ // Here we are preparing case statements for the first switch. It'll look like this:
+ // switch (expVar.hashCode()) {
+ // case <hash1>:
+ // if (expVar.equals("a")) idxVar = 0;
+ // case <hash2>:
+ // if (expVar.equals("b")) idxVar = 1;
+ // if (expVar.equals("c")) idxVar = 2;
+ // ...
+ // }
+ SwitchBuilder hashSwitch = switchBuilder()
+ .expression(expVar.invoke(HASH_CODE));
+
+ // Case for each hash value may have multiple matching strings due to collisions.
+ Map<Integer, List<CaseStatement>> groupedCases = cases.stream().collect(
+ groupingBy(statement -> statement.key.hashCode())
+ );
+
+ int idx = 0;
+ for (Map.Entry<Integer, List<CaseStatement>> entry : groupedCases.entrySet()) {
+ BytecodeBlock caseBody = new BytecodeBlock();
+
+ for (CaseStatement caseStmt : entry.getValue()) {
+ caseBody.append(new IfStatement()
+ .condition(expVar.invoke(EQUALS, constantString(caseStmt.key)))
+ .ifTrue(idxVar.set(constantInt(idx)))
+ );
+
+ caseBodies[idx++] = caseStmt.body;
+ }
+
+ hashSwitch.addCase(entry.getKey(), caseBody);
+ }
+
+ // No default statement required because "idxVar" was preemptively assigned to -1.
+ res.append(hashSwitch.build());
+
+ // Here's the actual switch by "idxVar" variable that uses user-defined "case" and "default" clauses.
+ res.append(range(0, caseBodies.length).boxed()
+ .reduce(
+ switchBuilder().expression(idxVar),
+ (builder, i) -> builder.addCase(i, caseBodies[i]),
+ (b0, b1) -> b0 // Won't be used by sequential stream but required to be non-null.
+ )
+ .defaultCase(defaultBody)
+ .build()
+ );
+
+ return res;
+ }
+
+ /**
+ * Case statement class for the builder.
+ */
+ private static class CaseStatement {
+ /** String key of the case statement. */
+ private final String key;
+
+ /** Body of the case statement. */
+ private final BytecodeNode body;
+
+ /**
+ * @param key String key of the case statement.
+ * @param body Body of the case statement.
+ */
+ CaseStatement(String key, BytecodeNode body) {
+ this.key = key;
+ this.body = requireNonNull(body, "body is null");
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return key.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+
+ CaseStatement other = (CaseStatement)obj;
+ return Objects.equals(this.key, other.key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return getClass().getSimpleName() + "[key=" + key + ']';
+ }
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
index 833b84c..b199b43 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
@@ -595,7 +595,7 @@ public class ConfigurationUtil {
/** {@inheritDoc} */
@Override public void descend(ConstructableTreeNode dstNode) {
- assert srcNode.getClass() == dstNode.getClass();
+ assert srcNode.getClass() == dstNode.getClass() : srcNode.getClass() + " : " + dstNode.getClass();
srcNode.traverseChildren(new ConfigurationVisitor<>() {
@Override public Void visitLeafNode(String key, Serializable val) {
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
index 44b77d8..5cad648 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConstructableTreeNode.java
@@ -29,7 +29,7 @@ public interface ConstructableTreeNode {
* @param src Source that provides data for construction.
* @throws NoSuchElementException If {@code key} cannot be constructed.
*/
- void construct(String key,/* boolean canMutate, */ ConfigurationSource src) throws NoSuchElementException;
+ void construct(String key, ConfigurationSource src) throws NoSuchElementException;
/**
* Public equivalent of {@link Object#clone()} method. Creates a copy with effectively the same content.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
index b21df65..605ac4b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/InnerNode.java
@@ -117,10 +117,9 @@ public abstract class InnerNode implements TraversableTreeNode, ConstructableTre
* Assigns default value to the corresponding leaf. Defaults are gathered from configuration schema class.
*
* @param fieldName Field name to be initialized.
- * @return {@code true} if default value has been assigned, {@code false} otherwise.
* @throws NoSuchElementException If there's no such field or it is not a leaf value.
*/
- public abstract boolean constructDefault(String fieldName) throws NoSuchElementException;
+ public abstract void constructDefault(String fieldName) throws NoSuchElementException;
/**
* @return Class of corresponding configuration schema.
diff --git a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ITMetaStorageServiceTest.java b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ITMetaStorageServiceTest.java
index 3689696..8125a18 100644
--- a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ITMetaStorageServiceTest.java
+++ b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ITMetaStorageServiceTest.java
@@ -43,7 +43,7 @@ import org.apache.ignite.network.internal.recovery.message.HandshakeStartMessage
import org.apache.ignite.network.internal.recovery.message.HandshakeStartMessageSerializationFactory;
import org.apache.ignite.network.internal.recovery.message.HandshakeStartResponseMessage;
import org.apache.ignite.network.internal.recovery.message.HandshakeStartResponseMessageSerializationFactory;
-import org.apache.ignite.network.scalecube.ScaleCubeClusterServiceFactory;
+import org.apache.ignite.network.scalecube.TestScaleCubeClusterServiceFactory;
import org.apache.ignite.network.scalecube.message.ScaleCubeMessage;
import org.apache.ignite.network.scalecube.message.ScaleCubeMessageSerializationFactory;
import org.apache.ignite.network.serialization.MessageSerializationRegistry;
@@ -94,7 +94,7 @@ public class ITMetaStorageServiceTest {
private static final RaftClientMessageFactory FACTORY = new RaftClientMessageFactoryImpl();
/** Network factory. */
- private static final ClusterServiceFactory NETWORK_FACTORY = new ScaleCubeClusterServiceFactory();
+ private static final ClusterServiceFactory NETWORK_FACTORY = new TestScaleCubeClusterServiceFactory();
/** */
private static final MessageSerializationRegistry SERIALIZATION_REGISTRY = new MessageSerializationRegistry()
diff --git a/modules/vault/src/test/java/org/apache/ignite/internal/vault/impl/VaultBaseContractsTest.java b/modules/vault/src/test/java/org/apache/ignite/internal/vault/impl/VaultBaseContractsTest.java
index 3a11a4d..3d5aee8 100644
--- a/modules/vault/src/test/java/org/apache/ignite/internal/vault/impl/VaultBaseContractsTest.java
+++ b/modules/vault/src/test/java/org/apache/ignite/internal/vault/impl/VaultBaseContractsTest.java
@@ -185,7 +185,7 @@ public class VaultBaseContractsTest {
for (int i = 3; i < 7; i++)
vaultManager.put(getKey(i), ("new" + i).getBytes());
- assertTrue(counter.await(10, TimeUnit.MILLISECONDS));
+ assertTrue(counter.await(1, TimeUnit.SECONDS));
}
/**