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/09/01 13:44:43 UTC
[ignite-3] branch main updated: IGNITE-15047 Implemented internal
configuration roots and extensions. (#290)
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 2fa8a85 IGNITE-15047 Implemented internal configuration roots and extensions. (#290)
2fa8a85 is described below
commit 2fa8a85a5de3bf7f4946b37c6fa098e85d722e60
Author: Kirill Tkalenko <tk...@yandex.ru>
AuthorDate: Wed Sep 1 16:44:40 2021 +0300
IGNITE-15047 Implemented internal configuration roots and extensions. (#290)
---
.../internal/affinity/AffinityManagerTest.java | 3 +-
.../facebook/presto/bytecode/ClassDefinition.java | 14 +-
.../handler/ClientHandlerIntegrationTest.java | 8 +-
.../apache/ignite/client/AbstractClientTest.java | 3 +-
.../configuration/processor/ITProcessorTest.java | 88 +++-
.../ErrorInternal0ConfigurationSchema.java | 29 +
.../ErrorInternal1ConfigurationSchema.java | 30 ++
.../ErrorInternal2ConfigurationSchema.java | 29 +
.../ErrorInternal3ConfigurationSchema.java | 27 +
.../ErrorInternal4ConfigurationSchema.java | 30 ++
.../ErrorInternal5ConfigurationSchema.java | 31 ++
.../ExtendedSimpleConfigurationSchema.java | 31 ++
.../ExtendedSimpleRootConfigurationSchema.java | 31 ++
.../internal/SimpleConfigurationSchema.java | 35 ++
.../internal/SimpleRootConfigurationSchema.java | 41 ++
.../configuration/processor/Processor.java | 208 ++++++--
.../configuration/ConfigurationChangerTest.java | 23 +-
.../configuration/ConfigurationRegistryTest.java | 86 +++
.../configuration/TestConfigurationChanger.java | 13 +-
.../asm/ConfigurationAsmGeneratorTest.java | 309 +++++++++++
.../configuration/hocon/HoconConverterTest.java | 3 +-
.../notifications/ConfigurationListenerTest.java | 7 +-
.../internal/configuration/sample/UsageTest.java | 6 +-
.../configuration/tree/ConfigurationArrayTest.java | 5 +-
.../tree/ConstructableTreeNodeTest.java | 25 +-
.../configuration/tree/NamedListNodeTest.java | 2 +-
.../tree/TraversableTreeNodeTest.java | 24 +-
.../configuration/util/ConfigurationUtilTest.java | 388 +++++++++++++-
.../validation/ValidationUtilTest.java | 2 +-
.../org/apache/ignite/configuration/RootKey.java | 15 +
.../annotation/InternalConfiguration.java | 42 ++
.../configuration/ConfigurationChanger.java | 4 +-
.../configuration/ConfigurationManager.java | 13 +-
.../internal/configuration/ConfigurationNode.java | 2 +-
.../configuration/ConfigurationRegistry.java | 48 +-
.../configuration/DynamicConfiguration.java | 5 +-
.../internal/configuration/DynamicProperty.java | 6 +-
.../internal/configuration/RootInnerNode.java | 71 +++
.../ignite/internal/configuration/SuperRoot.java | 111 ++--
.../asm/ConfigurationAsmGenerator.java | 585 ++++++++++++++-------
.../configuration/asm/SchemaClassesInfo.java | 64 ++-
.../configuration/hocon/HoconConverter.java | 3 +-
.../hocon/HoconListConfigurationSource.java | 2 +-
.../hocon/HoconObjectConfigurationSource.java | 12 +-
.../configuration/tree/ConstructableTreeNode.java | 3 +-
.../ConverterToMapVisitor.java} | 26 +-
.../internal/configuration/tree/InnerNode.java | 120 +++--
.../internal/configuration/tree/NamedListNode.java | 2 +-
.../configuration/util/ConfigurationFlattener.java | 14 +-
.../util/ConfigurationNotificationsUtil.java | 8 +-
.../configuration/util/ConfigurationUtil.java | 263 ++++++++-
.../util/KeysTrackingConfigurationVisitor.java | 2 +-
.../validation/ValidationContextImpl.java | 2 +-
.../configuration/validation/ValidationUtil.java | 4 +-
.../ignite/internal/util/CollectionUtils.java | 72 +++
.../ignite/internal/util/CollectionUtilsTest.java | 74 +++
.../ignite/utils/ClusterServiceTestUtils.java | 4 +-
.../ConfigurationPresentationTest.java | 3 +-
.../ITDistributedConfigurationStorageTest.java | 3 +-
.../org/apache/ignite/internal/app/IgniteImpl.java | 6 +-
.../SchemaConfigurationConverterTest.java | 3 +-
.../ignite/internal/table/TableManagerTest.java | 6 +-
62 files changed, 2657 insertions(+), 472 deletions(-)
diff --git a/modules/affinity/src/test/java/org/apache/ignite/internal/affinity/AffinityManagerTest.java b/modules/affinity/src/test/java/org/apache/ignite/internal/affinity/AffinityManagerTest.java
index 152c900..298211c 100644
--- a/modules/affinity/src/test/java/org/apache/ignite/internal/affinity/AffinityManagerTest.java
+++ b/modules/affinity/src/test/java/org/apache/ignite/internal/affinity/AffinityManagerTest.java
@@ -84,7 +84,8 @@ public class AffinityManagerTest {
cfrMgr = new ConfigurationManager(
rootConfigurationKeys(),
Map.of(),
- new TestConfigurationStorage(DISTRIBUTED)
+ new TestConfigurationStorage(DISTRIBUTED),
+ List.of()
);
cfrMgr.start();
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java
index 7fa7469..74363b6 100644
--- a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/ClassDefinition.java
@@ -26,8 +26,10 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.objectweb.asm.ClassVisitor;
+import static com.facebook.presto.bytecode.Access.BRIDGE;
import static com.facebook.presto.bytecode.Access.INTERFACE;
import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.Access.SYNTHETIC;
import static com.facebook.presto.bytecode.Access.a;
import static com.facebook.presto.bytecode.Access.toAccessModifier;
import static java.util.Objects.requireNonNull;
@@ -246,11 +248,19 @@ public class ClassDefinition {
EnumSet<Access> access,
String name,
ParameterizedType returnType,
- Collection<Parameter> parameters) {
+ Collection<Parameter> parameters
+ ) {
MethodDefinition methodDefinition = new MethodDefinition(this, access, name, returnType, parameters);
+
+ EnumSet<Access> bridgeAccess = EnumSet.of(SYNTHETIC, BRIDGE);
+
for (MethodDefinition method : methods) {
if (name.equals(method.getName()) && method.getParameterTypes().equals(methodDefinition.getParameterTypes())) {
- throw new IllegalArgumentException("Method with same name and signature already exists: " + name);
+ boolean curBridge = method.getAccess().containsAll(bridgeAccess);
+ boolean newBridge = methodDefinition.getAccess().containsAll(bridgeAccess);
+
+ if ((!curBridge && !newBridge) || method.getReturnType().equals(methodDefinition.getReturnType()))
+ throw new IllegalArgumentException("Method with same name and signature already exists: " + name);
}
}
methods.add(methodDefinition);
diff --git a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientHandlerIntegrationTest.java b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientHandlerIntegrationTest.java
index 574cbbb..9aa6a28 100644
--- a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientHandlerIntegrationTest.java
+++ b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientHandlerIntegrationTest.java
@@ -177,9 +177,11 @@ public class ClientHandlerIntegrationTest {
private ClientHandlerModule startServer() {
configurationManager = new ConfigurationManager(
- List.of(ClientConnectorConfiguration.KEY),
- Map.of(),
- new TestConfigurationStorage(LOCAL));
+ List.of(ClientConnectorConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ List.of()
+ );
configurationManager.start();
diff --git a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
index 744d2e9..86859f4 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
@@ -100,7 +100,8 @@ public abstract class AbstractClientTest {
var cfg = new ConfigurationRegistry(
List.of(ClientConnectorConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
cfg.start();
diff --git a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ITProcessorTest.java b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ITProcessorTest.java
index 9ed4db4..965f612 100644
--- a/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ITProcessorTest.java
+++ b/modules/configuration-annotation-processor/src/integrationTest/java/org/apache/ignite/internal/configuration/processor/ITProcessorTest.java
@@ -16,12 +16,14 @@
*/
package org.apache.ignite.internal.configuration.processor;
+import java.util.Arrays;
import com.google.testing.compile.Compilation;
import com.squareup.javapoet.ClassName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -32,7 +34,7 @@ public class ITProcessorTest extends AbstractProcessorTest {
* The simplest test for code generation.
*/
@Test
- public void test() {
+ public void testPublicConfigCodeGeneration() {
final String packageName = "org.apache.ignite.internal.configuration.processor";
final ClassName testConfigurationSchema = ClassName.get(packageName, "TestConfigurationSchema");
@@ -49,4 +51,88 @@ public class ITProcessorTest extends AbstractProcessorTest {
assertTrue(classSet.allGenerated());
}
+
+ /** */
+ @Test
+ void testSuccessInternalConfigCodeGeneration() {
+ String packageName = "org.apache.ignite.internal.configuration.processor.internal";
+
+ ClassName cls0 = ClassName.get(packageName, "SimpleRootConfigurationSchema");
+ ClassName cls1 = ClassName.get(packageName, "SimpleConfigurationSchema");
+ ClassName cls2 = ClassName.get(packageName, "ExtendedSimpleRootConfigurationSchema");
+ ClassName cls3 = ClassName.get(packageName, "ExtendedSimpleConfigurationSchema");
+
+ BatchCompilation batchCompile = batchCompile(cls0, cls1, cls2, cls3);
+
+ assertNotEquals(Compilation.Status.FAILURE, batchCompile.getCompilationStatus().status());
+
+ assertEquals(4 * 3, batchCompile.generated().size());
+
+ assertTrue(batchCompile.getBySchema(cls0).allGenerated());
+ assertTrue(batchCompile.getBySchema(cls1).allGenerated());
+ assertTrue(batchCompile.getBySchema(cls2).allGenerated());
+ assertTrue(batchCompile.getBySchema(cls3).allGenerated());
+ }
+
+ /** */
+ @Test
+ void testErrorInternalConfigCodeGeneration() {
+ String packageName = "org.apache.ignite.internal.configuration.processor.internal";
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(packageName, "ErrorInternal0ConfigurationSchema")
+ );
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(packageName, "ErrorInternal1ConfigurationSchema")
+ );
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(packageName, "ErrorInternal2ConfigurationSchema")
+ );
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(
+ packageName,
+ "SimpleRootConfigurationSchema",
+ "SimpleConfigurationSchema",
+ "ExtendedSimpleRootConfigurationSchema",
+ "ErrorInternal3ConfigurationSchema"
+ )
+ );
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(packageName, "ErrorInternal4ConfigurationSchema")
+ );
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> batchCompile(
+ packageName,
+ "SimpleRootConfigurationSchema",
+ "SimpleConfigurationSchema",
+ "ErrorInternal5ConfigurationSchema"
+ )
+ );
+ }
+
+ /**
+ * Compile set of classes.
+ *
+ * @param packageName Package names.
+ * @param classNames Simple class names.
+ * @return Result of batch compilation.
+ */
+ private BatchCompilation batchCompile(String packageName, String... classNames) {
+ ClassName[] classes = Arrays.stream(classNames)
+ .map(clsName -> ClassName.get(packageName, clsName))
+ .toArray(ClassName[]::new);
+
+ return batchCompile(classes);
+ }
}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal0ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal0ConfigurationSchema.java
new file mode 100644
index 0000000..e98911c
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal0ConfigurationSchema.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+
+/**
+ * Class cannot have {@link InternalConfiguration} and {@link Config}.
+ */
+@InternalConfiguration
+@Config
+public class ErrorInternal0ConfigurationSchema {
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal1ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal1ConfigurationSchema.java
new file mode 100644
index 0000000..de90a3b
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal1ConfigurationSchema.java
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+
+/**
+ * If there are {@link InternalConfiguration} and {@link ConfigurationRoot},
+ * the class should not have a parent (superclass).
+ */
+@InternalConfiguration
+@ConfigurationRoot(rootName = "errorRoot")
+public class ErrorInternal1ConfigurationSchema extends SimpleRootConfigurationSchema {
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal2ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal2ConfigurationSchema.java
new file mode 100644
index 0000000..58daa49
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal2ConfigurationSchema.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+
+/**
+ * If {@link InternalConfiguration} is present and {@link ConfigurationRoot} is absent,
+ * the class must have a parent (superclass).
+ */
+@InternalConfiguration
+public class ErrorInternal2ConfigurationSchema {
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal3ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal3ConfigurationSchema.java
new file mode 100644
index 0000000..42a337d
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal3ConfigurationSchema.java
@@ -0,0 +1,27 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+
+/**
+ * Parent (superclass) must not contain {@link InternalConfiguration}.
+ */
+@InternalConfiguration
+public class ErrorInternal3ConfigurationSchema extends ExtendedSimpleRootConfigurationSchema {
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal4ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal4ConfigurationSchema.java
new file mode 100644
index 0000000..77a8306
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal4ConfigurationSchema.java
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import java.util.ArrayList;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+
+/**
+ * Parent (superclass) must contain either {@link Config} or {@link ConfigurationRoot}.
+ */
+@InternalConfiguration
+public class ErrorInternal4ConfigurationSchema extends ArrayList {
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal5ConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal5ConfigurationSchema.java
new file mode 100644
index 0000000..57b5b61
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ErrorInternal5ConfigurationSchema.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.Value;
+
+/**
+ * Extension and the parent (superclass) must not have the same fields by name.
+ */
+@InternalConfiguration
+public class ErrorInternal5ConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** String value. */
+ @Value
+ public String str;
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleConfigurationSchema.java
new file mode 100644
index 0000000..7bfec00
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleConfigurationSchema.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.Value;
+
+/**
+ * Simple internal configuration schema.
+ */
+@InternalConfiguration
+public class ExtendedSimpleConfigurationSchema extends SimpleConfigurationSchema {
+ /** String value. */
+ @Value
+ public String exSubStr;
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleRootConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleRootConfigurationSchema.java
new file mode 100644
index 0000000..7384edc
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/ExtendedSimpleRootConfigurationSchema.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.Value;
+
+/**
+ * Simple internal root configuration schema.
+ */
+@InternalConfiguration
+public class ExtendedSimpleRootConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** String value. */
+ @Value
+ public String exStr;
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleConfigurationSchema.java
new file mode 100644
index 0000000..7d4fca4
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleConfigurationSchema.java
@@ -0,0 +1,35 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.Value;
+
+/**
+ * Simple configuration schema.
+ */
+@Config
+public class SimpleConfigurationSchema {
+ /** String value. */
+ @Value
+ public String subStr;
+
+ /** Int value. */
+ @Value
+ public int subInt;
+}
diff --git a/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleRootConfigurationSchema.java b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleRootConfigurationSchema.java
new file mode 100644
index 0000000..cf69726
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/internal/SimpleRootConfigurationSchema.java
@@ -0,0 +1,41 @@
+/*
+ * 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.internal.configuration.processor.internal;
+
+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;
+
+/**
+ * Simple root configuration schema.
+ */
+@ConfigurationRoot(rootName = "simpleRoot")
+public class SimpleRootConfigurationSchema {
+ /** String value. */
+ @Value
+ public String str;
+
+ /** Sub configuration. */
+ @ConfigValue
+ public SimpleConfigurationSchema subCfg;
+
+ /** Named configuration. */
+ @NamedConfigValue
+ public SimpleConfigurationSchema namedCfg;
+}
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
index 43ab153..fe923d9 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
@@ -20,19 +20,20 @@ package org.apache.ignite.internal.configuration.processor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
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.PackageElement;
+import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
@@ -51,9 +52,13 @@ import org.apache.ignite.configuration.NamedListView;
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.InternalConfiguration;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
+import org.jetbrains.annotations.Nullable;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
@@ -84,37 +89,38 @@ public class Processor extends AbstractProcessor {
/**
* Processes a set of annotation types on type elements.
+ *
* @param roundEnvironment Processing environment.
* @return Whether or not the set of annotation types are claimed by this processor.
*/
private boolean process0(RoundEnvironment roundEnvironment) {
Elements elementUtils = processingEnv.getElementUtils();
- // All classes annotated with @Config
+ // All classes annotated with @ConfigurationRoot, @Config, @InternalConfiguration.
List<TypeElement> annotatedConfigs = roundEnvironment
- .getElementsAnnotatedWithAny(Set.of(ConfigurationRoot.class, Config.class)).stream()
+ .getElementsAnnotatedWithAny(Set.of(ConfigurationRoot.class, Config.class, InternalConfiguration.class))
+ .stream()
.filter(element -> element.getKind() == ElementKind.CLASS)
.map(TypeElement.class::cast)
- .collect(Collectors.toList());
+ .collect(toList());
if (annotatedConfigs.isEmpty())
return false;
for (TypeElement clazz : annotatedConfigs) {
- // Get package name of the schema class
- PackageElement elementPackage = elementUtils.getPackageOf(clazz);
- String packageName = elementPackage.getQualifiedName().toString();
+ // Find all the fields of the schema.
+ Collection<VariableElement> fields = fields(clazz);
+
+ validate(clazz, fields);
- // Find all the fields of the schema
- List<VariableElement> fields = clazz.getEnclosedElements().stream()
- .filter(el -> el.getKind() == ElementKind.FIELD)
- .map(VariableElement.class::cast)
- .collect(Collectors.toList());
+ // Is root of the configuration.
+ boolean isRootConfig = clazz.getAnnotation(ConfigurationRoot.class) != null;
- ConfigurationRoot rootAnnotation = clazz.getAnnotation(ConfigurationRoot.class);
+ // Is the internal configuration.
+ boolean isInternalConfig = clazz.getAnnotation(InternalConfiguration.class) != null;
- // Is root of the configuration
- boolean isRoot = rootAnnotation != null;
+ // Get package name of the schema class
+ String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString();
ClassName schemaClassName = ClassName.get(packageName, clazz.getSimpleName().toString());
@@ -173,10 +179,16 @@ public class Processor extends AbstractProcessor {
createGetters(configurationInterfaceBuilder, fieldName, interfaceGetMethodType);
}
- // Create VIEW and CHANGE classes
- createPojoBindings(fields, schemaClassName, configurationInterfaceBuilder);
+ // Create VIEW and CHANGE classes.
+ createPojoBindings(
+ fields,
+ schemaClassName,
+ configurationInterfaceBuilder,
+ isInternalConfig && !isRootConfig,
+ clazz
+ );
- if (isRoot)
+ if (isRootConfig)
createRootKeyField(configInterface, configurationInterfaceBuilder, schemaClassName, clazz);
// Write configuration interface
@@ -273,36 +285,62 @@ public class Processor extends AbstractProcessor {
/**
* Create VIEW and CHANGE classes and methods.
+ *
* @param fields List of configuration fields.
* @param schemaClassName Class name of schema.
+ * @param configurationInterfaceBuilder Configuration interface builder.
+ * @param extendBaseSchema {@code true} if extending base schema interfaces.
+ * @param realSchemaClass Class descriptor.
*/
private void createPojoBindings(
- List<VariableElement> fields,
+ Collection<VariableElement> fields,
ClassName schemaClassName,
- TypeSpec.Builder configurationInterfaceBuilder
+ TypeSpec.Builder configurationInterfaceBuilder,
+ boolean extendBaseSchema,
+ TypeElement realSchemaClass
) {
- ClassName viewClassTypeName = Utils.getViewName(schemaClassName);
- ClassName changeClassName = Utils.getChangeName(schemaClassName);
+ ClassName viewClsName = Utils.getViewName(schemaClassName);
+ ClassName changeClsName = Utils.getChangeName(schemaClassName);
+
+ TypeName configInterfaceType;
+ @Nullable TypeName viewBaseSchemaInterfaceType;
+ @Nullable TypeName changeBaseSchemaInterfaceType;
+
+ if (extendBaseSchema) {
+ DeclaredType superClassType = (DeclaredType)realSchemaClass.getSuperclass();
+ ClassName superClassSchemaClassName = ClassName.get((TypeElement)superClassType.asElement());
+
+ configInterfaceType = Utils.getConfigurationInterfaceName(superClassSchemaClassName);
+ viewBaseSchemaInterfaceType = Utils.getViewName(superClassSchemaClassName);
+ changeBaseSchemaInterfaceType = Utils.getChangeName(superClassSchemaClassName);
+ }
+ else {
+ ClassName confTreeInterface = ClassName.get("org.apache.ignite.configuration", "ConfigurationTree");
+ configInterfaceType = ParameterizedTypeName.get(confTreeInterface, viewClsName, changeClsName);
- ClassName confTreeInterface = ClassName.get("org.apache.ignite.configuration", "ConfigurationTree");
- TypeName confTreeParameterized = ParameterizedTypeName.get(confTreeInterface, viewClassTypeName, changeClassName);
+ viewBaseSchemaInterfaceType = null;
+ changeBaseSchemaInterfaceType = null;
+ }
- configurationInterfaceBuilder.addSuperinterface(confTreeParameterized);
+ configurationInterfaceBuilder.addSuperinterface(configInterfaceType);
// This code will be refactored in the future. Right now I don't want to entangle it with existing code
// generation. It has only a few considerable problems - hardcode and a lack of proper arrays handling.
// Clone method should be used to guarantee data integrity.
- ClassName viewClsName = Utils.getViewName(schemaClassName);
-
- ClassName changeClsName = Utils.getChangeName(schemaClassName);
TypeSpec.Builder viewClsBuilder = TypeSpec.interfaceBuilder(viewClsName)
.addModifiers(PUBLIC);
+ if (viewBaseSchemaInterfaceType != null)
+ viewClsBuilder.addSuperinterface(viewBaseSchemaInterfaceType);
+
TypeSpec.Builder changeClsBuilder = TypeSpec.interfaceBuilder(changeClsName)
.addSuperinterface(viewClsName)
.addModifiers(PUBLIC);
+ if (changeBaseSchemaInterfaceType != null)
+ changeClsBuilder.addSuperinterface(changeBaseSchemaInterfaceType);
+
ClassName consumerClsName = ClassName.get(Consumer.class);
for (VariableElement field : fields) {
@@ -408,9 +446,119 @@ public class Processor extends AbstractProcessor {
return processingEnv.getTypeUtils().isSameType(type, stringType);
}
+ /**
+ * Check if a class type is {@link Object}.
+ *
+ * @param type Class type.
+ * @return {@code true} if class type is {@link Object}.
+ */
+ private boolean isObjectClass(TypeMirror type) {
+ TypeMirror objectType = processingEnv
+ .getElementUtils()
+ .getTypeElement(Object.class.getCanonicalName())
+ .asType();
+
+ return objectType.equals(type);
+ }
+
+ /**
+ * Get class fields.
+ *
+ * @param type Class type.
+ * @return Class fields.
+ */
+ private Collection<VariableElement> fields(TypeElement type) {
+ return type.getEnclosedElements().stream()
+ .filter(el -> el.getKind() == ElementKind.FIELD)
+ .map(VariableElement.class::cast)
+ .collect(toList());
+ }
+
+ /**
+ * Validate the class.
+ *
+ * @param clazz Class type.
+ * @param fields Class fields.
+ * @throws ProcessorException If the class validation fails.
+ */
+ private void validate(TypeElement clazz, Collection<VariableElement> fields) {
+ if (clazz.getAnnotation(InternalConfiguration.class) != null) {
+ if (clazz.getAnnotation(Config.class) != null) {
+ throw new ProcessorException(String.format(
+ "Class with @%s is not allowed with @%s: %s",
+ Config.class.getSimpleName(),
+ InternalConfiguration.class.getSimpleName(),
+ clazz.getQualifiedName()
+ ));
+ }
+ else if (clazz.getAnnotation(ConfigurationRoot.class) != null) {
+ if (!isObjectClass(clazz.getSuperclass())) {
+ throw new ProcessorException(String.format(
+ "Class with @%s and @%s should not have a superclass: %s",
+ ConfigurationRoot.class.getSimpleName(),
+ InternalConfiguration.class.getSimpleName(),
+ clazz.getQualifiedName()
+ ));
+ }
+ }
+ else if (isObjectClass(clazz.getSuperclass())) {
+ throw new ProcessorException(String.format(
+ "Class with @%s must have a superclass: %s",
+ InternalConfiguration.class.getSimpleName(),
+ clazz.getQualifiedName()
+ ));
+ }
+ else {
+ TypeElement superClazz = processingEnv
+ .getElementUtils()
+ .getTypeElement(clazz.getSuperclass().toString());
+
+ if (superClazz.getAnnotation(InternalConfiguration.class) != null) {
+ throw new ProcessorException(String.format(
+ "Superclass must not have @%s: %s",
+ InternalConfiguration.class.getSimpleName(),
+ clazz.getQualifiedName()
+ ));
+ }
+ else if (superClazz.getAnnotation(ConfigurationRoot.class) == null &&
+ superClazz.getAnnotation(Config.class) == null) {
+ throw new ProcessorException(String.format(
+ "Superclass must have @%s or @%s: %s",
+ ConfigurationRoot.class.getSimpleName(),
+ Config.class.getSimpleName(),
+ clazz.getQualifiedName()
+ ));
+ }
+ else {
+ Set<Name> superClazzFieldNames = fields(superClazz).stream()
+ .map(VariableElement::getSimpleName)
+ .collect(toSet());
+
+ Collection<Name> duplicateFieldNames = fields.stream()
+ .map(VariableElement::getSimpleName)
+ .filter(superClazzFieldNames::contains)
+ .collect(toList());
+
+ if (!duplicateFieldNames.isEmpty()) {
+ throw new ProcessorException(String.format(
+ "Duplicate field names are not allowed [class=%s, superClass=%s, fields=%s]",
+ clazz.getQualifiedName(),
+ superClazz.getQualifiedName(),
+ duplicateFieldNames
+ ));
+ }
+ }
+ }
+ }
+ }
+
/** {@inheritDoc} */
@Override public Set<String> getSupportedAnnotationTypes() {
- return Set.of(Config.class.getCanonicalName(), ConfigurationRoot.class.getCanonicalName());
+ return Set.of(
+ Config.class.getCanonicalName(),
+ ConfigurationRoot.class.getCanonicalName(),
+ InternalConfiguration.class.getCanonicalName()
+ );
}
/** {@inheritDoc} */
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
index b1e69fb..cd2e3c1 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
@@ -111,7 +111,7 @@ public class ConfigurationChangerTest {
public void testSimpleConfigurationChange() throws Exception {
var storage = new TestConfigurationStorage(LOCAL);
- ConfigurationChanger changer = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage);
+ ConfigurationChanger changer = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage, List.of());
changer.start();
changer.change(source(KEY, (AChange parent) -> parent
@@ -133,10 +133,10 @@ public class ConfigurationChangerTest {
public void testModifiedFromAnotherStorage() throws Exception {
var storage = new TestConfigurationStorage(LOCAL);
- ConfigurationChanger changer1 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage);
+ ConfigurationChanger changer1 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage, List.of());
changer1.start();
- ConfigurationChanger changer2 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage);
+ ConfigurationChanger changer2 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage, List.of());
changer2.start();
changer1.change(source(KEY, (AChange parent) -> parent
@@ -174,7 +174,7 @@ public class ConfigurationChangerTest {
public void testModifiedFromAnotherStorageWithIncompatibleChanges() throws Exception {
var storage = new TestConfigurationStorage(LOCAL);
- ConfigurationChanger changer1 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage);
+ ConfigurationChanger changer1 = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage, List.of());
changer1.start();
Validator<MaybeInvalid, Object> validator = new Validator<>() {
@@ -188,7 +188,8 @@ public class ConfigurationChangerTest {
cgen,
List.of(KEY),
Map.of(MaybeInvalid.class, Set.of(validator)),
- storage
+ storage,
+ List.of()
);
changer2.start();
@@ -220,7 +221,7 @@ public class ConfigurationChangerTest {
public void testFailedToWrite() {
var storage = new TestConfigurationStorage(LOCAL);
- ConfigurationChanger changer = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage);
+ ConfigurationChanger changer = new TestConfigurationChanger(cgen, List.of(KEY), Map.of(), storage, List.of());
storage.fail(true);
@@ -279,7 +280,13 @@ public class ConfigurationChangerTest {
public void defaultsOnInit() throws Exception {
var storage = new TestConfigurationStorage(LOCAL);
- var changer = new TestConfigurationChanger(cgen, List.of(DefaultsConfiguration.KEY), Map.of(), storage);
+ var changer = new TestConfigurationChanger(
+ cgen,
+ List.of(DefaultsConfiguration.KEY),
+ Map.of(),
+ storage,
+ List.of()
+ );
changer.start();
@@ -311,7 +318,7 @@ public class ConfigurationChangerTest {
}
};
- node.construct(rootKey.key(), changerSrc);
+ node.construct(rootKey.key(), changerSrc, true);
}
};
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
new file mode 100644
index 0000000..8b65be0
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.internal.configuration;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Class for testing the {@link ConfigurationRegistry}.
+ */
+public class ConfigurationRegistryTest {
+ /** */
+ @Test
+ void testValidationInternalConfigurationExtensions() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ConfigurationRegistry(
+ List.of(SecondRootConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ List.of(ExtendedFirstRootConfigurationSchema.class)
+ )
+ );
+
+ // Check that everything is fine.
+ new ConfigurationRegistry(
+ List.of(FirstRootConfiguration.KEY, SecondRootConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ List.of(ExtendedFirstRootConfigurationSchema.class)
+ );
+ }
+
+ /**
+ * First root configuration.
+ */
+ @ConfigurationRoot(rootName = "first")
+ public static class FirstRootConfigurationSchema {
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str = "str";
+ }
+
+ /**
+ * First root configuration.
+ */
+ @ConfigurationRoot(rootName = "second")
+ public static class SecondRootConfigurationSchema {
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str = "str";
+ }
+
+ /**
+ * First extended root configuration.
+ */
+ @InternalConfiguration
+ public static class ExtendedFirstRootConfigurationSchema extends FirstRootConfigurationSchema {
+ /** String field. */
+ @Value(hasDefault = true)
+ public String strEx = "str";
+ }
+}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationChanger.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationChanger.java
index 0d80082..4d2de45 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationChanger.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationChanger.java
@@ -22,12 +22,16 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
import org.apache.ignite.configuration.validation.Validator;
import org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator;
import org.apache.ignite.internal.configuration.storage.ConfigurationStorage;
import org.apache.ignite.internal.configuration.tree.InnerNode;
import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.internalSchemaExtensions;
/** Implementation of {@link ConfigurationChanger} to be used in tests. Has no support of listeners. */
public class TestConfigurationChanger extends ConfigurationChanger {
@@ -41,13 +45,16 @@ public class TestConfigurationChanger extends ConfigurationChanger {
* @param rootKeys Configuration root keys.
* @param validators Validators.
* @param storage Configuration storage.
+ * @param internalSchemaExtensions Internal extensions ({@link InternalConfiguration})
+ * of configuration schemas ({@link ConfigurationRoot} and {@link Config}).
* @throws IllegalArgumentException If the configuration type of the root keys is not equal to the storage type.
*/
public TestConfigurationChanger(
ConfigurationAsmGenerator cgen,
Collection<RootKey<?, ?>> rootKeys,
Map<Class<? extends Annotation>, Set<Validator<?, ?>>> validators,
- ConfigurationStorage storage
+ ConfigurationStorage storage,
+ Collection<Class<?>> internalSchemaExtensions
) {
super(
(oldRoot, newRoot, revision) -> completedFuture(null),
@@ -58,7 +65,9 @@ public class TestConfigurationChanger extends ConfigurationChanger {
this.cgen = cgen;
- rootKeys.forEach(key -> cgen.compileRootSchema(key.schemaClass()));
+ Map<Class<?>, Set<Class<?>>> extensions = internalSchemaExtensions(internalSchemaExtensions);
+
+ rootKeys.forEach(key -> cgen.compileRootSchema(key.schemaClass(), extensions));
}
/** {@inheritDoc} */
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
new file mode 100644
index 0000000..72a5c83
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.internal.configuration.asm;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+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.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.internal.configuration.ConfigurationChanger;
+import org.apache.ignite.internal.configuration.DynamicConfiguration;
+import org.apache.ignite.internal.configuration.TestConfigurationChanger;
+import org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
+import org.apache.ignite.internal.configuration.tree.InnerNode;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.addDefaults;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Testing the {@link ConfigurationAsmGenerator}.
+ */
+public class ConfigurationAsmGeneratorTest {
+ /** Configuration changer. */
+ private static ConfigurationChanger changer;
+
+ /** Configuration generator. */
+ private static ConfigurationAsmGenerator generator;
+
+ /** */
+ @BeforeAll
+ public static void beforeAll() {
+ Collection<Class<?>> extensions = List.of(
+ ExtendedTestRootConfigurationSchema.class,
+ ExtendedSecondTestRootConfigurationSchema.class,
+ ExtendedTestConfigurationSchema.class,
+ ExtendedSecondTestConfigurationSchema.class
+ );
+
+ generator = new ConfigurationAsmGenerator();
+
+ changer = new TestConfigurationChanger(
+ generator,
+ List.of(TestRootConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ extensions
+ );
+
+ changer.start();
+ changer.initializeDefaults();
+ }
+
+ /** */
+ @AfterAll
+ public static void after() {
+ changer.stop();
+
+ generator = null;
+ changer = null;
+ }
+
+ /** */
+ @Test
+ void testExtendedRootConfiguration() throws Exception {
+ DynamicConfiguration<?, ?> config = generator.instantiateCfg(TestRootConfiguration.KEY, changer);
+
+ TestRootConfiguration baseRootConfig = (TestRootConfiguration)config;
+
+ ExtendedTestRootConfiguration extendedRootConfig = (ExtendedTestRootConfiguration)config;
+
+ ExtendedSecondTestRootConfiguration extendedSecondRootConfig = (ExtendedSecondTestRootConfiguration)config;
+
+ assertSame(baseRootConfig.i0(), extendedRootConfig.i0());
+ assertSame(baseRootConfig.i0(), extendedSecondRootConfig.i0());
+
+ assertSame(baseRootConfig.str0(), extendedRootConfig.str0());
+ assertSame(baseRootConfig.str0(), extendedSecondRootConfig.str0());
+
+ assertSame(baseRootConfig.subCfg(), extendedRootConfig.subCfg());
+ assertSame(baseRootConfig.subCfg(), extendedSecondRootConfig.subCfg());
+
+ assertSame(baseRootConfig.namedCfg(), extendedRootConfig.namedCfg());
+ assertSame(baseRootConfig.namedCfg(), extendedSecondRootConfig.namedCfg());
+
+ assertNotNull(extendedSecondRootConfig.i1());
+
+ // Check view and change interfaces.
+
+ assertTrue(baseRootConfig.value() instanceof ExtendedTestRootView);
+ assertTrue(baseRootConfig.value() instanceof ExtendedSecondTestRootView);
+
+ assertSame(baseRootConfig.value(), extendedRootConfig.value());
+ assertSame(baseRootConfig.value(), extendedSecondRootConfig.value());
+
+ baseRootConfig.change(c -> {
+ assertTrue(c instanceof ExtendedTestRootChange);
+ assertTrue(c instanceof ExtendedSecondTestRootChange);
+
+ c.changeI0(10).changeStr0("str0");
+
+ ((ExtendedTestRootChange)c).changeStr1("str1").changeStr0("str0");
+ ((ExtendedSecondTestRootChange)c).changeI1(200).changeStr0("str0");
+ }).get(1, SECONDS);
+ }
+
+ /** */
+ @Test
+ void testExtendedSubConfiguration() throws Exception {
+ DynamicConfiguration<?, ?> config = generator.instantiateCfg(TestRootConfiguration.KEY, changer);
+
+ TestRootConfiguration rootConfig = (TestRootConfiguration)config;
+
+ TestConfiguration baseSubConfig = rootConfig.subCfg();
+
+ ExtendedTestConfiguration extendedSubConfig = (ExtendedTestConfiguration)rootConfig.subCfg();
+
+ ExtendedSecondTestConfiguration extendedSecondSubConfig =
+ (ExtendedSecondTestConfiguration)rootConfig.subCfg();
+
+ assertSame(baseSubConfig.i0(), extendedSubConfig.i0());
+ assertSame(baseSubConfig.i0(), extendedSecondSubConfig.i0());
+
+ assertSame(baseSubConfig.str2(), extendedSubConfig.str2());
+ assertSame(baseSubConfig.str2(), extendedSecondSubConfig.str2());
+
+ assertNotNull(extendedSecondSubConfig.i1());
+
+ // Check view and change interfaces.
+
+ assertTrue(baseSubConfig.value() instanceof ExtendedTestView);
+ assertTrue(baseSubConfig.value() instanceof ExtendedSecondTestView);
+
+ assertSame(baseSubConfig.value(), extendedSubConfig.value());
+ assertSame(baseSubConfig.value(), extendedSecondSubConfig.value());
+
+ baseSubConfig.change(c -> {
+ assertTrue(c instanceof ExtendedTestChange);
+ assertTrue(c instanceof ExtendedSecondTestChange);
+
+ c.changeI0(10).changeStr2("str2");
+
+ ((ExtendedTestChange)c).changeStr3("str3").changeStr2("str2");
+ ((ExtendedSecondTestChange)c).changeI1(200).changeStr2("str2");
+ }).get(1, SECONDS);
+ }
+
+ /** */
+ @Test
+ void testExtendedNamedConfiguration() throws Exception {
+ DynamicConfiguration<?, ?> config = generator.instantiateCfg(TestRootConfiguration.KEY, changer);
+
+ TestRootConfiguration rootConfig = (TestRootConfiguration)config;
+
+ String key = UUID.randomUUID().toString();
+
+ rootConfig.namedCfg().change(c -> c.create(key, c0 -> c0.changeI0(0).changeStr2("foo2"))).get(1, SECONDS);
+
+ TestConfiguration namedConfig = rootConfig.namedCfg().get(key);
+
+ assertTrue(namedConfig instanceof ExtendedTestConfiguration);
+ assertTrue(namedConfig instanceof ExtendedSecondTestConfiguration);
+
+ // Check view and change interfaces.
+
+ assertTrue(namedConfig.value() instanceof ExtendedTestView);
+ assertTrue(namedConfig.value() instanceof ExtendedSecondTestView);
+
+ namedConfig.change(c -> {
+ assertTrue(c instanceof ExtendedTestChange);
+ assertTrue(c instanceof ExtendedSecondTestChange);
+
+ c.changeStr2("str2").changeI0(10);
+
+ ((ExtendedTestChange)c).changeStr3("str3").changeStr2("str2");
+ ((ExtendedSecondTestChange)c).changeI1(100).changeStr2("str2");
+ }).get(1, SECONDS);
+ }
+
+ /** */
+ @Test
+ void testConstructInternalConfig() {
+ InnerNode innerNode = generator.instantiateNode(TestRootConfiguration.KEY.schemaClass());
+
+ addDefaults(innerNode);
+
+ InnerNode subInnerNode = (InnerNode)((TestRootView)innerNode).subCfg();
+
+ // Check that no fields for internal configuration will be changed.
+
+ assertThrows(NoSuchElementException.class, () -> innerNode.construct("str1", null, false));
+ assertThrows(NoSuchElementException.class, () -> innerNode.construct("i1", null, false));
+
+ assertThrows(NoSuchElementException.class, () -> subInnerNode.construct("str3", null, false));
+ assertThrows(NoSuchElementException.class, () -> subInnerNode.construct("i1", null, false));
+
+ // Check that fields for internal configuration will be changed.
+
+ innerNode.construct("str1", null, true);
+ innerNode.construct("i1", null, true);
+
+ subInnerNode.construct("str3", null, true);
+ subInnerNode.construct("i1", null, true);
+ }
+
+ /**
+ * Test root configuration schema.
+ */
+ @ConfigurationRoot(rootName = "test")
+ public static class TestRootConfigurationSchema {
+ /** Integer field. */
+ @Value(hasDefault = true)
+ public int i0 = 0;
+
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str0 = "str0";
+
+ /** Sub configuration field. */
+ @ConfigValue
+ public TestConfigurationSchema subCfg;
+
+ /** Named configuration field. */
+ @NamedConfigValue
+ public TestConfigurationSchema namedCfg;
+ }
+
+ /**
+ * Extending the {@link TestRootConfigurationSchema}.
+ */
+ @InternalConfiguration
+ public static class ExtendedTestRootConfigurationSchema extends TestRootConfigurationSchema {
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str1 = "str1";
+ }
+
+ /**
+ * Extending the {@link TestRootConfigurationSchema}.
+ */
+ @InternalConfiguration
+ public static class ExtendedSecondTestRootConfigurationSchema extends TestRootConfigurationSchema {
+ /** Integer field. */
+ @Value(hasDefault = true)
+ public int i1 = 0;
+ }
+
+ /**
+ * Test configuration schema.
+ */
+ @Config
+ public static class TestConfigurationSchema {
+ /** Integer field. */
+ @Value(hasDefault = true)
+ public int i0 = 0;
+
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str2 = "str2";
+ }
+
+ /**
+ * Extending the {@link TestConfigurationSchema}.
+ */
+ @InternalConfiguration
+ public static class ExtendedTestConfigurationSchema extends TestConfigurationSchema {
+ /** String field. */
+ @Value(hasDefault = true)
+ public String str3 = "str3";
+ }
+
+ /**
+ * Extending the {@link TestConfigurationSchema}.
+ */
+ @InternalConfiguration
+ public static class ExtendedSecondTestConfigurationSchema extends TestConfigurationSchema {
+ /** Integer field. */
+ @Value(hasDefault = true)
+ public int i1 = 0;
+ }
+}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/hocon/HoconConverterTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/hocon/HoconConverterTest.java
index 85174e8..4adb616 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/hocon/HoconConverterTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/hocon/HoconConverterTest.java
@@ -160,7 +160,8 @@ public class HoconConverterTest {
registry = new ConfigurationRegistry(
List.of(HoconRootConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
registry.start();
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/notifications/ConfigurationListenerTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/notifications/ConfigurationListenerTest.java
index 77753dd..72e312c 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/notifications/ConfigurationListenerTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/notifications/ConfigurationListenerTest.java
@@ -78,7 +78,12 @@ public class ConfigurationListenerTest {
public void before() {
var testConfigurationStorage = new TestConfigurationStorage(LOCAL);
- registry = new ConfigurationRegistry(List.of(ParentConfiguration.KEY), Map.of(), testConfigurationStorage);
+ registry = new ConfigurationRegistry(
+ List.of(ParentConfiguration.KEY),
+ Map.of(),
+ testConfigurationStorage,
+ List.of()
+ );
registry.start();
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/sample/UsageTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/sample/UsageTest.java
index 72a8f0a..c747ee5 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/sample/UsageTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/sample/UsageTest.java
@@ -52,7 +52,8 @@ public class UsageTest {
registry = new ConfigurationRegistry(
List.of(LocalConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
registry.start();
@@ -112,7 +113,8 @@ public class UsageTest {
registry = new ConfigurationRegistry(
List.of(NetworkConfiguration.KEY, LocalConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
registry.start();
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConfigurationArrayTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConfigurationArrayTest.java
index a0422ff..927e047 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConfigurationArrayTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConfigurationArrayTest.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.configuration.tree;
+import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Stream;
@@ -84,7 +85,7 @@ public class ConfigurationArrayTest {
/** */
@BeforeAll
public static void beforeAll() {
- cgen.compileRootSchema(TestArrayConfigurationSchema.class);
+ cgen.compileRootSchema(TestArrayConfigurationSchema.class, Map.of());
}
/** */
@@ -145,7 +146,7 @@ public class ConfigurationArrayTest {
* Gets an array field from the given {@code InnerNode}.
*/
private static <T> T getArray(InnerNode arrayNode, Class<T> cls) {
- return cls.cast(arrayNode.traverseChild(getFieldName(cls), leafNodeVisitor()));
+ return cls.cast(arrayNode.traverseChild(getFieldName(cls), leafNodeVisitor(), true));
}
/**
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNodeTest.java
index 6d1503c..4d72612 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNodeTest.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.configuration.tree;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator;
import org.junit.jupiter.api.AfterAll;
@@ -38,7 +39,7 @@ public class ConstructableTreeNodeTest {
public static void beforeAll() {
cgen = new ConfigurationAsmGenerator();
- cgen.compileRootSchema(TraversableTreeNodeTest.ParentConfigurationSchema.class);
+ cgen.compileRootSchema(TraversableTreeNodeTest.ParentConfigurationSchema.class, Map.of());
}
@AfterAll
@@ -59,7 +60,7 @@ public class ConstructableTreeNodeTest {
public void noKey() {
var childNode = newChildInstance();
- assertThrows(NoSuchElementException.class, () -> childNode.construct("foo", null));
+ assertThrows(NoSuchElementException.class, () -> childNode.construct("foo", null, true));
}
/** */
@@ -77,7 +78,7 @@ public class ConstructableTreeNodeTest {
// Named list node.
var elements = parentNode.elements();
- parentNode.construct("elements", null);
+ parentNode.construct("elements", null, true);
assertNotNull(parentNode.elements());
assertNotSame(elements, parentNode.elements());
@@ -86,12 +87,12 @@ public class ConstructableTreeNodeTest {
// Inner node.
NamedElementView element = elements.get("name");
- ((ConstructableTreeNode)elements).construct("name", null);
+ ((ConstructableTreeNode)elements).construct("name", null, true);
assertNull(elements.get("name"));
// Leaf.
- ((ConstructableTreeNode)element).construct("strCfg", null);
+ ((ConstructableTreeNode)element).construct("strCfg", null, true);
assertNull(element.strCfg());
}
@@ -119,16 +120,16 @@ public class ConstructableTreeNodeTest {
public void unwrap() {
var childNode = newChildInstance();
- childNode.construct("strCfg", new ConstantConfigurationSource("value"));
+ childNode.construct("strCfg", new ConstantConfigurationSource("value"), true);
assertEquals("value", childNode.strCfg());
- childNode.construct("intCfg", new ConstantConfigurationSource(255));
+ childNode.construct("intCfg", new ConstantConfigurationSource(255), true);
assertEquals(255, childNode.intCfg());
assertThrows(ClassCastException.class, () ->
- childNode.construct("intCfg", new ConstantConfigurationSource(new Object()))
+ childNode.construct("intCfg", new ConstantConfigurationSource(new Object()), true)
);
}
@@ -144,9 +145,9 @@ public class ConstructableTreeNodeTest {
}
@Override public void descend(ConstructableTreeNode node) {
- node.construct("strCfg", new ConstantConfigurationSource("value"));
+ node.construct("strCfg", new ConstantConfigurationSource("value"), true);
}
- });
+ }, true);
assertEquals("value", parentNode.child().strCfg());
@@ -159,9 +160,9 @@ public class ConstructableTreeNodeTest {
}
@Override public void descend(ConstructableTreeNode node) {
- node.construct("strCfg", new ConstantConfigurationSource("value"));
+ node.construct("strCfg", new ConstantConfigurationSource("value"), true);
}
- });
+ }, true);
assertEquals("value", elementsNode.get("name").strCfg());
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/NamedListNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/NamedListNodeTest.java
index e1b1491..eb28d04 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/NamedListNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/NamedListNodeTest.java
@@ -90,7 +90,7 @@ public class NamedListNodeTest {
public void before() {
storage = new TestConfigurationStorage(LOCAL);
- changer = new TestConfigurationChanger(cgen, List.of(AConfiguration.KEY), Map.of(), storage);
+ changer = new TestConfigurationChanger(cgen, List.of(AConfiguration.KEY), Map.of(), storage, List.of());
changer.start();
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/TraversableTreeNodeTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/TraversableTreeNodeTest.java
index d2f9b52..f6ddfd0 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/TraversableTreeNodeTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/tree/TraversableTreeNodeTest.java
@@ -18,9 +18,11 @@
package org.apache.ignite.internal.configuration.tree;
import java.io.Serializable;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.TreeSet;
import org.apache.ignite.configuration.NamedListChange;
import org.apache.ignite.configuration.NamedListView;
import org.apache.ignite.configuration.annotation.Config;
@@ -52,7 +54,7 @@ public class TraversableTreeNodeTest {
public static void beforeAll() {
cgen = new ConfigurationAsmGenerator();
- cgen.compileRootSchema(ParentConfigurationSchema.class);
+ cgen.compileRootSchema(ParentConfigurationSchema.class, Map.of());
}
@AfterAll
@@ -277,7 +279,7 @@ public class TraversableTreeNodeTest {
public void traverseChildren() {
var parentNode = newParentInstance();
- List<String> keys = new ArrayList<>(2);
+ Collection<String> keys = new TreeSet<>();
parentNode.traverseChildren(new ConfigurationVisitor<Object>() {
@Override public Object visitInnerNode(String key, InnerNode node) {
@@ -293,10 +295,10 @@ public class TraversableTreeNodeTest {
return keys.add(key);
}
- });
+ }, true);
// Assert that updates happened in the same order as fields declaration in schema.
- assertEquals(List.of("child", "elements"), keys);
+ assertEquals(new TreeSet<>(List.of("child", "elements")), keys);
keys.clear();
@@ -306,10 +308,10 @@ public class TraversableTreeNodeTest {
@Override public Object visitLeafNode(String key, Serializable val) {
return keys.add(key);
}
- });
+ }, true);
// Assert that updates happened in the same order as fields declaration in schema.
- assertEquals(List.of("intCfg", "strCfg"), keys);
+ assertEquals(new TreeSet<>(List.of("intCfg", "strCfg")), keys);
}
/**
@@ -327,7 +329,7 @@ public class TraversableTreeNodeTest {
throw new VisitException();
}
- })
+ }, true)
);
// Assert that proper method has been invoked.
@@ -339,7 +341,7 @@ public class TraversableTreeNodeTest {
throw new VisitException();
}
- })
+ }, true)
);
var childNode = newChildInstance();
@@ -352,12 +354,12 @@ public class TraversableTreeNodeTest {
throw new VisitException();
}
- })
+ }, true)
);
// Assert that traversing inexistent field leads to exception.
assertThrows(NoSuchElementException.class, () ->
- childNode.traverseChild("foo", new ConfigurationVisitor<>() {})
+ childNode.traverseChild("foo", new ConfigurationVisitor<>() {}, true)
);
}
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
index fa72dff..0f61d65 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
@@ -18,19 +18,26 @@
package org.apache.ignite.internal.configuration.util;
import java.io.Serializable;
+import java.lang.reflect.Field;
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.Stream;
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.InternalConfiguration;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.internal.configuration.RootInnerNode;
import org.apache.ignite.internal.configuration.SuperRoot;
import org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator;
import org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
+import org.apache.ignite.internal.configuration.tree.ConverterToMapVisitor;
import org.apache.ignite.internal.configuration.tree.InnerNode;
import org.apache.ignite.internal.configuration.tree.TraversableTreeNode;
import org.jetbrains.annotations.NotNull;
@@ -39,12 +46,18 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static java.util.Collections.singletonMap;
+import static java.util.stream.Collectors.toSet;
import static org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
import static org.apache.ignite.internal.configuration.tree.NamedListNode.NAME;
import static org.apache.ignite.internal.configuration.tree.NamedListNode.ORDER_IDX;
import static org.apache.ignite.internal.configuration.util.ConfigurationFlattener.createFlattenedUpdatesMap;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.addDefaults;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.checkConfigurationType;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.collectSchemas;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.extensionsFields;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.find;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.internalSchemaExtensions;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.allOf;
@@ -55,9 +68,11 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.matchesPattern;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
/** */
public class ConfigurationUtilTest {
@@ -67,7 +82,7 @@ public class ConfigurationUtilTest {
public static void beforeAll() {
cgen = new ConfigurationAsmGenerator();
- cgen.compileRootSchema(ParentConfigurationSchema.class);
+ cgen.compileRootSchema(ParentConfigurationSchema.class, Map.of());
}
@AfterAll
@@ -140,8 +155,8 @@ public class ConfigurationUtilTest {
}
/**
- * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode)} finds proper node when provided with correct
- * path.
+ * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode, boolean)} finds proper node when provided
+ * with correct path.
*/
@Test
public void findSuccessfully() {
@@ -157,52 +172,52 @@ public class ConfigurationUtilTest {
assertSame(
parent,
- ConfigurationUtil.find(List.of(), parent)
+ ConfigurationUtil.find(List.of(), parent, true)
);
assertSame(
parent.elements(),
- ConfigurationUtil.find(List.of("elements"), parent)
+ ConfigurationUtil.find(List.of("elements"), parent, true)
);
assertSame(
parent.elements().get("name"),
- ConfigurationUtil.find(List.of("elements", "name"), parent)
+ ConfigurationUtil.find(List.of("elements", "name"), parent, true)
);
assertSame(
parent.elements().get("name").child(),
- ConfigurationUtil.find(List.of("elements", "name", "child"), parent)
+ ConfigurationUtil.find(List.of("elements", "name", "child"), parent, true)
);
assertSame(
parent.elements().get("name").child().str(),
- ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent)
+ ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent, true)
);
}
/**
- * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode)} returns null when path points to nonexistent
- * named list element.
+ * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode, boolean)} returns null when path points to
+ * nonexistent named list element.
*/
@Test
public void findNulls() {
var parent = newParentInstance();
- assertNull(ConfigurationUtil.find(List.of("elements", "name"), parent));
+ assertNull(ConfigurationUtil.find(List.of("elements", "name"), parent, true));
parent.changeElements(elements -> elements.createOrUpdate("name", element -> {}));
- assertNull(ConfigurationUtil.find(List.of("elements", "name", "child"), parent));
+ assertNull(ConfigurationUtil.find(List.of("elements", "name", "child"), parent, true));
((NamedElementChange)parent.elements().get("name")).changeChild(child -> {});
- assertNull(ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent));
+ assertNull(ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent, true));
}
/**
- * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode)} throws {@link KeyNotFoundException} when
- * provided with a wrong path.
+ * Tests that {@link ConfigurationUtil#find(List, TraversableTreeNode, boolean)} throws
+ * {@link KeyNotFoundException} when provided with a wrong path.
*/
@Test
public void findUnsuccessfully() {
@@ -210,21 +225,21 @@ public class ConfigurationUtilTest {
assertThrows(
KeyNotFoundException.class,
- () -> ConfigurationUtil.find(List.of("elements", "name", "child"), parent)
+ () -> ConfigurationUtil.find(List.of("elements", "name", "child"), parent, true)
);
parent.changeElements(elements -> elements.createOrUpdate("name", element -> {}));
assertThrows(
KeyNotFoundException.class,
- () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent)
+ () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent, true)
);
((NamedElementChange)parent.elements().get("name")).changeChild(child -> child.changeStr("value"));
assertThrows(
KeyNotFoundException.class,
- () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str", "foo"), parent)
+ () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str", "foo"), parent, true)
);
}
@@ -395,6 +410,225 @@ public class ConfigurationUtilTest {
);
}
+ /** */
+ @Test
+ void testInternalSchemaExtensions() {
+ assertTrue(internalSchemaExtensions(List.of()).isEmpty());
+
+ assertThrows(IllegalArgumentException.class, () -> internalSchemaExtensions(List.of(Object.class)));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> internalSchemaExtensions(List.of(SimpleRootConfigurationSchema.class))
+ );
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> internalSchemaExtensions(List.of(SimpleConfigurationSchema.class))
+ );
+
+ Map<Class<?>, Set<Class<?>>> extensions = internalSchemaExtensions(List.of(
+ InternalFirstSimpleRootConfigurationSchema.class,
+ InternalSecondSimpleRootConfigurationSchema.class,
+ InternalFirstSimpleConfigurationSchema.class,
+ InternalSecondSimpleConfigurationSchema.class
+ ));
+
+ assertEquals(2, extensions.size());
+
+ assertEquals(
+ Set.of(
+ InternalFirstSimpleRootConfigurationSchema.class,
+ InternalSecondSimpleRootConfigurationSchema.class
+ ),
+ extensions.get(SimpleRootConfigurationSchema.class)
+ );
+
+ assertEquals(
+ Set.of(
+ InternalFirstSimpleConfigurationSchema.class,
+ InternalSecondSimpleConfigurationSchema.class
+ ),
+ extensions.get(SimpleConfigurationSchema.class)
+ );
+ }
+
+ /** */
+ @Test
+ void testSchemaFields() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> extensionsFields(
+ List.of(
+ InternalExtendedSimpleRootConfigurationSchema.class,
+ ErrorInternalExtendedSimpleRootConfigurationSchema.class
+ )
+ )
+ );
+
+ assertTrue(extensionsFields(List.of()).isEmpty());
+
+ List<Class<?>> extensions = List.of(
+ InternalFirstSimpleRootConfigurationSchema.class,
+ InternalSecondSimpleRootConfigurationSchema.class
+ );
+
+ Set<Field> exp = extensions.stream()
+ .flatMap(cls -> Stream.of(cls.getDeclaredFields()))
+ .collect(toSet());
+
+ assertEquals(exp, extensionsFields(extensions));
+ }
+
+ /** */
+ @Test
+ void testFindInternalConfigs() {
+ Map<Class<?>, Set<Class<?>>> extensions = internalSchemaExtensions(List.of(
+ InternalFirstSimpleRootConfigurationSchema.class,
+ InternalSecondSimpleRootConfigurationSchema.class,
+ InternalFirstSimpleConfigurationSchema.class,
+ InternalSecondSimpleConfigurationSchema.class
+ ));
+
+ ConfigurationAsmGenerator generator = new ConfigurationAsmGenerator();
+ generator.compileRootSchema(SimpleRootConfigurationSchema.class, extensions);
+
+ InnerNode innerNode = generator.instantiateNode(SimpleRootConfigurationSchema.class);
+
+ addDefaults(innerNode);
+
+ // Check that no internal configuration will be found.
+
+ assertThrows(KeyNotFoundException.class, () -> find(List.of("str2"), innerNode, false));
+ assertThrows(KeyNotFoundException.class, () -> find(List.of("str3"), innerNode, false));
+ assertThrows(KeyNotFoundException.class, () -> find(List.of("subCfg1"), innerNode, false));
+
+ assertThrows(KeyNotFoundException.class, () -> find(List.of("subCfg", "str01"), innerNode, false));
+ assertThrows(KeyNotFoundException.class, () -> find(List.of("subCfg", "str02"), innerNode, false));
+
+ // Check that internal configuration will be found.
+
+ assertNull(find(List.of("str2"), innerNode, true));
+ assertEquals("foo", find(List.of("str3"), innerNode, true));
+ assertNotNull(find(List.of("subCfg1"), innerNode, true));
+
+ assertEquals("foo", find(List.of("subCfg", "str01"), innerNode, true));
+ assertEquals("foo", find(List.of("subCfg", "str02"), innerNode, true));
+ }
+
+ /** */
+ @Test
+ void testGetInternalConfigs() {
+ Map<Class<?>, Set<Class<?>>> extensions = internalSchemaExtensions(List.of(
+ InternalFirstSimpleRootConfigurationSchema.class,
+ InternalSecondSimpleRootConfigurationSchema.class,
+ InternalFirstSimpleConfigurationSchema.class,
+ InternalSecondSimpleConfigurationSchema.class
+ ));
+
+ ConfigurationAsmGenerator generator = new ConfigurationAsmGenerator();
+ generator.compileRootSchema(SimpleRootConfigurationSchema.class, extensions);
+
+ InnerNode innerNode = generator.instantiateNode(SimpleRootConfigurationSchema.class);
+
+ addDefaults(innerNode);
+
+ Map<String, Object> config = (Map<String, Object>)innerNode.accept(null, new ConverterToMapVisitor(false));
+
+ // Check that no internal configuration will be received.
+
+ assertEquals(4, config.size());
+ assertNull(config.get("str0"));
+ assertEquals("foo", config.get("str1"));
+ assertNotNull(config.get("subCfg"));
+ assertNotNull(config.get("namedCfg"));
+
+ Map<String, Object> subConfig = (Map<String, Object>)config.get("subCfg");
+
+ assertEquals(1, subConfig.size());
+ assertEquals("foo", subConfig.get("str00"));
+
+ // Check that no internal configuration will be received.
+
+ config = (Map<String, Object>)innerNode.accept(null, new ConverterToMapVisitor(true));
+
+ assertEquals(7, config.size());
+ assertNull(config.get("str0"));
+ assertNull(config.get("str2"));
+ assertEquals("foo", config.get("str1"));
+ assertEquals("foo", config.get("str3"));
+ assertNotNull(config.get("subCfg"));
+ assertNotNull(config.get("subCfg1"));
+ assertNotNull(config.get("namedCfg"));
+
+ subConfig = (Map<String, Object>)config.get("subCfg");
+
+ assertEquals(3, subConfig.size());
+ assertEquals("foo", subConfig.get("str00"));
+ assertEquals("foo", subConfig.get("str01"));
+ assertEquals("foo", subConfig.get("str02"));
+
+ subConfig = (Map<String, Object>)config.get("subCfg1");
+
+ assertEquals(3, subConfig.size());
+ assertEquals("foo", subConfig.get("str00"));
+ assertEquals("foo", subConfig.get("str01"));
+ assertEquals("foo", subConfig.get("str02"));
+ }
+
+ /** */
+ @Test
+ void testSuperRootWithInternalConfig() {
+ ConfigurationAsmGenerator generator = new ConfigurationAsmGenerator();
+
+ Class<?> schemaClass = InternalWithoutSuperclassConfigurationSchema.class;
+ RootKey<?, ?> schemaKey = InternalWithoutSuperclassConfiguration.KEY;
+
+ generator.compileRootSchema(schemaClass, Map.of());
+
+ SuperRoot superRoot = new SuperRoot(
+ s -> new RootInnerNode(schemaKey, generator.instantiateNode(schemaClass))
+ );
+
+ assertThrows(NoSuchElementException.class, () -> superRoot.construct(schemaKey.key(), null, false));
+
+ superRoot.construct(schemaKey.key(), null, true);
+
+ superRoot.addRoot(schemaKey, generator.instantiateNode(schemaClass));
+
+ assertThrows(KeyNotFoundException.class, () -> find(List.of(schemaKey.key()), superRoot, false));
+
+ assertNotNull(find(List.of(schemaKey.key()), superRoot, true));
+
+ Map<String, Object> config =
+ (Map<String, Object>)superRoot.accept(schemaKey.key(), new ConverterToMapVisitor(false));
+
+ assertTrue(config.isEmpty());
+
+ config = (Map<String, Object>)superRoot.accept(schemaKey.key(), new ConverterToMapVisitor(true));
+
+ assertEquals(1, config.size());
+ assertNotNull(config.get(schemaKey.key()));
+ }
+
+ /** */
+ @Test
+ void testCollectSchemas() {
+ assertTrue(collectSchemas(List.of()).isEmpty());
+
+ assertThrows(IllegalArgumentException.class, () -> collectSchemas(List.of(Object.class)));
+
+ assertEquals(
+ Set.of(LocalFirstConfigurationSchema.class, SimpleConfigurationSchema.class),
+ collectSchemas(List.of(LocalFirstConfigurationSchema.class, SimpleConfigurationSchema.class))
+ );
+
+ assertEquals(
+ Set.of(SimpleRootConfigurationSchema.class, SimpleConfigurationSchema.class),
+ collectSchemas(List.of(SimpleRootConfigurationSchema.class))
+ );
+ }
+
/**
* Patches super root and returns flat representation of the changes. Passed {@code superRoot} object will contain
* patched tree when method execution is completed.
@@ -408,7 +642,7 @@ public class ConfigurationUtilTest {
SuperRoot originalSuperRoot = superRoot.copy();
// Make a copy of the root insode of the superRoot. This copy will be used for further patching.
- superRoot.construct(ParentConfiguration.KEY.key(), new ConfigurationSource() {});
+ superRoot.construct(ParentConfiguration.KEY.key(), new ConfigurationSource() {}, true);
// Patch root node.
patch.accept((ParentChange)superRoot.getRoot(ParentConfiguration.KEY));
@@ -456,4 +690,120 @@ public class ConfigurationUtilTest {
@Value(hasDefault = true)
public String str = "str";
}
+
+ /**
+ * Simple root configuration schema.
+ */
+ @ConfigurationRoot(rootName = "testRootSimple")
+ public static class SimpleRootConfigurationSchema {
+ /** String value without default. */
+ @Value
+ public String str0;
+
+ /** String value with default. */
+ @Value(hasDefault = true)
+ public String str1 = "foo";
+
+ /** Sub configuration schema. */
+ @ConfigValue
+ public SimpleConfigurationSchema subCfg;
+
+ /** Named configuration schema. */
+ @NamedConfigValue
+ public SimpleConfigurationSchema namedCfg;
+ }
+
+ /**
+ * Simple configuration schema.
+ */
+ @Config
+ public static class SimpleConfigurationSchema {
+ /** String value with default. */
+ @Value(hasDefault = true)
+ public String str00 = "foo";
+ }
+
+ /**
+ * Internal schema extension without superclass.
+ */
+ @InternalConfiguration
+ @ConfigurationRoot(rootName = "testRootInternal")
+ public static class InternalWithoutSuperclassConfigurationSchema {
+ }
+
+ /**
+ * First simple internal schema extension.
+ */
+ @InternalConfiguration
+ public static class InternalFirstSimpleConfigurationSchema extends SimpleConfigurationSchema {
+ /** String value with default. */
+ @Value(hasDefault = true)
+ public String str01 = "foo";
+ }
+
+ /**
+ * Second simple internal schema extension.
+ */
+ @InternalConfiguration
+ public static class InternalSecondSimpleConfigurationSchema extends SimpleConfigurationSchema {
+ /** String value with default. */
+ @Value(hasDefault = true)
+ public String str02 = "foo";
+ }
+
+ /**
+ * First root simple internal schema extension.
+ */
+ @InternalConfiguration
+ public static class InternalFirstSimpleRootConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** Second string value without default. */
+ @Value
+ public String str2;
+
+ /** Second string value with default. */
+ @Value(hasDefault = true)
+ public String str3 = "foo";
+ }
+
+ /**
+ * Second root simple internal schema extension.
+ */
+ @InternalConfiguration
+ public static class InternalSecondSimpleRootConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** Second sub configuration schema. */
+ @ConfigValue
+ public SimpleConfigurationSchema subCfg1;
+ }
+
+ /**
+ * Internal extended simple root configuration schema.
+ */
+ @InternalConfiguration
+ public static class InternalExtendedSimpleRootConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** String value without default. */
+ @Value
+ public String str00;
+
+ /** String value with default. */
+ @Value(hasDefault = true)
+ public String str01 = "foo";
+
+ /** Sub configuration schema. */
+ @ConfigValue
+ public SimpleConfigurationSchema subCfg0;
+
+ /** Named configuration schema. */
+ @NamedConfigValue
+ public SimpleConfigurationSchema namedCfg0;
+ }
+
+ /**
+ * Error: Duplicate field.
+ */
+ @InternalConfiguration
+ public static class ErrorInternalExtendedSimpleRootConfigurationSchema extends SimpleRootConfigurationSchema {
+ /** String value without default. */
+ @Value
+ public String str00;
+ }
}
diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/validation/ValidationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/validation/ValidationUtilTest.java
index 8946f09..e394fe3 100644
--- a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/validation/ValidationUtilTest.java
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/internal/configuration/validation/ValidationUtilTest.java
@@ -55,7 +55,7 @@ public class ValidationUtilTest {
public static void beforeAll() {
cgen = new ConfigurationAsmGenerator();
- cgen.compileRootSchema(ValidatedRootConfigurationSchema.class);
+ cgen.compileRootSchema(ValidatedRootConfigurationSchema.class, Map.of());
}
@AfterAll
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/RootKey.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/RootKey.java
index 3ff15be..af08226 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/RootKey.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/RootKey.java
@@ -19,6 +19,7 @@ package org.apache.ignite.configuration;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
import org.apache.ignite.internal.tostring.S;
/**
@@ -37,6 +38,9 @@ public class RootKey<T extends ConfigurationTree<VIEW, ?>, VIEW> {
/** Schema class for the root. */
private final Class<?> schemaClass;
+ /** Marked with {@link InternalConfiguration}. */
+ private final boolean internal;
+
/**
* Constructor.
*
@@ -51,6 +55,8 @@ public class RootKey<T extends ConfigurationTree<VIEW, ?>, VIEW> {
this.rootName = rootAnnotation.rootName();
this.storageType = rootAnnotation.type();
+
+ internal = schemaClass.isAnnotationPresent(InternalConfiguration.class);
}
/**
@@ -80,6 +86,15 @@ public class RootKey<T extends ConfigurationTree<VIEW, ?>, VIEW> {
return schemaClass;
}
+ /**
+ * Check if the root configuration is marked with {@link InternalConfiguration}.
+ *
+ * @return {@code true} if the root configuration is internal.
+ */
+ public boolean internal() {
+ return internal;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(RootKey.class, this);
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InternalConfiguration.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InternalConfiguration.java
new file mode 100644
index 0000000..f0ed596
--- /dev/null
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/InternalConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * This annotation can only be applied to a class that is either marked with {@link ConfigurationRoot}
+ * or the superclass is marked with {@link ConfigurationRoot}, {@link Config}.
+ * <p>
+ * It indicates that this is an internal configuration that should be hidden from the end user.
+ * Any extensions are allowed for any configuration.
+ * <p>
+ * NOTE: Field name collisions are not allowed.
+ *
+ * @see ConfigurationRoot
+ * @see Config
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface InternalConfiguration {
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
index b64f291..ff48983 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
@@ -158,11 +158,11 @@ public abstract class ConfigurationChanger {
*
* @return Function that creates root node by root name or returns {@code null} if root name is not found.
*/
- private Function<String, InnerNode> rootCreator() {
+ private Function<String, RootInnerNode> rootCreator() {
return key -> {
RootKey<?, ?> rootKey = rootKeys.get(key);
- return rootKey == null ? null : createRootNode(rootKey);
+ return rootKey == null ? null : new RootInnerNode(rootKey, createRootNode(rootKey));
};
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationManager.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationManager.java
index c1c6056..170594d 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationManager.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationManager.java
@@ -25,6 +25,9 @@ import java.util.concurrent.ExecutionException;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigObject;
import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
import org.apache.ignite.configuration.validation.Validator;
import org.apache.ignite.internal.configuration.hocon.HoconConverter;
import org.apache.ignite.internal.configuration.storage.ConfigurationStorage;
@@ -45,16 +48,20 @@ public class ConfigurationManager implements IgniteComponent {
* @param rootKeys Configuration root keys.
* @param validators Validators.
* @param storage Configuration storage.
- * @throws IllegalArgumentException If the configuration type of the root keys is not equal to the storage type.
+ * @param internalSchemaExtensions Internal extensions ({@link InternalConfiguration})
+ * of configuration schemas ({@link ConfigurationRoot} and {@link Config}).
+ * @throws IllegalArgumentException If the configuration type of the root keys is not equal to the storage type,
+ * or if the schema or its extensions are not valid.
*/
public ConfigurationManager(
Collection<RootKey<?, ?>> rootKeys,
Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators,
- ConfigurationStorage storage
+ ConfigurationStorage storage,
+ Collection<Class<?>> internalSchemaExtensions
) {
checkConfigurationType(rootKeys, storage);
- registry = new ConfigurationRegistry(rootKeys, validators, storage);
+ registry = new ConfigurationRegistry(rootKeys, validators, storage, internalSchemaExtensions);
}
/** {@inheritDoc} */
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationNode.java
index a7a6807..8025153 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationNode.java
@@ -106,7 +106,7 @@ public abstract class ConfigurationNode<VIEW, CHANGE> implements ConfigurationPr
return val;
try {
- VIEW newVal = (VIEW)ConfigurationUtil.find(keys.subList(1, keys.size()), newRootNode);
+ VIEW newVal = (VIEW)ConfigurationUtil.find(keys.subList(1, keys.size()), newRootNode, true);
synchronized (this) {
if (cachedRootNode == oldRootNode) {
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java
index 8ef663e..09223bf 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java
@@ -30,6 +30,9 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.apache.ignite.configuration.ConfigurationTree;
import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
import org.apache.ignite.configuration.validation.Immutable;
import org.apache.ignite.configuration.validation.Max;
import org.apache.ignite.configuration.validation.Min;
@@ -49,16 +52,20 @@ import org.apache.ignite.internal.configuration.validation.MinValidator;
import org.apache.ignite.internal.manager.IgniteComponent;
import org.apache.ignite.lang.IgniteLogger;
+import static java.util.function.Predicate.not;
+import static java.util.stream.Collectors.toSet;
import static org.apache.ignite.internal.configuration.util.ConfigurationNotificationsUtil.notifyListeners;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.checkConfigurationType;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.collectSchemas;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.innerNodeVisitor;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.internalSchemaExtensions;
/** */
public class ConfigurationRegistry implements IgniteComponent {
/** The logger. */
private static final IgniteLogger LOG = IgniteLogger.forClass(ConfigurationRegistry.class);
- /** */
+ /** Generated configuration implementations. Mapping: {@link RootKey#key} -> configuration implementation. */
private final Map<String, DynamicConfiguration<?, ?>> configs = new HashMap<>();
/** Root keys. */
@@ -76,15 +83,33 @@ public class ConfigurationRegistry implements IgniteComponent {
* @param rootKeys Configuration root keys.
* @param validators Validators.
* @param storage Configuration storage.
- * @throws IllegalArgumentException If the configuration type of the root keys is not equal to the storage type.
+ * @param internalSchemaExtensions Internal extensions ({@link InternalConfiguration})
+ * of configuration schemas ({@link ConfigurationRoot} and {@link Config}).
+ * @throws IllegalArgumentException If the configuration type of the root keys is not equal to the storage type,
+ * or if the schema or its extensions are not valid.
*/
public ConfigurationRegistry(
Collection<RootKey<?, ?>> rootKeys,
Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators,
- ConfigurationStorage storage
+ ConfigurationStorage storage,
+ Collection<Class<?>> internalSchemaExtensions
) {
checkConfigurationType(rootKeys, storage);
+ Set<Class<?>> allSchemas = collectSchemas(rootKeys.stream().map(RootKey::schemaClass).collect(toSet()));
+
+ Map<Class<?>, Set<Class<?>>> extensions = internalSchemaExtensions(internalSchemaExtensions);
+
+ if (!allSchemas.containsAll(extensions.keySet())) {
+ Set<Class<?>> notInAllSchemas = extensions.keySet().stream()
+ .filter(not(allSchemas::contains))
+ .collect(toSet());
+
+ throw new IllegalArgumentException(
+ "Internal extensions for which no parent configuration schemes were found: " + notInAllSchemas
+ );
+ }
+
this.rootKeys = rootKeys;
Map<Class<? extends Annotation>, Set<Validator<?, ?>>> validators0 = new HashMap<>(validators);
@@ -99,25 +124,24 @@ public class ConfigurationRegistry implements IgniteComponent {
return cgen.instantiateNode(rootKey.schemaClass());
}
};
- }
- /** {@inheritDoc} */
- @Override public void start() {
rootKeys.forEach(rootKey -> {
- cgen.compileRootSchema(rootKey.schemaClass());
+ cgen.compileRootSchema(rootKey.schemaClass(), extensions);
DynamicConfiguration<?, ?> cfg = cgen.instantiateCfg(rootKey, changer);
configs.put(rootKey.key(), cfg);
});
+ }
+ /** {@inheritDoc} */
+ @Override public void start() {
changer.start();
}
/** {@inheritDoc} */
@Override public void stop() {
- if (changer != null)
- changer.stop();
+ changer.stop();
}
/**
@@ -160,7 +184,7 @@ public class ConfigurationRegistry implements IgniteComponent {
Object node;
try {
- node = ConfigurationUtil.find(path, superRoot);
+ node = ConfigurationUtil.find(path, superRoot, false);
}
catch (KeyNotFoundException e) {
throw new IllegalArgumentException(e.getMessage());
@@ -198,7 +222,7 @@ public class ConfigurationRegistry implements IgniteComponent {
newSuperRoot.traverseChildren(new ConfigurationVisitor<Void>() {
/** {@inheritDoc} */
@Override public Void visitInnerNode(String key, InnerNode newRoot) {
- InnerNode oldRoot = oldSuperRoot.traverseChild(key, innerNodeVisitor());
+ InnerNode oldRoot = oldSuperRoot.traverseChild(key, innerNodeVisitor(), true);
var cfg = (DynamicConfiguration<InnerNode, ?>)configs.get(key);
@@ -209,7 +233,7 @@ public class ConfigurationRegistry implements IgniteComponent {
return null;
}
- });
+ }, true);
// Map futures is only for logging errors.
Function<CompletableFuture<?>, CompletableFuture<?>> mapping = fut -> fut.whenComplete((res, throwable) -> {
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicConfiguration.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicConfiguration.java
index 010f34d..ebe8538 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicConfiguration.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicConfiguration.java
@@ -73,15 +73,18 @@ public abstract class DynamicConfiguration<VIEW, CHANGE> extends ConfigurationNo
assert keys instanceof RandomAccess;
ConfigurationSource src = new ConfigurationSource() {
+ /** Current index in the {@code keys}. */
private int level = 0;
+ /** {@inheritDoc} */
@Override public void descend(ConstructableTreeNode node) {
if (level == keys.size())
change.accept((CHANGE)node);
else
- node.construct(keys.get(level++), this);
+ node.construct(keys.get(level++), this, true);
}
+ /** {@inheritDoc} */
@Override public void reset() {
level = 0;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicProperty.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicProperty.java
index 769c489..3203c56 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicProperty.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/DynamicProperty.java
@@ -60,14 +60,17 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
assert !keys.isEmpty();
ConfigurationSource src = new ConfigurationSource() {
+ /** Current index in the {@code keys}. */
private int level = 0;
+ /** {@inheritDoc} */
@Override public void descend(ConstructableTreeNode node) {
assert level < keys.size();
- node.construct(keys.get(level++), this);
+ node.construct(keys.get(level++), this, true);
}
+ /** {@inheritDoc} */
@Override public <T> T unwrap(Class<T> clazz) {
assert level == keys.size();
@@ -76,6 +79,7 @@ public class DynamicProperty<T extends Serializable> extends ConfigurationNode<T
return clazz.cast(newValue);
}
+ /** {@inheritDoc} */
@Override public void reset() {
level = 0;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/RootInnerNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/RootInnerNode.java
new file mode 100644
index 0000000..2c3ce71
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/RootInnerNode.java
@@ -0,0 +1,71 @@
+/*
+ * 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.internal.configuration;
+
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.internal.configuration.tree.InnerNode;
+
+/**
+ * Holder of root configuration.
+ */
+public class RootInnerNode {
+ /** Root node. */
+ private final InnerNode node;
+
+ /** Internal configuration. */
+ private final boolean internal;
+
+ /**
+ * Constructor.
+ *
+ * @param node Root node.
+ * @param key Root key.
+ */
+ public RootInnerNode(RootKey<?, ?> key, InnerNode node) {
+ this.node = node;
+
+ internal = key.internal();
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public RootInnerNode(RootInnerNode rootInnerNode) {
+ node = rootInnerNode.node.copy();
+ internal = rootInnerNode.internal;
+ }
+
+ /**
+ * Get root node.
+ *
+ * @return Root node.
+ */
+ public InnerNode node() {
+ return node;
+ }
+
+ /**
+ * Check if the root configuration is marked with {@link InternalConfiguration}.
+ *
+ * @return {@code true} if the root configuration is internal.
+ */
+ public boolean internal() {
+ return internal;
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/SuperRoot.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/SuperRoot.java
index 887256f..0fa3d08 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/SuperRoot.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/SuperRoot.java
@@ -17,8 +17,6 @@
package org.apache.ignite.internal.configuration;
-import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.SortedMap;
@@ -28,16 +26,21 @@ import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
import org.apache.ignite.internal.configuration.tree.InnerNode;
+import org.jetbrains.annotations.Nullable;
-/** */
+/**
+ * Holder of root configurations.
+ */
public final class SuperRoot extends InnerNode {
- /** */
- private final SortedMap<String, InnerNode> roots = new TreeMap<>();
+ /** Root configurations. Mapping: {@link RootKey#key} -> configuration. */
+ private final SortedMap<String, RootInnerNode> roots = new TreeMap<>();
- /** */
- private final Function<String, InnerNode> nodeCreator;
+ /** Function that creates root node by root name or returns {@code null} if root name is not found. */
+ private final Function<String, RootInnerNode> nodeCreator;
- /** Copy constructor. */
+ /**
+ * Copy constructor.
+ */
private SuperRoot(SuperRoot superRoot) {
roots.putAll(superRoot.roots);
@@ -45,92 +48,94 @@ public final class SuperRoot extends InnerNode {
}
/**
- * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found
+ * Constructor.
+ *
+ * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found.
*/
- public SuperRoot(Function<String, InnerNode> nodeCreator) {
- this.nodeCreator = nodeCreator;
+ public SuperRoot(Function<String, RootInnerNode> nodeCreator) {
+ this(nodeCreator, Map.of());
}
/**
- * @param nodeCreator Function that creates root node by root name or returns {@code null} if root name is not found
+ * Constructor.
+ *
+ * @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(Function<String, InnerNode> nodeCreator, Map<RootKey<?, ?>, InnerNode> roots) {
+ public SuperRoot(Function<String, RootInnerNode> nodeCreator, Map<RootKey<?, ?>, InnerNode> roots) {
this.nodeCreator = nodeCreator;
for (Map.Entry<RootKey<?, ?>, InnerNode> entry : roots.entrySet())
- this.roots.put(entry.getKey().key(), entry.getValue().copy());
- }
-
- /**
- * @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(Function<String, InnerNode> nodeCreator, List<SuperRoot> superRoots) {
- this(nodeCreator);
-
- for (SuperRoot superRoot : superRoots)
- roots.putAll(superRoot.roots);
+ this.roots.put(entry.getKey().key(), new RootInnerNode(entry.getKey(), entry.getValue().copy()));
}
/**
* Adds a root to the super root.
+ *
* @param rootKey Root key.
* @param root Root node.
*/
public void addRoot(RootKey<?, ?> rootKey, InnerNode root) {
assert !roots.containsKey(rootKey.key()) : rootKey.key() + " : " + roots;
- roots.put(rootKey.key(), root);
+ roots.put(rootKey.key(), new RootInnerNode(rootKey, root));
}
/**
* Gets a root.
+ *
* @param rootKey Root key of the desired root.
* @return Root node.
*/
- public InnerNode getRoot(RootKey<?, ?> rootKey) {
- return roots.get(rootKey.key());
+ @Nullable public InnerNode getRoot(RootKey<?, ?> rootKey) {
+ RootInnerNode root = roots.get(rootKey.key());
+
+ return root == null ? null : root.node();
}
/** {@inheritDoc} */
- @Override public <T> void traverseChildren(ConfigurationVisitor<T> visitor) {
- for (String key : new LinkedHashSet<>(roots.keySet()))
- visitor.visitInnerNode(key, roots.get(key));
+ @Override public <T> void traverseChildren(ConfigurationVisitor<T> visitor, boolean includeInternal) {
+ for (Map.Entry<String, RootInnerNode> e : roots.entrySet()) {
+ if (includeInternal || !e.getValue().internal())
+ visitor.visitInnerNode(e.getKey(), e.getValue().node());
+ }
}
/** {@inheritDoc} */
- @Override public <T> T traverseChild(String key, ConfigurationVisitor<T> visitor) throws NoSuchElementException {
- InnerNode root = roots.get(key);
-
- if (root == null)
+ @Override public <T> T traverseChild(
+ String key,
+ ConfigurationVisitor<T> visitor,
+ boolean includeInternal
+ ) throws NoSuchElementException {
+ RootInnerNode root = roots.get(key);
+
+ if (root == null || (!includeInternal && root.internal()))
throw new NoSuchElementException(key);
-
- return visitor.visitInnerNode(key, root);
+ else
+ return visitor.visitInnerNode(key, root.node());
}
/** {@inheritDoc} */
- @Override public void construct(String key, ConfigurationSource src) throws NoSuchElementException {
- if (src == null) {
- roots.remove(key);
-
- return;
- }
-
- InnerNode root = roots.get(key);
+ @Override public void construct(
+ String key,
+ ConfigurationSource src,
+ boolean includeInternal
+ ) throws NoSuchElementException {
+ RootInnerNode root = roots.get(key);
- if (root == null) {
+ if (root == null)
root = nodeCreator.apply(key);
- if (root == null)
- throw new NoSuchElementException(key);
- }
- else
- root = root.copy();
+ if (root == null || !includeInternal && root.internal())
+ throw new NoSuchElementException(key);
- roots.put(key, root);
+ if (src == null)
+ roots.remove(key);
+ else {
+ roots.put(key, root = new RootInnerNode(root));
- src.descend(root);
+ src.descend(root.node());
+ }
}
/** {@inheritDoc} */
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
index 261ce9c..2479744 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
@@ -26,7 +26,7 @@ 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -40,7 +40,9 @@ import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
import com.facebook.presto.bytecode.ClassDefinition;
import com.facebook.presto.bytecode.ClassGenerator;
import com.facebook.presto.bytecode.FieldDefinition;
@@ -55,8 +57,8 @@ import org.apache.ignite.configuration.NamedConfigurationTree;
import org.apache.ignite.configuration.NamedListView;
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.InternalConfiguration;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.internal.configuration.ConfigurationChanger;
@@ -74,6 +76,7 @@ import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
+import static com.facebook.presto.bytecode.Access.BRIDGE;
import static com.facebook.presto.bytecode.Access.FINAL;
import static com.facebook.presto.bytecode.Access.PRIVATE;
import static com.facebook.presto.bytecode.Access.PUBLIC;
@@ -91,8 +94,19 @@ import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invoke
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.Arrays.asList;
import static java.util.Collections.singleton;
import static java.util.EnumSet.of;
+import static java.util.stream.Collectors.toList;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.changeClassName;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.configurationClassName;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.viewClassName;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.extensionsFields;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isNamedConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isValue;
+import static org.apache.ignite.internal.util.CollectionUtils.concat;
+import static org.apache.ignite.internal.util.CollectionUtils.union;
import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
import static org.objectweb.asm.Type.getMethodDescriptor;
import static org.objectweb.asm.Type.getMethodType;
@@ -241,10 +255,17 @@ public class ConfigurationAsmGenerator {
}
/**
- * Generates, defines, loads and initializes all dynamic classes required for the given Configuration Schema.
- * @param rootSchemaClass Class of the root Configuration Schema.
+ * Generates, defines, loads and initializes all dynamic classes required for the given configuration schema.
+ *
+ * @param rootSchemaClass Class of the root configuration schema.
+ * @param internalSchemaExtensions Internal extensions ({@link InternalConfiguration})
+ * of configuration schemas ({@link ConfigurationRoot} and {@link Config}).
+ * Mapping: original schema -> extensions.
*/
- public synchronized void compileRootSchema(Class<?> rootSchemaClass) {
+ public synchronized void compileRootSchema(
+ Class<?> rootSchemaClass,
+ Map<Class<?>, Set<Class<?>>> internalSchemaExtensions
+ ) {
if (schemasInfo.containsKey(rootSchemaClass))
return; // Already compiled.
@@ -263,11 +284,16 @@ public class ConfigurationAsmGenerator {
|| schemaClass.isAnnotationPresent(Config.class)
: schemaClass + " is not properly annotated";
- assert schemasInfo.containsKey(schemaClass);
+ assert schemasInfo.containsKey(schemaClass) : schemaClass;
+
+ Field[] schemaFields = schemaClass.getDeclaredFields();
+
+ Set<Class<?>> schemaExtensions = internalSchemaExtensions.getOrDefault(schemaClass, Set.of());
+ Set<Field> extensionsFields = extensionsFields(schemaExtensions);
- for (Field field : schemaClass.getDeclaredFields()) {
- if (isConfigValue(field) || isNamedConfigValue(field)) {
- Class<?> subSchemaClass = field.getType();
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
+ if (isConfigValue(schemaField) || isNamedConfigValue(schemaField)) {
+ Class<?> subSchemaClass = schemaField.getType();
if (!schemasInfo.containsKey(subSchemaClass)) {
compileQueue.offer(subSchemaClass);
@@ -277,8 +303,8 @@ public class ConfigurationAsmGenerator {
}
schemas.add(schemaClass);
- definitions.add(createNodeClass(schemaClass));
- definitions.add(createCfgImplClass(schemaClass));
+ definitions.add(createNodeClass(schemaClass, schemaExtensions, schemaFields, extensionsFields));
+ definitions.add(createCfgImplClass(schemaClass, schemaExtensions, schemaFields, extensionsFields));
}
Map<String, Class<?>> definedClasses = generator.defineClasses(definitions);
@@ -291,7 +317,21 @@ public class ConfigurationAsmGenerator {
}
}
- private ClassDefinition createNodeClass(Class<?> schemaClass) {
+ /**
+ * Construct a {@link InnerNode} definition for a configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
+ * @return Constructed {@link InnerNode} definition for the configuration schema.
+ */
+ private ClassDefinition createNodeClass(
+ Class<?> schemaClass,
+ Set<Class<?>> schemaExtensions,
+ Field[] schemaFields,
+ Set<Field> extensionsFields
+ ) {
SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
// Node class definition.
@@ -299,83 +339,61 @@ public class ConfigurationAsmGenerator {
of(PUBLIC, FINAL),
internalName(schemaClassInfo.nodeClassName),
type(InnerNode.class),
- typeFromJavaClassName(schemaClassInfo.viewClassName),
- typeFromJavaClassName(schemaClassInfo.changeClassName)
+ nodeClassInterfaces(schemaClass, schemaExtensions)
);
- // Spec field.
- FieldDefinition specField = classDef.declareField(of(PRIVATE, FINAL), "_spec", schemaClass);
+ // Spec fields.
+ Map<Class<?>, FieldDefinition> specFields = new HashMap<>();
- // org.apache.ignite.internal.configuration.tree.InnerNode#schemaType
- addNodeSchemaTypeMethod(classDef, specField);
+ int i = 0;
- // Cached array of reflected fields. Helps to avoid unnecessary clonings.
- Field[] schemaFields = schemaClass.getDeclaredFields();
+ for (Class<?> clazz : concat(List.of(schemaClass), schemaExtensions))
+ specFields.put(clazz, classDef.declareField(of(PRIVATE, FINAL), "_spec" + i++, clazz));
+
+ // org.apache.ignite.internal.configuration.tree.InnerNode#schemaType
+ addNodeSchemaTypeMethod(classDef, specFields.get(schemaClass));
// Define the rest of the fields.
Map<String, FieldDefinition> fieldDefs = new HashMap<>();
- for (Field schemaField : schemaFields) {
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
assert isValue(schemaField) || isConfigValue(schemaField) || isNamedConfigValue(schemaField) : schemaField;
fieldDefs.put(schemaField.getName(), addNodeField(classDef, schemaField));
}
// Constructor.
- addNodeConstructor(classDef, schemaClass, specField, schemaFields, fieldDefs);
+ addNodeConstructor(classDef, specFields, fieldDefs, schemaFields, extensionsFields);
// VIEW and CHANGE methods.
- for (Field schemaField : schemaFields) {
- FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
+ String fieldName = schemaField.getName();
+
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
addNodeViewMethod(classDef, schemaField, fieldDef);
- addNodeChangeMethod(classDef, schemaField, fieldDef, schemaClassInfo);
+ // Add change methods.
+ MethodDefinition changeMtd = addNodeChangeMethod(classDef, schemaField, fieldDef, schemaClassInfo.nodeClassName);
+ addNodeChangeBridgeMethod(classDef, changeClassName(schemaField.getDeclaringClass()), changeMtd);
}
// traverseChildren
- addNodeTraverseChildrenMethod(classDef, schemaFields, fieldDefs);
+ addNodeTraverseChildrenMethod(classDef, fieldDefs, schemaFields, extensionsFields);
// traverseChild
- addNodeTraverseChildMethod(classDef, schemaFields, fieldDefs);
+ addNodeTraverseChildMethod(classDef, fieldDefs, schemaFields, extensionsFields);
// construct
- addNodeConstructMethod(classDef, schemaFields, fieldDefs);
+ addNodeConstructMethod(classDef, fieldDefs, schemaFields, extensionsFields);
// constructDefault
- addNodeConstructDefaultMethod(classDef, specField, schemaFields, fieldDefs);
+ addNodeConstructDefaultMethod(classDef, specFields, fieldDefs, schemaFields, extensionsFields);
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() {
@@ -431,28 +449,31 @@ public class ConfigurationAsmGenerator {
/**
* 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 specFields Definition of fields for the {@code _spec#} fields of the node class.
+ * Mapping: configuration schema class -> {@code _spec#} field.
* @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addNodeConstructor(
ClassDefinition classDef,
- Class<?> schemaClass,
- FieldDefinition specField,
+ Map<Class<?>, FieldDefinition> specFields,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
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)));
+ // this._spec# = new MyConfigurationSchema();
+ for (Map.Entry<Class<?>, FieldDefinition> e : specFields.entrySet())
+ ctor.getBody().append(ctor.getThis().setField(e.getValue(), newInstance(e.getKey())));
- for (Field schemaField : schemaFields) {
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
if (!isNamedConfigValue(schemaField))
continue;
@@ -480,7 +501,7 @@ public class ConfigurationAsmGenerator {
* depending on type.
* @param classDef Node class definition.
* @param schemaField Configuration Schema class field.
- * @param fieldDefs Field definition.
+ * @param fieldDef Field definition.
*/
private void addNodeViewMethod(
ClassDefinition classDef,
@@ -529,23 +550,25 @@ public class ConfigurationAsmGenerator {
/**
* 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.
+ * @param nodeClassName Class name for the Node class.
+ * @return Definition of change method.
*/
- private void addNodeChangeMethod(
+ private MethodDefinition addNodeChangeMethod(
ClassDefinition classDef,
Field schemaField,
FieldDefinition fieldDef,
- SchemaClassesInfo schemaClassInfo
+ String nodeClassName
) {
Class<?> schemaFieldType = schemaField.getType();
MethodDefinition changeMtd = classDef.declareMethod(
of(PUBLIC),
"change" + capitalize(schemaField.getName()),
- typeFromJavaClassName(schemaClassInfo.changeClassName),
+ typeFromJavaClassName(nodeClassName),
// Change argument type is a Consumer for all inner or named fields.
arg("change", isValue(schemaField) ? type(schemaFieldType) : type(Consumer.class))
);
@@ -585,86 +608,123 @@ public class ConfigurationAsmGenerator {
// return this;
changeBody.append(changeMtd.getThis()).retObject();
+
+ return changeMtd;
}
/**
- * Implements {@link InnerNode#traverseChildren(ConfigurationVisitor)} method.
+ * Implements changer bridge method from {@code CHANGE} interface.
+ *
+ * @param classDef Node class definition.
+ * @param changeClassName Class name for the CHANGE class.
+ * @param changeMtd Definition of change method.
+ */
+ private void addNodeChangeBridgeMethod(
+ ClassDefinition classDef,
+ String changeClassName,
+ MethodDefinition changeMtd
+ ) {
+ MethodDefinition bridgeMtd = classDef.declareMethod(
+ of(PUBLIC, SYNTHETIC, BRIDGE),
+ changeMtd.getName(),
+ typeFromJavaClassName(changeClassName),
+ changeMtd.getParameters()
+ );
+
+ Variable changeVar = bridgeMtd.getScope().getVariable("change");
+
+ // this.change*(change);
+ BytecodeExpression invokeChangeMtd = bridgeMtd.getThis().invoke(changeMtd, List.of(changeVar));
+
+ // return this.change*(change);
+ bridgeMtd.getBody().append(invokeChangeMtd).retObject();
+ }
+
+ /**
+ * Implements {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} method.
+ *
* @param classDef Class definition.
- * @param schemaFields Array of all schema fields that represent configurations.
* @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addNodeTraverseChildrenMethod(
ClassDefinition classDef,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
MethodDefinition traverseChildrenMtd = classDef.declareMethod(
of(PUBLIC),
"traverseChildren",
type(void.class),
- arg("visitor", type(ConfigurationVisitor.class))
+ arg("visitor", type(ConfigurationVisitor.class)),
+ arg("includeInternal", type(boolean.class))
).addException(NoSuchElementException.class);
BytecodeBlock mtdBody = traverseChildrenMtd.getBody();
- for (Field schemaField : schemaFields) {
- FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
+ invokeVisitForTraverseChildren(asList(schemaFields), fieldDefs, traverseChildrenMtd)
+ .forEach(mtdBody::append);
+
+ if (!extensionsFields.isEmpty()) {
+ Variable includeInternalVar = traverseChildrenMtd.getScope().getVariable("includeInternal");
+
+ BytecodeBlock includeInternalBlock = new BytecodeBlock();
- // Visit every field. Returned value isn't used so we pop it off the stack.
- mtdBody.append(invokeVisit(traverseChildrenMtd, schemaField, fieldDef).pop());
+ invokeVisitForTraverseChildren(extensionsFields, fieldDefs, traverseChildrenMtd)
+ .forEach(includeInternalBlock::append);
+
+ mtdBody.append(new IfStatement().condition(includeInternalVar).ifTrue(includeInternalBlock));
}
mtdBody.ret();
}
/**
- * Implements {@link InnerNode#traverseChild(String, ConfigurationVisitor)} method.
+ * Implements {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} method.
+ *
* @param classDef Class definition.
- * @param schemaFields Array of all schema fields that represent configurations.
* @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addNodeTraverseChildMethod(
ClassDefinition classDef,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
MethodDefinition traverseChildMtd = classDef.declareMethod(
of(PUBLIC),
"traverseChild",
type(Object.class),
arg("key", type(String.class)),
- arg("visitor", type(ConfigurationVisitor.class))
+ arg("visitor", type(ConfigurationVisitor.class)),
+ arg("includeInternal", type(boolean.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();
+ BytecodeBlock mtdBody = traverseChildMtd.getBody();
- FieldDefinition fieldDef = fieldDefs.get(fieldName);
+ if (extensionsFields.isEmpty())
+ mtdBody.append(invokeVisitForTraverseChild(asList(schemaFields), fieldDefs, traverseChildMtd));
+ else {
+ Variable includeInternalVar = traverseChildMtd.getScope().getVariable("includeInternal");
- // Visit result should be immediately returned.
- switchBuilder.addCase(fieldName, invokeVisit(traverseChildMtd, schemaField, fieldDef).retObject());
+ mtdBody.append(
+ new IfStatement()
+ .condition(includeInternalVar)
+ .ifTrue(invokeVisitForTraverseChild(union(extensionsFields, schemaFields), fieldDefs, traverseChildMtd))
+ .ifFalse(invokeVisitForTraverseChild(asList(schemaFields), fieldDefs, traverseChildMtd))
+ );
}
-
- // 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 mtd Method definition, either {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} or
+ * {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} 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.
@@ -687,104 +747,60 @@ public class ConfigurationAsmGenerator {
}
/**
- * Implements {@link ConstructableTreeNode#construct(String, ConfigurationSource)} method.
+ * Implements {@link ConstructableTreeNode#construct(String, ConfigurationSource, boolean)} method.
+ *
* @param classDef Class definition.
- * @param schemaFields Array of all schema fields that represent configurations.
* @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addNodeConstructMethod(
ClassDefinition classDef,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
MethodDefinition constructMtd = classDef.declareMethod(
of(PUBLIC),
"construct",
type(void.class),
- arg("key", String.class),
- arg("src", ConfigurationSource.class)
+ arg("key", type(String.class)),
+ arg("src", type(ConfigurationSource.class)),
+ arg("includeInternal", type(boolean.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(copyNodeField(constructMtd, fieldDef))
- .append(srcVar.invoke(DESCEND, constructMtd.getThis().getField(fieldDef)))
- )
- );
- }
- // this.field = src == null ? new NamedListNode<>(key, ValueNode::new) : src.descend(field = field.copy()));
- else {
- NamedConfigValue namedCfgAnnotation = schemaField.getAnnotation(NamedConfigValue.class);
-
- String fieldNodeClassName = schemasInfo.get(schemaField.getType()).nodeClassName;
+ BytecodeBlock mtdBody = constructMtd.getBody();
- caseClause.append(new IfStatement()
- .condition(isNull(srcVar))
- .ifTrue(constructMtd.getThis().setField(
- fieldDef,
- newInstance(
- NamedListNode.class,
- constantString(namedCfgAnnotation.syntheticKeyName()),
- 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)))
- )
- );
- }
+ if (extensionsFields.isEmpty())
+ mtdBody.append(treatSourceForConstruct(asList(schemaFields), fieldDefs, constructMtd)).ret();
+ else {
+ Variable includeInternalVar = constructMtd.getScope().getVariable("includeInternal");
+
+ mtdBody.append(
+ new IfStatement()
+ .condition(includeInternalVar)
+ .ifTrue(treatSourceForConstruct(union(extensionsFields, schemaFields), fieldDefs, constructMtd))
+ .ifFalse(treatSourceForConstruct(asList(schemaFields), fieldDefs, constructMtd))
+ ).ret();
}
-
- // 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 specFields Definition of fields for the {@code _spec#} fields of the node class.
+ * Mapping: configuration schema class -> {@code _spec#} field.
* @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addNodeConstructDefaultMethod(
ClassDefinition classDef,
- FieldDefinition specField,
+ Map<Class<?>, FieldDefinition> specFields,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
MethodDefinition constructDfltMtd = classDef.declareMethod(
of(PUBLIC),
@@ -798,7 +814,7 @@ public class ConfigurationAsmGenerator {
StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructDfltMtd.getScope())
.expression(keyVar);
- for (Field schemaField : schemaFields) {
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
if (!isValue(schemaField))
continue;
@@ -812,7 +828,8 @@ public class ConfigurationAsmGenerator {
Class<?> schemaFieldType = schemaField.getType();
- // defaultValue = _spec.field;
+ // defaultValue = _spec#.field;
+ FieldDefinition specField = specFields.get(schemaField.getDeclaringClass());
BytecodeExpression defaultValue = constructDfltMtd.getThis().getField(specField).getField(schemaField);
// defaultValue = Box.valueOf(defaultValue); // Boxing.
@@ -867,7 +884,7 @@ public class ConfigurationAsmGenerator {
@NotNull private BytecodeExpression newNamedListElementLambda(String nodeClassName) {
return invokeDynamic(
LAMBDA_METAFACTORY,
- Arrays.asList(
+ asList(
getMethodType(getType(Object.class)),
new Handle(
H_NEWINVOKESPECIAL,
@@ -883,7 +900,21 @@ public class ConfigurationAsmGenerator {
);
}
- private ClassDefinition createCfgImplClass(Class<?> schemaClass) {
+ /**
+ * Construct a {@link DynamicConfiguration} definition for a configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
+ * @return Constructed {@link DynamicConfiguration} definition for the configuration schema.
+ */
+ private ClassDefinition createCfgImplClass(
+ Class<?> schemaClass,
+ Set<Class<?>> schemaExtensions,
+ Field[] schemaFields,
+ Set<Field> extensionsFields
+ ) {
SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
// Configuration impl class definition.
@@ -891,24 +922,20 @@ public class ConfigurationAsmGenerator {
of(PUBLIC, FINAL),
internalName(schemaClassInfo.cfgImplClassName),
type(DynamicConfiguration.class),
- typeFromJavaClassName(schemaClassInfo.cfgClassName)
+ configClassInterfaces(schemaClass, schemaExtensions)
);
- // 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)
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields))
fieldDefs.put(schemaField.getName(), addConfigurationImplField(classDef, schemaField));
// Constructor
- addConfigurationImplConstructor(classDef, schemaClassInfo, schemaFields, fieldDefs);
+ addConfigurationImplConstructor(classDef, schemaClassInfo, fieldDefs, schemaFields, extensionsFields);
- for (Field schemaField : schemaFields) {
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields))
addConfigurationImplGetMethod(classDef, schemaClass, fieldDefs, schemaField);
- }
return classDef;
}
@@ -951,14 +978,16 @@ public class ConfigurationAsmGenerator {
* 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.
+ * @param schemaFields Fields of the schema class.
+ * @param extensionsFields Fields of internal extensions of the configuration schema.
*/
private void addConfigurationImplConstructor(
ClassDefinition classDef,
SchemaClassesInfo schemaClassInfo,
+ Map<String, FieldDefinition> fieldDefs,
Field[] schemaFields,
- Map<String, FieldDefinition> fieldDefs
+ Set<Field> extensionsFields
) {
MethodDefinition ctor = classDef.declareConstructor(
of(PUBLIC),
@@ -977,7 +1006,7 @@ public class ConfigurationAsmGenerator {
.invokeConstructor(DYNAMIC_CONFIGURATION_CTOR);
int newIdx = 0;
- for (Field schemaField : schemaFields) {
+ for (Field schemaField : concat(asList(schemaFields), extensionsFields)) {
FieldDefinition fieldDef = fieldDefs.get(schemaField.getName());
BytecodeExpression newValue;
@@ -1031,7 +1060,7 @@ public class ConfigurationAsmGenerator {
ctor.getScope().getVariable("changer"),
invokeDynamic(
LAMBDA_METAFACTORY,
- Arrays.asList(
+ asList(
getMethodType(getType(Object.class), getType(Object.class), getType(Object.class)),
new Handle(
Opcodes.H_INVOKESTATIC,
@@ -1149,4 +1178,174 @@ public class ConfigurationAsmGenerator {
return boxed == null ? clazz : boxed;
}
+
+ /**
+ * Create bytecode blocks that invokes of {@link ConfigurationVisitor}'s methods for
+ * {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)}.
+ *
+ * @param schemaFields Fields of the schema.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param traverseChildrenMtd Method definition {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)}
+ * defined in {@code *Node} class.
+ * @return Created bytecode blocks that invokes of {@link ConfigurationVisitor}'s methods for fields.
+ */
+ private Collection<BytecodeNode> invokeVisitForTraverseChildren(
+ Collection<Field> schemaFields,
+ Map<String, FieldDefinition> fieldDefs,
+ MethodDefinition traverseChildrenMtd
+ ) {
+ if (schemaFields.isEmpty())
+ return List.of();
+ else {
+ return schemaFields.stream()
+ .map(field -> invokeVisit(traverseChildrenMtd, field, fieldDefs.get(field.getName())).pop())
+ .collect(toList());
+ }
+ }
+
+ /**
+ * Created switch bytecode block that invokes of {@link ConfigurationVisitor}'s methods for
+ * {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)}.
+ *
+ * @param schemaFields Fields of the schema.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param traverseChildMtd Method definition {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)}}
+ * defined in {@code *Node} class.
+ * @return Created switch bytecode block that invokes of {@link ConfigurationVisitor}'s methods for fields.
+ */
+ private BytecodeNode invokeVisitForTraverseChild(
+ Collection<Field> schemaFields,
+ Map<String, FieldDefinition> fieldDefs,
+ MethodDefinition traverseChildMtd
+ ) {
+ Variable keyVar = traverseChildMtd.getScope().getVariable("key");
+
+ 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()
+ );
+
+ return switchBuilder.build();
+ }
+
+ /**
+ * Created switch bytecode block that invokes of construct methods for
+ * {@link InnerNode#construct(String, ConfigurationSource, boolean)}.
+ *
+ * @param schemaFields Fields of the schema.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param constructMtd Method definition {@link InnerNode#construct(String, ConfigurationSource, boolean)}
+ * defined in {@code *Node} class.
+ * @return Created switch bytecode block that invokes of construct methods for fields.
+ */
+ private BytecodeNode treatSourceForConstruct(
+ Collection<Field> schemaFields,
+ Map<String, FieldDefinition> fieldDefs,
+ MethodDefinition constructMtd
+ ) {
+ 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(copyNodeField(constructMtd, fieldDef))
+ .append(srcVar.invoke(DESCEND, constructMtd.getThis().getField(fieldDef)))
+ )
+ );
+ }
+ // this.field = src == null ? new NamedListNode<>(key, ValueNode::new) : src.descend(field = field.copy()));
+ else {
+ NamedConfigValue namedCfgAnnotation = schemaField.getAnnotation(NamedConfigValue.class);
+
+ String fieldNodeClassName = schemasInfo.get(schemaField.getType()).nodeClassName;
+
+ caseClause.append(new IfStatement()
+ .condition(isNull(srcVar))
+ .ifTrue(constructMtd.getThis().setField(
+ fieldDef,
+ newInstance(
+ NamedListNode.class,
+ constantString(namedCfgAnnotation.syntheticKeyName()),
+ 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()
+ );
+
+ return switchBuilder.build();
+ }
+
+ /**
+ * Get interfaces for {@link InnerNode} definition for a configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link InnerNode} definition for a configuration schema.
+ */
+ private static ParameterizedType[] nodeClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
+ return Stream.concat(Stream.of(schemaClass), schemaExtensions.stream())
+ .flatMap(cls -> Stream.of(viewClassName(cls), changeClassName(cls)))
+ .map(ParameterizedType::typeFromJavaClassName)
+ .toArray(ParameterizedType[]::new);
+ }
+
+ /**
+ * Get interfaces for {@link DynamicConfiguration} definition for a configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link DynamicConfiguration} definition for a configuration schema.
+ */
+ private static ParameterizedType[] configClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
+ return Stream.concat(Stream.of(schemaClass), schemaExtensions.stream())
+ .map(cls -> typeFromJavaClassName(configurationClassName(cls)))
+ .toArray(ParameterizedType[]::new);
+ }
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/SchemaClassesInfo.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/SchemaClassesInfo.java
index 0a842e3..3568e47 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/SchemaClassesInfo.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/SchemaClassesInfo.java
@@ -24,6 +24,15 @@ import org.apache.ignite.internal.configuration.tree.InnerNode;
* Class to cache compiled classes and hold precalculated names to reference other existing classes.
*/
class SchemaClassesInfo {
+ /** Configuration class name postfix. */
+ private static final String CONFIGURATION_CLASS_POSTFIX = "Configuration";
+
+ /** View class name postfix. */
+ private static final String VIEW_CLASS_POSTFIX = "View";
+
+ /** Change class name postfix. */
+ private static final String CHANGE_CLASS_POSTFIX = "Change";
+
/** Configuration Schema class. */
public final Class<?> schemaClass;
@@ -49,19 +58,64 @@ class SchemaClassesInfo {
public Class<? extends DynamicConfiguration<?, ?>> cfgImplClass;
/**
+ * Constructor.
+ *
* @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$", "");
+ String prefix = prefix(schemaClass);
- viewClassName = prefix + "View";
- changeClassName = prefix + "Change";
- cfgClassName = prefix + "Configuration";
+ viewClassName = prefix + VIEW_CLASS_POSTFIX;
+ changeClassName = prefix + CHANGE_CLASS_POSTFIX;
+ cfgClassName = prefix + CONFIGURATION_CLASS_POSTFIX;
nodeClassName = prefix + "Node";
cfgImplClassName = prefix + "ConfigurationImpl";
}
+
+ /**
+ * Get the prefix for inner classes.
+ * <p/>
+ * Example: org.apache.ignite.NodeConfigurationSchema -> org.apache.ignite.Node
+ *
+ * @param schemaClass Configuration schema class.
+ * @return Prefix for inner classes.
+ */
+ static String prefix(Class<?> schemaClass) {
+ String schemaClassName = schemaClass.getPackageName() + "." + schemaClass.getSimpleName();
+
+ return schemaClassName.replaceAll("ConfigurationSchema$", "");
+ }
+
+ /**
+ * Get class name for the VIEW class.
+ *
+ * @param schemaClass Configuration schema class.
+ * @return Class name for the VIEW class.
+ */
+ static String viewClassName(Class<?> schemaClass) {
+ return prefix(schemaClass) + VIEW_CLASS_POSTFIX;
+ }
+
+ /**
+ * Get class name for the CHANGE class.
+ *
+ * @param schemaClass Configuration schema class.
+ * @return Class name for the CHANGE class.
+ */
+ static String changeClassName(Class<?> schemaClass) {
+ return prefix(schemaClass) + CHANGE_CLASS_POSTFIX;
+ }
+
+ /**
+ * Get class name for the Configuration class.
+ *
+ * @param schemaClass Configuration schema class.
+ * @return Class name for the Configuration class.
+ */
+ static String configurationClassName(Class<?> schemaClass) {
+ return prefix(schemaClass) + CONFIGURATION_CLASS_POSTFIX;
+ }
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConverter.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConverter.java
index 5ce3bd2..c10995e 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConverter.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConverter.java
@@ -23,6 +23,7 @@ import com.typesafe.config.ConfigValue;
import com.typesafe.config.impl.ConfigImpl;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
+import org.apache.ignite.internal.configuration.tree.ConverterToMapVisitor;
import org.jetbrains.annotations.NotNull;
public class HoconConverter {
@@ -38,7 +39,7 @@ public class HoconConverter {
ConfigurationRegistry registry,
@NotNull List<String> path
) throws IllegalArgumentException {
- Object res = registry.represent(path, new HoconConfigurationVisitor());
+ Object res = registry.represent(path, new ConverterToMapVisitor(false));
return ConfigImpl.fromAnyRef(res, null);
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconListConfigurationSource.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconListConfigurationSource.java
index 0deb215..00a93c8 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconListConfigurationSource.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconListConfigurationSource.java
@@ -131,7 +131,7 @@ class HoconListConfigurationSource implements ConfigurationSource {
List<String> path = appendKey(this.path, syntheticKeyName);
- node.construct(key, new HoconObjectConfigurationSource(syntheticKeyName, path, hoconCfg));
+ node.construct(key, new HoconObjectConfigurationSource(syntheticKeyName, path, hoconCfg), false);
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconObjectConfigurationSource.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconObjectConfigurationSource.java
index ce6f14c..ae8359c 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconObjectConfigurationSource.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconObjectConfigurationSource.java
@@ -83,14 +83,18 @@ class HoconObjectConfigurationSource implements ConfigurationSource {
try {
switch (hoconCfgValue.valueType()) {
case NULL:
- node.construct(key, null);
+ node.construct(key, null, false);
break;
case OBJECT: {
List<String> path = appendKey(this.path, key);
- node.construct(key, new HoconObjectConfigurationSource(null, path, (ConfigObject)hoconCfgValue));
+ node.construct(
+ key,
+ new HoconObjectConfigurationSource(null, path, (ConfigObject)hoconCfgValue),
+ false
+ );
break;
}
@@ -98,7 +102,7 @@ class HoconObjectConfigurationSource implements ConfigurationSource {
case LIST: {
List<String> path = appendKey(this.path, key);
- node.construct(key, new HoconListConfigurationSource(path, (ConfigList)hoconCfgValue));
+ node.construct(key, new HoconListConfigurationSource(path, (ConfigList)hoconCfgValue), false);
break;
}
@@ -106,7 +110,7 @@ class HoconObjectConfigurationSource implements ConfigurationSource {
default: {
List<String> path = appendKey(this.path, key);
- node.construct(key, new HoconPrimitiveConfigurationSource(path, hoconCfgValue));
+ node.construct(key, new HoconPrimitiveConfigurationSource(path, hoconCfgValue), false);
}
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNode.java
index cd59073..aa68f1e 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConstructableTreeNode.java
@@ -27,9 +27,10 @@ public interface ConstructableTreeNode {
*
* @param key Field / named list element name to be constructed.
* @param src Source that provides data for construction.
+ * @param includeInternal Include internal configuration nodes (private configuration extensions).
* @throws NoSuchElementException If {@code key} cannot be constructed.
*/
- void construct(String key, ConfigurationSource src) throws NoSuchElementException;
+ void construct(String key, ConfigurationSource src, boolean includeInternal) 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/internal/configuration/hocon/HoconConfigurationVisitor.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConverterToMapVisitor.java
similarity index 84%
rename from modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConfigurationVisitor.java
rename to modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConverterToMapVisitor.java
index 7485792..5c45c64 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/hocon/HoconConfigurationVisitor.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/ConverterToMapVisitor.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.configuration.hocon;
+package org.apache.ignite.internal.configuration.tree;
import java.io.Serializable;
import java.lang.reflect.Array;
@@ -29,18 +29,26 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
-import com.typesafe.config.ConfigValue;
-import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
-import org.apache.ignite.internal.configuration.tree.InnerNode;
-import org.apache.ignite.internal.configuration.tree.NamedListNode;
/**
* {@link ConfigurationVisitor} implementation that converts a configuration tree to a combination of {@link Map} and
- * {@link List} objects to convert the result into HOCON {@link ConfigValue} instance.
+ * {@link List} objects.
*/
-class HoconConfigurationVisitor implements ConfigurationVisitor<Object> {
+public class ConverterToMapVisitor implements ConfigurationVisitor<Object> {
+ /** Include internal configuration nodes (private configuration extensions). */
+ private final boolean includeInternal;
+
/** Stack with intermediate results. Used to store values during recursive calls. */
- private Deque<Object> deque = new ArrayDeque<>();
+ private final Deque<Object> deque = new ArrayDeque<>();
+
+ /**
+ * Constructor.
+ *
+ * @param includeInternal Include internal configuration nodes (private configuration extensions).
+ */
+ public ConverterToMapVisitor(boolean includeInternal) {
+ this.includeInternal = includeInternal;
+ }
/** {@inheritDoc} */
@Override public Object visitLeafNode(String key, Serializable val) {
@@ -62,7 +70,7 @@ class HoconConfigurationVisitor implements ConfigurationVisitor<Object> {
deque.push(innerMap);
- node.traverseChildren(this);
+ node.traverseChildren(this, includeInternal);
deque.pop();
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
index 858fbf9..058112b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/InnerNode.java
@@ -29,89 +29,127 @@ public abstract class InnerNode implements TraversableTreeNode, ConstructableTre
/**
* Method with auto-generated implementation. Must look like this:
* <pre><code>
- * {@literal @}Override public void traverseChildren(ConfigurationVisitor visitor) {
+ * {@literal @}Override public void traverseChildren(ConfigurationVisitor visitor, boolean includeInternal) {
* visitor.visitInnerNode("pojoField1", this.pojoField1);
*
* visitor.visitNamedListNode("pojoField2", this.pojoField2);
*
* visitor.visitLeafNode("primitiveField1", this.primitiveField1);
*
- * visitor.visitLeafNode("primitiveField2", this.primitiveField2);
+ * if (includeInternal) {
+ * visitor.visitLeafNode("primitiveField2", this.primitiveField2);
+ * }
* }
* </code></pre>
*
* Order of fields must be the same as they are described in configuration schema.
*
* @param visitor Configuration visitor.
+ * @param includeInternal Include internal configuration nodes (private configuration extensions).
* @param <T> Parameter type of the passed visitor.
*/
- public abstract <T> void traverseChildren(ConfigurationVisitor<T> visitor);
+ public abstract <T> void traverseChildren(ConfigurationVisitor<T> visitor, boolean includeInternal);
/**
* Method with auto-generated implementation. Must look like this:
* <pre><code>
- * {@literal @}Override public void traverseChild(String key, ConfigurationVisitor visitor) throws NoSuchElementException {
- * switch (key) {
- * case "pojoField1":
- * visitor.visitInnerNode("pojoField1", this.pojoField1);
- * break;
+ * {@literal @}Override public void traverseChild(String key, ConfigurationVisitor visitor, boolean includeInternal) throws NoSuchElementException {
+ * if (boolean includeInternal) {
+ * switch (key) {
+ * case "pojoField1":
+ * visitor.visitInnerNode("pojoField1", this.pojoField1);
+ * break;
*
- * case "pojoField2":
- * visitor.visitNamedListNode("pojoField2", this.pojoField2);
- * break;
+ * case "pojoField2":
+ * visitor.visitNamedListNode("pojoField2", this.pojoField2);
+ * break;
*
- * case "primitiveField1":
- * visitor.visitLeafNode("primitiveField1", this.primitiveField1);
- * break;
+ * case "primitiveField1":
+ * visitor.visitLeafNode("primitiveField1", this.primitiveField1);
+ * break;
*
- * case "primitiveField2":
- * visitor.visitLeafNode("primitiveField2", this.primitiveField2);
- * break;
+ * case "primitiveField2":
+ * visitor.visitLeafNode("primitiveField2", this.primitiveField2);
+ * break;
*
- * default:
- * throw new NoSuchElementException(key);
+ * default:
+ * throw new NoSuchElementException(key);
+ * }
+ * }
+ * else {
+ * switch (key) {
+ * case "primitiveField2":
+ * visitor.visitLeafNode("primitiveField2", this.primitiveField2);
+ * break;
+ * default:
+ * throw new NoSuchElementException(key);
+ * }
* }
* }
* </code></pre>
*
* @param key Name of the child.
* @param visitor Configuration visitor.
+ * @param includeInternal Include internal configuration nodes (private configuration extensions).
* @param <T> Parameter type of passed visitor.
* @return Whatever {@code visitor} returned.
* @throws NoSuchElementException If field {@code key} is not found.
*/
- public abstract <T> T traverseChild(String key, ConfigurationVisitor<T> visitor) throws NoSuchElementException;
+ public abstract <T> T traverseChild(
+ String key,
+ ConfigurationVisitor<T> visitor,
+ boolean includeInternal
+ ) throws NoSuchElementException;
/**
* Method with auto-generated implementation. Must look like this:
* <pre><code>
- * {@literal @}Override public abstract void construct(String key, ConfigurationSource src) throws NoSuchElementException {
+ * {@literal @}Override public abstract void construct(String key, ConfigurationSource src, boolean includeInternal) throws NoSuchElementException {
+ * if (includeInternal) {
+ * switch (key) {
+ * case "namedList":
+ * if (src == null)
+ * namedList = new NamedListNode<>(Foo::new);
+ * else
+ * src.descend(namedList = namedList.copy());
+ * break;
+ *
+ * case "innerNode":
+ * if (src == null)
+ * innerNode = null;
+ * else
+ * src.descend(innerNode = (innerNode == null ? new Bar() : (Bar)innerNode.copy()));
+ * break;
+ *
+ * case "leafInt":
+ * leafInt = src == null ? null : src.unwrap(Integer.class);
+ * break;
+ *
+ * case "leafStr":
+ * leafStr = src == null ? null : src.unwrap(String.class);
+ * break;
+ *
+ * default: throw new NoSuchElementException(key);
+ * }
+ * }
* switch (key) {
- * case "namedList":
- * if (src == null)
- * namedList = new NamedListNode<>(Foo::new);
- * else
- * src.descend(namedList = namedList.copy());
- * break;
- *
- * case "innerNode":
- * if (src == null)
- * innerNode = null;
- * else
- * src.descend(innerNode = (innerNode == null ? new Bar() : (Bar)innerNode.copy()));
- * break;
- *
- * case "leaf":
- * leaf = src == null ? null : src.unwrap(Integer.class);
- * break;
- *
- * default: throw new NoSuchElementException(key);
+ * switch (key) {
+ * case "leafStr":
+ * leafStr = src == null ? null : src.unwrap(String.class);
+ * break;
+ *
+ * default: throw new NoSuchElementException(key);
+ * }
* }
* }
* </code></pre>
* {@inheritDoc}
*/
- @Override public abstract void construct(String key, ConfigurationSource src) throws NoSuchElementException;
+ @Override public abstract void construct(
+ String key,
+ ConfigurationSource src,
+ boolean includeInternal
+ ) throws NoSuchElementException;
/**
* Assigns default value to the corresponding leaf. Defaults are gathered from configuration schema class.
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/NamedListNode.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/NamedListNode.java
index d6b97ee..342673d 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/NamedListNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/tree/NamedListNode.java
@@ -336,7 +336,7 @@ public final class NamedListNode<N extends InnerNode> implements NamedListChange
}
/** {@inheritDoc} */
- @Override public void construct(String key, ConfigurationSource src) {
+ @Override public void construct(String key, ConfigurationSource src, boolean includeInternal) {
if (src == null)
delete(key);
else
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
index a9f2739..c8e359d 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
@@ -49,7 +49,7 @@ public class ConfigurationFlattener {
// Explicit access to the children of super root guarantees that "oldInnerNodesStack" is never empty and thus
// we don't need null-checks when calling Deque#peek().
- updates.traverseChildren(new FlattenerVisitor(oldInnerNodesStack, resMap));
+ updates.traverseChildren(new FlattenerVisitor(oldInnerNodesStack, resMap), true);
assert oldInnerNodesStack.peek() == curRoot : oldInnerNodesStack;
@@ -100,7 +100,7 @@ public class ConfigurationFlattener {
/** {@inheritDoc} */
@Override public Void doVisitLeafNode(String key, Serializable newVal) {
// Read same value from old tree.
- Serializable oldVal = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.leafNodeVisitor());
+ Serializable oldVal = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.leafNodeVisitor(), true);
// Do not put duplicates into the resulting map.
if (singleTreeTraversal || !Objects.deepEquals(oldVal, newVal))
@@ -112,7 +112,7 @@ public class ConfigurationFlattener {
/** {@inheritDoc} */
@Override public Void doVisitInnerNode(String key, InnerNode newNode) {
// Read same node from old tree.
- InnerNode oldNode = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.innerNodeVisitor());
+ InnerNode oldNode = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.innerNodeVisitor(), true);
// Skip subtree that has not changed.
if (oldNode == newNode && !singleTreeTraversal)
@@ -123,7 +123,7 @@ public class ConfigurationFlattener {
else {
oldInnerNodesStack.push(oldNode);
- newNode.traverseChildren(this);
+ newNode.traverseChildren(this, true);
oldInnerNodesStack.pop();
}
@@ -134,7 +134,7 @@ public class ConfigurationFlattener {
/** {@inheritDoc} */
@Override public <N extends InnerNode> Void doVisitNamedListNode(String key, NamedListNode<N> newNode) {
// Read same named list node from old tree.
- NamedListNode<?> oldNode = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.namedListNodeVisitor());
+ NamedListNode<?> oldNode = oldInnerNodesStack.peek().traverseChild(key, ConfigurationUtil.namedListNodeVisitor(), true);
// Skip subtree that has not changed.
if (oldNode == newNode && !singleTreeTraversal)
@@ -171,7 +171,7 @@ public class ConfigurationFlattener {
else {
oldInnerNodesStack.push(oldNamedElement);
- newNamedElement.traverseChildren(this);
+ newNamedElement.traverseChildren(this, true);
oldInnerNodesStack.pop();
}
@@ -215,7 +215,7 @@ public class ConfigurationFlattener {
singleTreeTraversal = true;
deletion = delete;
- node.traverseChildren(this);
+ node.traverseChildren(this, true);
deletion = false;
singleTreeTraversal = false;
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationNotificationsUtil.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationNotificationsUtil.java
index 02a58b0..332c94b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationNotificationsUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationNotificationsUtil.java
@@ -78,7 +78,7 @@ public class ConfigurationNotificationsUtil {
oldInnerNode.traverseChildren(new ConfigurationVisitor<Void>() {
/** {@inheritDoc} */
@Override public Void visitLeafNode(String key, Serializable oldLeaf) {
- Serializable newLeaf = newInnerNode.traverseChild(key, leafNodeVisitor());
+ Serializable newLeaf = newInnerNode.traverseChild(key, leafNodeVisitor(), true);
if (newLeaf != oldLeaf) {
var dynProperty = (DynamicProperty<Serializable>)cfgNodeMembers.get(key);
@@ -98,7 +98,7 @@ public class ConfigurationNotificationsUtil {
/** {@inheritDoc} */
@Override public Void visitInnerNode(String key, InnerNode oldNode) {
- InnerNode newNode = newInnerNode.traverseChild(key, innerNodeVisitor());
+ InnerNode newNode = newInnerNode.traverseChild(key, innerNodeVisitor(), true);
var dynCfg = (DynamicConfiguration<InnerNode, ?>)cfgNodeMembers.get(key);
@@ -109,7 +109,7 @@ public class ConfigurationNotificationsUtil {
/** {@inheritDoc} */
@Override public <N extends InnerNode> Void visitNamedListNode(String key, NamedListNode<N> oldNamedList) {
- var newNamedList = (NamedListNode<InnerNode>)newInnerNode.traverseChild(key, namedListNodeVisitor());
+ var newNamedList = (NamedListNode<InnerNode>)newInnerNode.traverseChild(key, namedListNodeVisitor(), true);
if (newNamedList != oldNamedList) {
var namedListCfg = (NamedListConfiguration<?, InnerNode, ?>)cfgNodeMembers.get(key);
@@ -226,7 +226,7 @@ public class ConfigurationNotificationsUtil {
return null;
}
- });
+ }, true);
}
/**
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
index 8c1cdb5..e093f12 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
@@ -18,17 +18,29 @@
package org.apache.ignite.internal.configuration.util;
import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Queue;
import java.util.RandomAccess;
+import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.ignite.configuration.NamedListView;
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.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.internal.configuration.storage.ConfigurationStorage;
import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
@@ -37,6 +49,8 @@ import org.apache.ignite.internal.configuration.tree.InnerNode;
import org.apache.ignite.internal.configuration.tree.NamedListNode;
import org.apache.ignite.internal.configuration.tree.TraversableTreeNode;
+import static java.util.stream.Collectors.toList;
+
/** */
public class ConfigurationUtil {
/** Configuration source that copies values without modifying tham. */
@@ -96,23 +110,30 @@ public class ConfigurationUtil {
*
* @param keys Random access list with keys.
* @param node Node where method will search for subnode.
+ * @param includeInternal Include internal configuration nodes (private configuration extensions).
* @return Either {@link TraversableTreeNode} or {@link Serializable} depending on the keys and schema.
* @throws KeyNotFoundException If node is not found.
*/
- public static Object find(List<String> keys, TraversableTreeNode node) throws KeyNotFoundException {
+ public static Object find(
+ List<String> keys,
+ TraversableTreeNode node,
+ boolean includeInternal
+ ) throws KeyNotFoundException {
assert keys instanceof RandomAccess : keys.getClass();
var visitor = new ConfigurationVisitor<>() {
- /** */
+ /** Current index of the key in the {@code keys}. */
private int i;
+ /** {@inheritDoc} */
@Override public Object visitLeafNode(String key, Serializable val) {
if (i != keys.size())
throw new KeyNotFoundException("Configuration value '" + join(keys.subList(0, i)) + "' is a leaf");
-
- return val;
+ else
+ return val;
}
+ /** {@inheritDoc} */
@Override public Object visitInnerNode(String key, InnerNode node) {
if (i == keys.size())
return node;
@@ -120,14 +141,17 @@ public class ConfigurationUtil {
throw new KeyNotFoundException("Configuration node '" + join(keys.subList(0, i)) + "' is null");
else {
try {
- return node.traverseChild(keys.get(i++), this);
+ return node.traverseChild(keys.get(i++), this, includeInternal);
}
catch (NoSuchElementException e) {
- throw new KeyNotFoundException("Configuration '" + join(keys.subList(0, i)) + "' is not found");
+ throw new KeyNotFoundException(
+ "Configuration '" + join(keys.subList(0, i)) + "' is not found"
+ );
}
}
}
+ /** {@inheritDoc} */
@Override public <N extends InnerNode> Object visitNamedListNode(String key, NamedListNode<N> node) {
if (i == keys.size())
return node;
@@ -272,13 +296,13 @@ public class ConfigurationUtil {
continue;
if (val == null)
- node.construct(key, null);
+ node.construct(key, null, true);
else if (val instanceof Map)
- node.construct(key, new InnerConfigurationSource((Map<String, ?>)val));
+ node.construct(key, new InnerConfigurationSource((Map<String, ?>)val), true);
else {
assert val instanceof Serializable;
- node.construct(key, new LeafConfigurationSource((Serializable)val));
+ node.construct(key, new LeafConfigurationSource((Serializable)val), true);
}
}
}
@@ -326,7 +350,7 @@ public class ConfigurationUtil {
boolean construct = map.size() != sizeDiff;
if (oldKey == null) {
- node.construct(newKey, new InnerConfigurationSource(map));
+ node.construct(newKey, new InnerConfigurationSource(map), true);
node.setInternalId(newKey, internalId);
}
@@ -334,10 +358,10 @@ public class ConfigurationUtil {
node.rename(oldKey, newKey);
if (construct)
- node.construct(newKey, new InnerConfigurationSource(map));
+ node.construct(newKey, new InnerConfigurationSource(map), true);
}
else if (construct)
- node.construct(oldKey, new InnerConfigurationSource(map));
+ node.construct(oldKey, new InnerConfigurationSource(map), true);
// Else it's just index adjustment after new elements insertion.
if (newKey == null)
@@ -366,7 +390,7 @@ public class ConfigurationUtil {
else {
assert val instanceof Serializable;
- node.construct(oldKey, new LeafConfigurationSource((Serializable)val));
+ node.construct(oldKey, new LeafConfigurationSource((Serializable)val), true);
}
}
@@ -407,6 +431,7 @@ public class ConfigurationUtil {
*/
public static void addDefaults(InnerNode node) {
node.traverseChildren(new ConfigurationVisitor<>() {
+ /** {@inheritDoc} */
@Override public Object visitLeafNode(String key, Serializable val) {
// If source value is null then inititalise the same value on the destination node.
if (val == null)
@@ -415,25 +440,27 @@ public class ConfigurationUtil {
return null;
}
+ /** {@inheritDoc} */
@Override public Object visitInnerNode(String key, InnerNode innerNode) {
// Instantiate field in destination node before doing something else or copy it if it wasn't null.
- node.construct(key, EMPTY_CFG_SRC);
+ node.construct(key, EMPTY_CFG_SRC, true);
- addDefaults(node.traverseChild(key, innerNodeVisitor()));
+ addDefaults(node.traverseChild(key, innerNodeVisitor(), true));
return null;
}
+ /** {@inheritDoc} */
@Override public <N extends InnerNode> Object visitNamedListNode(String key, NamedListNode<N> namedList) {
// Copy internal map.
- node.construct(key, EMPTY_CFG_SRC);
+ node.construct(key, EMPTY_CFG_SRC, true);
- namedList = (NamedListNode<N>)node.traverseChild(key, namedListNodeVisitor());
+ namedList = (NamedListNode<N>)node.traverseChild(key, namedListNodeVisitor(), true);
for (String namedListKey : namedList.namedListKeys()) {
if (namedList.get(namedListKey) != null) {
// Copy the element.
- namedList.construct(namedListKey, EMPTY_CFG_SRC);
+ namedList.construct(namedListKey, EMPTY_CFG_SRC, true);
addDefaults(namedList.get(namedListKey));
}
@@ -441,7 +468,7 @@ public class ConfigurationUtil {
return null;
}
- });
+ }, true);
}
/**
@@ -469,7 +496,7 @@ public class ConfigurationUtil {
return null;
}
- });
+ }, true);
}
/**
@@ -499,8 +526,8 @@ public class ConfigurationUtil {
*/
public static ConfigurationVisitor<NamedListNode<?>> namedListNodeVisitor() {
return new ConfigurationVisitor<>() {
- @Override
- public <N extends InnerNode> NamedListNode<?> visitNamedListNode(String key, NamedListNode<N> node) {
+ /** {@inheritDoc} */
+ @Override public <N extends InnerNode> NamedListNode<?> visitNamedListNode(String key, NamedListNode<N> node) {
return node;
}
};
@@ -519,4 +546,196 @@ public class ConfigurationUtil {
}
}
}
+
+ /**
+ * Get and check schemas and their extensions.
+ *
+ * @param extensions Schema extensions with {@link InternalConfiguration}.
+ * @return Internal schema extensions. Mapping: original of the scheme -> internal extensions.
+ * @throws IllegalArgumentException If the schema or its extensions are not valid.
+ */
+ public static Map<Class<?>, Set<Class<?>>> internalSchemaExtensions(Collection<Class<?>> extensions) {
+ if (extensions.isEmpty())
+ return Map.of();
+ else {
+ Map<Class<?>, Set<Class<?>>> res = new HashMap<>();
+
+ for (Class<?> extension : extensions) {
+ if (!extension.isAnnotationPresent(InternalConfiguration.class)) {
+ throw new IllegalArgumentException(String.format(
+ "Extension should contain @%s: %s",
+ InternalConfiguration.class.getSimpleName(),
+ extension.getName()
+ ));
+ }
+ else
+ res.computeIfAbsent(extension.getSuperclass(), cls -> new HashSet<>()).add(extension);
+ }
+
+ return res;
+ }
+ }
+
+ /**
+ * Checks whether configuration schema field represents primitive configuration value.
+ *
+ * @param schemaField Configuration Schema class field.
+ * @return {@code true} if field represents primitive configuration.
+ */
+ public 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.
+ */
+ public 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.
+ */
+ public static boolean isNamedConfigValue(Field schemaField) {
+ return schemaField.isAnnotationPresent(NamedConfigValue.class);
+ }
+
+ /**
+ * Get the value of a {@link NamedConfigValue#syntheticKeyName}.
+ *
+ * @param field Configuration Schema class field.
+ * @return Name for the synthetic key.
+ */
+ public static String syntheticKeyName(Field field) {
+ assert isNamedConfigValue(field) : field;
+
+ return field.getAnnotation(NamedConfigValue.class).syntheticKeyName();
+ }
+
+ /**
+ * Get the value of a {@link Value#hasDefault}.
+ *
+ * @param field Configuration Schema class field.
+ * @return Indicates that the current configuration value has a default value.
+ */
+ public static boolean hasDefault(Field field) {
+ assert isValue(field) : field;
+
+ return field.getAnnotation(Value.class).hasDefault();
+ }
+
+ /**
+ * Get the default value of a {@link Value}.
+ *
+ * @param field Configuration Schema class field.
+ * @return Default value.
+ */
+ public static Object defaultValue(Field field) {
+ assert hasDefault(field) : field;
+
+ try {
+ Object o = field.getDeclaringClass().getDeclaredConstructor().newInstance();
+
+ field.setAccessible(true);
+
+ return field.get(o);
+ }
+ catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get merged schema extension fields.
+ *
+ * @param extensions Configuration schema extensions ({@link InternalConfiguration}).
+ * @return Unique fields of the schema and its extensions.
+ * @throws IllegalArgumentException If there is a conflict in field names.
+ */
+ public static Set<Field> extensionsFields(Collection<Class<?>> extensions) {
+ if (extensions.isEmpty())
+ return Set.of();
+ else {
+ Map<String, Field> res = new HashMap<>();
+
+ for (Class<?> extension : extensions) {
+ assert extension.isAnnotationPresent(InternalConfiguration.class) : extension;
+
+ for (Field field : extension.getDeclaredFields()) {
+ String fieldName = field.getName();
+
+ if (res.containsKey(fieldName)) {
+ throw new IllegalArgumentException(String.format(
+ "Duplicate field names are not allowed [field=%s, classes=%s]",
+ field,
+ classNames(res.get(fieldName), field)
+ ));
+ }
+ else
+ res.put(fieldName, field);
+ }
+ }
+
+ return Set.copyOf(res.values());
+ }
+ }
+
+ /**
+ * Collect all configuration schemes with {@link ConfigurationRoot} or {@link Config}
+ * including all sub configuration schemes for fields with {@link ConfigValue} or {@link NamedConfigValue}.
+ *
+ * @param schemaClasses Configuration schemas (starting points) with {@link ConfigurationRoot} or {@link Config}.
+ * @return All configuration schemes with {@link ConfigurationRoot} or {@link Config}.
+ * @throws IllegalArgumentException If the configuration schemas does not contain
+ * {@link ConfigurationRoot} or {@link Config}.
+ */
+ public static Set<Class<?>> collectSchemas(Collection<Class<?>> schemaClasses) {
+ if (schemaClasses.isEmpty())
+ return Set.of();
+ else {
+ Set<Class<?>> res = new HashSet<>();
+
+ Queue<Class<?>> queue = new ArrayDeque<>(Set.copyOf(schemaClasses));
+
+ while (!queue.isEmpty()) {
+ Class<?> cls = queue.poll();
+
+ if (!cls.isAnnotationPresent(ConfigurationRoot.class) && !cls.isAnnotationPresent(Config.class)) {
+ throw new IllegalArgumentException(String.format(
+ "Configuration schema must contain @%s or @%s: %s",
+ ConfigurationRoot.class.getSimpleName(),
+ Config.class.getSimpleName(),
+ cls.getName()
+ ));
+ }
+ else {
+ res.add(cls);
+
+ for (Field f : cls.getDeclaredFields()) {
+ if ((f.isAnnotationPresent(ConfigValue.class) || f.isAnnotationPresent(NamedConfigValue.class))
+ && !res.contains(f.getType()))
+ queue.add(f.getType());
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ /**
+ * Get the class names of the fields.
+ *
+ * @param fields Fields.
+ * @return Fields class names.
+ */
+ public static List<String> classNames(Field... fields) {
+ return Stream.of(fields).map(Field::getDeclaringClass).map(Class::getName).collect(toList());
+ }
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
index a2608f9..9b9a16f 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
@@ -89,7 +89,7 @@ public abstract class KeysTrackingConfigurationVisitor<T> implements Configurati
* @return Anything that implementation decides to return.
*/
protected T doVisitInnerNode(String key, InnerNode node) {
- node.traverseChildren(this);
+ node.traverseChildren(this, true);
return null;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationContextImpl.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationContextImpl.java
index e8f5127..171b90b 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationContextImpl.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationContextImpl.java
@@ -96,7 +96,7 @@ class ValidationContextImpl<VIEW> implements ValidationContext<VIEW> {
/** {@inheritDoc} */
@Override public VIEW getOldValue() {
try {
- return (VIEW)find(currentPath, oldRoots);
+ return (VIEW)find(currentPath, oldRoots, true);
}
catch (KeyNotFoundException ignore) {
return null;
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
index 0af0765..ccb1bb1 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/validation/ValidationUtil.java
@@ -70,7 +70,7 @@ public class ValidationUtil {
return null;
}
- });
+ }, true);
return super.doVisitInnerNode(key, innerNode);
}
@@ -135,7 +135,7 @@ public class ValidationUtil {
}
}
}
- });
+ }, true);
return issues;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
index 21a7899..7a1bf4b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/CollectionUtils.java
@@ -18,10 +18,19 @@
package org.apache.ignite.internal.util;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
import org.jetbrains.annotations.Nullable;
+import static java.util.Collections.addAll;
+import static java.util.Collections.emptyIterator;
+import static java.util.Collections.unmodifiableSet;
+
/**
* Utility class provides various method to work with collections.
*/
@@ -70,6 +79,69 @@ public final class CollectionUtils {
return list.get(0);
}
+ /**
+ * Union set and items.
+ *
+ * @param set Set.
+ * @param ts Items.
+ * @param <T> Type of the elements of the list.
+ * @return Immutable union of set and items.
+ */
+ @SafeVarargs
+ public static <T> Set<T> union(@Nullable Set<T> set, @Nullable T... ts) {
+ if (nullOrEmpty(set))
+ return ts == null || ts.length == 0 ? Set.of() : Set.of(ts);
+ else if (ts == null || ts.length == 0)
+ return unmodifiableSet(set);
+ else {
+ Set<T> res = new HashSet<>(set);
+
+ addAll(res, ts);
+
+ return unmodifiableSet(res);
+ }
+ }
+
+ /**
+ * Create a lazy concatenation of iterables.
+ * <p>
+ * NOTE: {@link Iterator#remove} - not supported.
+ *
+ * @param iterables Iterables.
+ * @param <T> Type of the elements.
+ * @return Concatenation of iterables.
+ */
+ @SafeVarargs
+ public static <T> Iterable<? extends T> concat(@Nullable Iterable<? extends T>... iterables) {
+ if (iterables == null || iterables.length == 0)
+ return Collections::emptyIterator;
+ else {
+ return () -> new Iterator<T>() {
+ /** Current index at {@code iterables}. */
+ int i = 0;
+
+ /** Current iterator. */
+ Iterator<? extends T> curr = emptyIterator();
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNext() {
+ while (!curr.hasNext() && i < iterables.length)
+ curr = iterables[i++].iterator();
+
+ return curr.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ @Override public T next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ else
+ return curr.next();
+ }
+ };
+ }
+ }
+
/** Stub. */
private CollectionUtils() {
// No op.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
new file mode 100644
index 0000000..27acfa0
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.util;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.StreamSupport;
+import org.junit.jupiter.api.Test;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.ignite.internal.util.CollectionUtils.concat;
+import static org.apache.ignite.internal.util.CollectionUtils.union;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Testing the {@link CollectionUtils}.
+ */
+public class CollectionUtilsTest {
+ /** */
+ @Test
+ void testConcatIterables() {
+ assertTrue(collect(concat(null)).isEmpty());
+ assertTrue(collect(concat(List.of())).isEmpty());
+ assertTrue(collect(concat(List.of(), List.of())).isEmpty());
+
+ assertEquals(List.of(1), collect(concat(List.of(1))));
+ assertEquals(List.of(1), collect(concat(List.of(1), List.of())));
+ assertEquals(List.of(1), collect(concat(List.of(), List.of(1))));
+
+ assertEquals(List.of(1, 2, 3), collect(concat(List.of(1), List.of(2, 3))));
+ }
+
+ /** */
+ @Test
+ void testUnion() {
+ assertTrue(union(null, null).isEmpty());
+ assertTrue(union(Set.of(), null).isEmpty());
+ assertTrue(union(null, new Object[] {}).isEmpty());
+
+ assertEquals(Set.of(1), union(Set.of(1), null));
+ assertEquals(Set.of(1), union(Set.of(1), new Integer[] {}));
+ assertEquals(Set.of(1), union(null, new Integer[] {1}));
+ assertEquals(Set.of(1), union(Set.of(), new Integer[] {1}));
+
+ assertEquals(Set.of(1, 2), union(Set.of(1), new Integer[] {2}));
+ }
+
+ /**
+ * Collect of elements.
+ *
+ * @param iterable Iterable.
+ * @param <T> Type of the elements.
+ * @return Collected elements.
+ */
+ private <T> List<? extends T> collect(Iterable<? extends T> iterable) {
+ return StreamSupport.stream(iterable.spliterator(), false).collect(toList());
+ }
+}
diff --git a/modules/network/src/integrationTest/java/org/apache/ignite/utils/ClusterServiceTestUtils.java b/modules/network/src/integrationTest/java/org/apache/ignite/utils/ClusterServiceTestUtils.java
index 2767b0a..93ca0e3 100644
--- a/modules/network/src/integrationTest/java/org/apache/ignite/utils/ClusterServiceTestUtils.java
+++ b/modules/network/src/integrationTest/java/org/apache/ignite/utils/ClusterServiceTestUtils.java
@@ -18,6 +18,7 @@
package org.apache.ignite.utils;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import org.apache.ignite.configuration.annotation.ConfigurationType;
import org.apache.ignite.configuration.schemas.network.NetworkConfiguration;
@@ -59,7 +60,8 @@ public class ClusterServiceTestUtils {
ConfigurationManager nodeConfigurationMgr = new ConfigurationManager(
Collections.singleton(NetworkConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(ConfigurationType.LOCAL)
+ new TestConfigurationStorage(ConfigurationType.LOCAL),
+ List.of()
);
var clusterSvc = clusterSvcFactory.createClusterService(
diff --git a/modules/rest/src/test/java/org/apache/ignite/rest/presentation/ConfigurationPresentationTest.java b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/ConfigurationPresentationTest.java
index d54669e..6c35c8f 100644
--- a/modules/rest/src/test/java/org/apache/ignite/rest/presentation/ConfigurationPresentationTest.java
+++ b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/ConfigurationPresentationTest.java
@@ -70,7 +70,8 @@ public class ConfigurationPresentationTest {
cfgRegistry = new ConfigurationRegistry(
List.of(TestRootConfiguration.KEY),
Map.of(Value.class, Set.of(validator)),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
cfgRegistry.start();
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/storage/ITDistributedConfigurationStorageTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/storage/ITDistributedConfigurationStorageTest.java
index cbc070c..7ed2695 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/storage/ITDistributedConfigurationStorageTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/storage/ITDistributedConfigurationStorageTest.java
@@ -101,7 +101,8 @@ public class ITDistributedConfigurationStorageTest {
cfgManager = new ConfigurationManager(
rootKeys,
Map.of(),
- new LocalConfigurationStorage(vaultManager)
+ new LocalConfigurationStorage(vaultManager),
+ List.of()
);
metaStorageManager = new MetaStorageManager(
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 9c2755f..0aa136c 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -150,7 +150,8 @@ public class IgniteImpl implements Ignite {
ClientConnectorConfiguration.KEY
),
Map.of(),
- new LocalConfigurationStorage(vaultMgr)
+ new LocalConfigurationStorage(vaultMgr),
+ List.of()
);
clusterSvc = new ScaleCubeClusterServiceFactory().createClusterService(
@@ -178,7 +179,8 @@ public class IgniteImpl implements Ignite {
TablesConfiguration.KEY
),
Map.of(),
- new DistributedConfigurationStorage(metaStorageMgr, vaultMgr)
+ new DistributedConfigurationStorage(metaStorageMgr, vaultMgr),
+ List.of()
);
baselineMgr = new BaselineManager(
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
index 4ec9da5..f92ee5b 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
@@ -73,7 +73,8 @@ public class SchemaConfigurationConverterTest {
confRegistry = new ConfigurationRegistry(
List.of(TablesConfiguration.KEY),
Map.of(TableValidator.class, Set.of(SchemaTableValidatorImpl.INSTANCE)),
- new TestConfigurationStorage(DISTRIBUTED)
+ new TestConfigurationStorage(DISTRIBUTED),
+ List.of()
);
confRegistry.start();
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
index d5fa621..c2e79b6 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
@@ -160,13 +160,15 @@ public class TableManagerTest {
nodeCfgMgr = new ConfigurationManager(
List.of(NodeConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(LOCAL)
+ new TestConfigurationStorage(LOCAL),
+ List.of()
);
clusterCfgMgr = new ConfigurationManager(
List.of(ClusterConfiguration.KEY, TablesConfiguration.KEY),
Map.of(),
- new TestConfigurationStorage(DISTRIBUTED)
+ new TestConfigurationStorage(DISTRIBUTED),
+ List.of()
);
nodeCfgMgr.start();