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/12/06 09:24:54 UTC
[ignite-3] branch main updated: IGNITE-15897 ServiceLoader integration into configuration (#460)
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 e984c7c IGNITE-15897 ServiceLoader integration into configuration (#460)
e984c7c is described below
commit e984c7c83bd671d5be978ae586db547c3c4b50a0
Author: Roman Puchkovskiy <ro...@gmail.com>
AuthorDate: Mon Dec 6 13:24:46 2021 +0400
IGNITE-15897 ServiceLoader integration into configuration (#460)
---
.idea/codeStyles/codeStyleConfig.xml | 2 +-
modules/api/pom.xml | 24 ++++
.../src/main/java/org/apache/ignite/Ignition.java | 15 +++
.../java/org/apache/ignite/IgnitionManager.java | 2 +-
.../CoreDistributedConfigurationModule.java | 60 ++++++++++
.../CoreLocalConfigurationModule.java | 49 ++++++++
...nite.internal.configuration.ConfigurationModule | 18 +++
.../CoreDistributedConfigurationModuleTest.java | 100 ++++++++++++++++
.../CoreLocalConfigurationModuleTest.java | 93 +++++++++++++++
.../ignite/configuration/annotation/Config.java | 18 ++-
.../annotation/ConfigurationRoot.java | 30 ++++-
.../configuration/ConfigurationModule.java | 103 +++++++++++++++++
modules/runner/pom.xml | 6 +
.../org/apache/ignite/internal/app/IgniteImpl.java | 79 +++++++------
.../apache/ignite/internal/app/IgnitionImpl.java | 30 +++--
.../internal/configuration/CompoundModule.java | 89 +++++++++++++++
.../configuration/ConfigurationModules.java | 68 +++++++++++
.../ServiceLoaderModulesProvider.java} | 40 +++----
.../internal/configuration/CompoundModuleTest.java | 127 +++++++++++++++++++++
.../configuration/ConfigurationModulesTest.java | 81 +++++++++++++
.../ServiceLoaderModulesProviderTest.java | 50 ++++++++
.../configuration/TestConfigurationModule.java | 31 +++++
...nite.internal.configuration.ConfigurationModule | 17 +++
modules/schema/pom.xml | 7 +-
.../SchemaDistributedConfigurationModule.java | 47 ++++++++
...nite.internal.configuration.ConfigurationModule | 17 +++
.../SchemaDistributedConfigurationModuleTest.java | 82 +++++++++++++
modules/table/pom.xml | 12 ++
.../TableDistributedConfigurationModule.java | 40 +++++++
...nite.internal.configuration.ConfigurationModule | 17 +++
.../TableDistributedConfigurationModuleTest.java | 73 ++++++++++++
parent/pom.xml | 7 ++
32 files changed, 1356 insertions(+), 78 deletions(-)
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
index b9d18bf..ea0ad13 100644
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -2,4 +2,4 @@
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="GoogleStyle" />
</state>
-</component>
\ No newline at end of file
+</component>
diff --git a/modules/api/pom.xml b/modules/api/pom.xml
index f9f07e7..833b90f 100644
--- a/modules/api/pom.xml
+++ b/modules/api/pom.xml
@@ -38,11 +38,35 @@
<artifactId>ignite-configuration-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-configuration</artifactId>
+ </dependency>
+
<!-- 3rd party dependencies -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</dependency>
+
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/modules/api/src/main/java/org/apache/ignite/Ignition.java b/modules/api/src/main/java/org/apache/ignite/Ignition.java
index 2871273..4824fb6 100644
--- a/modules/api/src/main/java/org/apache/ignite/Ignition.java
+++ b/modules/api/src/main/java/org/apache/ignite/Ignition.java
@@ -39,6 +39,21 @@ public interface Ignition {
public Ignite start(@NotNull String name, @Nullable Path configPath, @NotNull Path workDir);
/**
+ * Starts an Ignite node with an optional bootstrap configuration from a HOCON file, with an optional
+ * class loader for further usage by {@link java.util.ServiceLoader}.
+ *
+ * @param name Name of the node. Must not be {@code null}.
+ * @param configPath Path to the node configuration in the HOCON format. Can be {@code null}.
+ * @param workDir Work directory for the started node. Must not be {@code null}.
+ * @param serviceLoaderClassLoader The class loader to be used to load provider-configuration files and provider
+ * classes, or {@code null} if the system
+ * class loader (or, failing that, the bootstrap class loader) is to be used
+ * @return Started Ignite node.
+ */
+ public Ignite start(@NotNull String name, @Nullable Path configPath, @NotNull Path workDir,
+ @Nullable ClassLoader serviceLoaderClassLoader);
+
+ /**
* Starts an Ignite node with an optional bootstrap configuration from a URL linking to HOCON configs.
*
* @param name Name of the node. Must not be {@code null}.
diff --git a/modules/api/src/main/java/org/apache/ignite/IgnitionManager.java b/modules/api/src/main/java/org/apache/ignite/IgnitionManager.java
index 400442b..84074f6 100644
--- a/modules/api/src/main/java/org/apache/ignite/IgnitionManager.java
+++ b/modules/api/src/main/java/org/apache/ignite/IgnitionManager.java
@@ -106,7 +106,7 @@ public class IgnitionManager {
}
}
- return ignition.start(nodeName, cfgPath, workDir);
+ return ignition.start(nodeName, cfgPath, workDir, clsLdr);
}
/**
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/CoreDistributedConfigurationModule.java b/modules/api/src/main/java/org/apache/ignite/configuration/CoreDistributedConfigurationModule.java
new file mode 100644
index 0000000..e47b02f
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/CoreDistributedConfigurationModule.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.schemas.runner.ClusterConfiguration;
+import org.apache.ignite.configuration.schemas.store.DataStorageConfiguration;
+import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+
+/**
+ * {@link ConfigurationModule} for cluster-wide configuration provided by ignite-api.
+ */
+public class CoreDistributedConfigurationModule implements ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.DISTRIBUTED;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<RootKey<?, ?>> rootKeys() {
+ return List.of(
+ ClusterConfiguration.KEY,
+ TablesConfiguration.KEY,
+ DataStorageConfiguration.KEY
+ );
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<Class<?>> polymorphicSchemaExtensions() {
+ return List.of(
+ HashIndexConfigurationSchema.class,
+ SortedIndexConfigurationSchema.class,
+ PartialIndexConfigurationSchema.class
+ );
+ }
+}
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/CoreLocalConfigurationModule.java b/modules/api/src/main/java/org/apache/ignite/configuration/CoreLocalConfigurationModule.java
new file mode 100644
index 0000000..965bfca
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/CoreLocalConfigurationModule.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.schemas.clientconnector.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.schemas.network.NetworkConfiguration;
+import org.apache.ignite.configuration.schemas.rest.RestConfiguration;
+import org.apache.ignite.configuration.schemas.runner.NodeConfiguration;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+
+/**
+ * {@link ConfigurationModule} for node-local configuration provided by ignite-api.
+ */
+public class CoreLocalConfigurationModule implements ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.LOCAL;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<RootKey<?, ?>> rootKeys() {
+ return List.of(
+ NetworkConfiguration.KEY,
+ NodeConfiguration.KEY,
+ RestConfiguration.KEY,
+ ClientConnectorConfiguration.KEY
+ );
+ }
+}
diff --git a/modules/api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule b/modules/api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
new file mode 100644
index 0000000..e16f06d
--- /dev/null
+++ b/modules/api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+org.apache.ignite.configuration.CoreLocalConfigurationModule
+org.apache.ignite.configuration.CoreDistributedConfigurationModule
diff --git a/modules/api/src/test/java/org/apache/ignite/configuration/CoreDistributedConfigurationModuleTest.java b/modules/api/src/test/java/org/apache/ignite/configuration/CoreDistributedConfigurationModuleTest.java
new file mode 100644
index 0000000..84156b7
--- /dev/null
+++ b/modules/api/src/test/java/org/apache/ignite/configuration/CoreDistributedConfigurationModuleTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.ServiceLoader.Provider;
+import org.apache.ignite.configuration.schemas.runner.ClusterConfiguration;
+import org.apache.ignite.configuration.schemas.store.DataStorageConfiguration;
+import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link CoreDistributedConfigurationModule}.
+ */
+class CoreDistributedConfigurationModuleTest {
+ private final CoreDistributedConfigurationModule module = new CoreDistributedConfigurationModule();
+
+ @Test
+ void typeIsDistributed() {
+ assertThat(module.type(), is(DISTRIBUTED));
+ }
+
+ @Test
+ void hasClusterConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(ClusterConfiguration.KEY));
+ }
+
+ @Test
+ void hasTablesConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(TablesConfiguration.KEY));
+ }
+
+ @Test
+ void hasDataStorageConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(DataStorageConfiguration.KEY));
+ }
+
+ @Test
+ void providesNoValidators() {
+ assertThat(module.validators(), is(anEmptyMap()));
+ }
+
+ @Test
+ void providesNoInternalSchemaExtensions() {
+ assertThat(module.internalSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void providesHashIndexConfigurationSchemaAsPolymorphicExtension() {
+ assertThat(module.polymorphicSchemaExtensions(), hasItem(HashIndexConfigurationSchema.class));
+ }
+
+ @Test
+ void providesSortedIndexConfigurationSchemaAsPolymorphicExtension() {
+ assertThat(module.polymorphicSchemaExtensions(), hasItem(SortedIndexConfigurationSchema.class));
+ }
+
+ @Test
+ void providesPartialIndexConfigurationSchemaAsPolymorphicExtension() {
+ assertThat(module.polymorphicSchemaExtensions(), hasItem(PartialIndexConfigurationSchema.class));
+ }
+
+ @Test
+ void isLoadedByServiceLoader() {
+ Optional<ConfigurationModule> maybeModule = ServiceLoader.load(ConfigurationModule.class).stream()
+ .map(Provider::get)
+ .filter(module -> module instanceof CoreDistributedConfigurationModule)
+ .findAny();
+
+ assertThat(maybeModule, isPresent());
+ }
+}
diff --git a/modules/api/src/test/java/org/apache/ignite/configuration/CoreLocalConfigurationModuleTest.java b/modules/api/src/test/java/org/apache/ignite/configuration/CoreLocalConfigurationModuleTest.java
new file mode 100644
index 0000000..46504ad
--- /dev/null
+++ b/modules/api/src/test/java/org/apache/ignite/configuration/CoreLocalConfigurationModuleTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.ServiceLoader.Provider;
+import org.apache.ignite.configuration.schemas.clientconnector.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.schemas.network.NetworkConfiguration;
+import org.apache.ignite.configuration.schemas.rest.RestConfiguration;
+import org.apache.ignite.configuration.schemas.runner.NodeConfiguration;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link CoreLocalConfigurationModule}.
+ */
+class CoreLocalConfigurationModuleTest {
+ private final CoreLocalConfigurationModule module = new CoreLocalConfigurationModule();
+
+ @Test
+ void typeIsLocal() {
+ assertThat(module.type(), is(LOCAL));
+ }
+
+ @Test
+ void hasNetworkConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(NetworkConfiguration.KEY));
+ }
+
+ @Test
+ void hasNodeConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(NodeConfiguration.KEY));
+ }
+
+ @Test
+ void hasRestConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(RestConfiguration.KEY));
+ }
+
+ @Test
+ void hasClientConnectorConfigurationRoot() {
+ assertThat(module.rootKeys(), hasItem(ClientConnectorConfiguration.KEY));
+ }
+
+ @Test
+ void providesNoValidators() {
+ assertThat(module.validators(), is(anEmptyMap()));
+ }
+
+ @Test
+ void providesNoInternalSchemaExtensions() {
+ assertThat(module.internalSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void providesNoPolymorphicSchemaExtensions() {
+ assertThat(module.polymorphicSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void isLoadedByServiceLoader() {
+ Optional<ConfigurationModule> maybeModule = ServiceLoader.load(ConfigurationModule.class).stream()
+ .map(Provider::get)
+ .filter(module -> module instanceof CoreLocalConfigurationModule)
+ .findAny();
+
+ assertThat(maybeModule, isPresent());
+ }
+}
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
index 18000bb..d17aacc 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/Config.java
@@ -25,19 +25,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * This annotation, if applied to a class, marks it as a configuration schema. Annotation processor generates several classes for each
- * configuration schema:
+ * This annotation, if applied to a class, marks it as a configuration schema part. Annotation processor generates
+ * several classes for each configuration schema part:
* <ul>
- * <li>Config - Represents configuration itself, provides API to init, change and view it. Extends {@code DynamicConfiguration}</li>
+ * <li>Config - Represents configuration itself, provides API to init, change and view it.
+ * Extends {@code DynamicConfiguration}</li>
* <li>Change - changes config tree</li>
* <li>View - immutable object to view config tree</li>
* </ul>
*
* <h1 class="header">Example</h1>
- * Here is how to create a root configuration schema:
+ * Here is how to create a configuration schema part:
* <pre><code>
- * {@literal @}Config(value = "local", root = true)
- * public class LocalConfigurationSchema {
+ * {@literal @}Config
+ * public class SubConfigurationSchema {
*
* {@literal @}Value
* public String foo;
@@ -49,6 +50,11 @@ import java.lang.annotation.Target;
* public SomeOtherConfiguration someOther;
* }
* </code></pre>
+ *
+ * <p>The main difference between @{@link ConfigurationRoot} and @{@link Config} is that the former marks schema root,
+ * while the latter is for marking non-root parts of the schema.
+ *
+ * @see ConfigurationRoot
*/
@Target({TYPE})
@Retention(RUNTIME)
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
index f2c124e..e790bdb 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
@@ -25,8 +25,34 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * Annotation that marks underlying class as a root configuration schema. Has basically the same properties as {@link Config} + adds extra
- * properties.
+ * This annotation, if applied to a class, marks it as a configuration schema root. Annotation processor generates
+ * several classes for each configuration schema:
+ * <ul>
+ * <li>Config - Represents the configuration, provides API to init, change and view it.
+ * Extends {@code DynamicConfiguration}</li>
+ * <li>Change - changes the config tree</li>
+ * <li>View - an immutable object to view the config tree</li>
+ * </ul>
+ *
+ * <h1 class="header">Example</h1>
+ * Here is how to create a root configuration schema:
+ * <pre><code>
+ * {@literal @}ConfigurationRoot(rootName = "a.b", type = ConfigurationType.LOCAL)
+ * public class LocalConfigurationSchema {
+ *
+ * {@literal @}Value
+ * public String foo;
+ *
+ * {@literal @}Value
+ * public boolean bar;
+ *
+ * {@literal @}ConfigValue
+ * public SomeOtherConfiguration someOther;
+ * }
+ * </code></pre>
+ *
+ * <p>The main difference between @{@link ConfigurationRoot} and @{@link Config} is that the former marks schema root,
+ * while the latter is for marking non-root parts of the schema.
*
* @see Config
*/
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModule.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModule.java
new file mode 100644
index 0000000..94081c5
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModule.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.annotation.InternalConfiguration;
+import org.apache.ignite.configuration.annotation.PolymorphicConfig;
+import org.apache.ignite.configuration.validation.Validator;
+
+/**
+ * A module of configuration provided by a JAR file (or, in its source form, by a Maven/Gradle/... module).
+ *
+ * <p>Each configuration module only supplies configuration of only one {@link ConfigurationType}, so,
+ * if a library needs to provide both node-local and cluster-wide configuration, it needs to supply
+ * two ConfigurationModule instances.
+ *
+ * <p>Designed for integration with {@link java.util.ServiceLoader} mechanism, so ConfigurationModule instances
+ * provided by a library are to be defined as services either in
+ * {@code META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule}, or in a {@code module-info.java}.
+ *
+ * <p>Supplies the following configuration components:
+ * <ul>
+ * <li><b>rootKeys</b> ({@link RootKey} instances)</li>
+ * <li><b>validators</b> ({@link Validator} instances)</li>
+ * <li><b>internalSchemaExtensions</b> (classes annotated with {@link InternalConfiguration})</li>
+ * <li><b>polymorphicSchemaExtensions</b> (classes annotataed with {@link PolymorphicConfig})</li>
+ * </ul>
+ *
+ * @see ConfigurationType
+ * @see RootKey
+ * @see Validator
+ * @see InternalConfiguration
+ * @see PolymorphicConfig
+ */
+public interface ConfigurationModule {
+ /**
+ * Type of the configuration provided by this module.
+ *
+ * @return configuration type
+ */
+ ConfigurationType type();
+
+ /**
+ * Returns keys of configuration roots provided by this module.
+ *
+ * @return root keys
+ */
+ default Collection<RootKey<?, ?>> rootKeys() {
+ return emptySet();
+ }
+
+ /**
+ * Returns configuration validators provided by this module.
+ *
+ * @return configuration validators
+ */
+ default Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators() {
+ return emptyMap();
+ }
+
+ /**
+ * Returns classes of internal schema extensions (annotated with {@link InternalConfiguration})
+ * provided by this module.
+ *
+ * @return internal schema extensions' classes
+ */
+ default Collection<Class<?>> internalSchemaExtensions() {
+ return emptySet();
+ }
+
+ /**
+ * Returns classes of polymorphic schema extensions (annotated with {@link PolymorphicConfig})
+ * provided by this module.
+ *
+ * @return polymorphic schema extensions' classes
+ */
+ default Collection<Class<?>> polymorphicSchemaExtensions() {
+ return emptySet();
+ }
+}
diff --git a/modules/runner/pom.xml b/modules/runner/pom.xml
index 41eeeab..05e7c9e 100644
--- a/modules/runner/pom.xml
+++ b/modules/runner/pom.xml
@@ -113,6 +113,12 @@
</dependency>
<dependency>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
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 5abb738..e375b3d 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
@@ -24,28 +24,20 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgnitionManager;
import org.apache.ignite.client.handler.ClientHandlerModule;
-import org.apache.ignite.configuration.schemas.clientconnector.ClientConnectorConfiguration;
import org.apache.ignite.configuration.schemas.network.NetworkConfiguration;
-import org.apache.ignite.configuration.schemas.rest.RestConfiguration;
-import org.apache.ignite.configuration.schemas.runner.ClusterConfiguration;
-import org.apache.ignite.configuration.schemas.runner.NodeConfiguration;
import org.apache.ignite.configuration.schemas.store.DataStorageConfiguration;
-import org.apache.ignite.configuration.schemas.table.ColumnTypeValidator;
-import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
-import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
-import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
-import org.apache.ignite.configuration.schemas.table.TableValidator;
import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
import org.apache.ignite.internal.baseline.BaselineManager;
import org.apache.ignite.internal.configuration.ConfigurationManager;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+import org.apache.ignite.internal.configuration.ConfigurationModules;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
-import org.apache.ignite.internal.configuration.schema.ExtendedTableConfigurationSchema;
+import org.apache.ignite.internal.configuration.ServiceLoaderModulesProvider;
import org.apache.ignite.internal.configuration.storage.DistributedConfigurationStorage;
import org.apache.ignite.internal.configuration.storage.LocalConfigurationStorage;
import org.apache.ignite.internal.manager.IgniteComponent;
@@ -54,8 +46,6 @@ import org.apache.ignite.internal.metastorage.server.persistence.RocksDbKeyValue
import org.apache.ignite.internal.processors.query.calcite.QueryProcessor;
import org.apache.ignite.internal.processors.query.calcite.SqlQueryProcessor;
import org.apache.ignite.internal.raft.Loza;
-import org.apache.ignite.internal.schema.configuration.ColumnTypeValidatorImpl;
-import org.apache.ignite.internal.schema.configuration.TableValidatorImpl;
import org.apache.ignite.internal.table.distributed.TableManager;
import org.apache.ignite.internal.table.distributed.TableTxManagerImpl;
import org.apache.ignite.internal.tx.TxManager;
@@ -148,28 +138,29 @@ public class IgniteImpl implements Ignite {
/**
* The Constructor.
*
- * @param name Ignite node name.
- * @param workDir Work directory for the started node. Must not be {@code null}.
+ * @param name Ignite node name.
+ * @param workDir Work directory for the started node. Must not be {@code null}.
+ * @param serviceProviderClassLoader The class loader to be used to load provider-configuration files and provider
+ * classes, or {@code null} if the system class loader (or, failing that
+ * the bootstrap class loader) is to be used
*/
IgniteImpl(
String name,
- Path workDir
+ Path workDir,
+ ClassLoader serviceProviderClassLoader
) {
this.name = name;
vaultMgr = createVault(workDir);
+ ConfigurationModules modules = loadConfigurationModules(serviceProviderClassLoader);
+
nodeCfgMgr = new ConfigurationManager(
- List.of(
- NetworkConfiguration.KEY,
- NodeConfiguration.KEY,
- RestConfiguration.KEY,
- ClientConnectorConfiguration.KEY
- ),
- Map.of(),
+ modules.local().rootKeys(),
+ modules.local().validators(),
new LocalConfigurationStorage(vaultMgr),
- List.of(),
- List.of()
+ modules.local().internalSchemaExtensions(),
+ modules.local().polymorphicSchemaExtensions()
);
NetworkConfiguration networkConfiguration = nodeCfgMgr.configurationRegistry().getConfiguration(NetworkConfiguration.KEY);
@@ -197,18 +188,11 @@ public class IgniteImpl implements Ignite {
);
clusterCfgMgr = new ConfigurationManager(
- List.of(
- ClusterConfiguration.KEY,
- TablesConfiguration.KEY,
- DataStorageConfiguration.KEY
- ),
- Map.of(
- TableValidator.class, Set.of(TableValidatorImpl.INSTANCE),
- ColumnTypeValidator.class, Set.of(ColumnTypeValidatorImpl.INSTANCE)
- ),
+ modules.distributed().rootKeys(),
+ modules.distributed().validators(),
new DistributedConfigurationStorage(metaStorageMgr, vaultMgr),
- List.of(ExtendedTableConfigurationSchema.class),
- List.of(HashIndexConfigurationSchema.class, SortedIndexConfigurationSchema.class, PartialIndexConfigurationSchema.class)
+ modules.distributed().internalSchemaExtensions(),
+ modules.distributed().polymorphicSchemaExtensions()
);
baselineMgr = new BaselineManager(
@@ -242,6 +226,29 @@ public class IgniteImpl implements Ignite {
);
}
+ private ConfigurationModules loadConfigurationModules(ClassLoader classLoader) {
+ var modulesProvider = new ServiceLoaderModulesProvider();
+ List<ConfigurationModule> modules = modulesProvider.modules(classLoader);
+
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Configuration modules loaded: {}", modules);
+ }
+
+ if (modules.isEmpty()) {
+ throw new IllegalStateException("No configuration modules were loaded, this means Ignite cannot start. "
+ + "Please make sure that the classloader for loading services is correct.");
+ }
+
+ var configModules = new ConfigurationModules(modules);
+
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Local root keys: {}", configModules.local().rootKeys());
+ LOG.info("Distributed root keys: {}", configModules.distributed().rootKeys());
+ }
+
+ return configModules;
+ }
+
/**
* Starts ignite node.
*
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
index bb7f897..0d3a81b 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
@@ -62,16 +62,24 @@ public class IgnitionImpl implements Ignition {
/**
* Node name to node instance mapping. Please pay attention, that nodes in given map might be in any state: STARTING, STARTED, STOPPED.
*/
- private static Map<String, IgniteImpl> nodes = new ConcurrentHashMap<>();
+ private static final Map<String, IgniteImpl> nodes = new ConcurrentHashMap<>();
/** {@inheritDoc} */
@Override
public Ignite start(@NotNull String nodeName, @Nullable Path cfgPath, @NotNull Path workDir) {
+ return start(nodeName, cfgPath, workDir, defaultServiceClassLoader());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Ignite start(@NotNull String nodeName, @Nullable Path cfgPath, @NotNull Path workDir,
+ @Nullable ClassLoader serviceLoaderClassLoader) {
try {
return doStart(
nodeName,
cfgPath == null ? null : Files.readString(cfgPath),
- workDir
+ workDir,
+ serviceLoaderClassLoader
);
} catch (IOException e) {
throw new IgniteException("Unable to read user specific configuration.", e);
@@ -82,10 +90,10 @@ public class IgnitionImpl implements Ignition {
@Override
public Ignite start(@NotNull String name, @Nullable URL cfgUrl, @NotNull Path workDir) {
if (cfgUrl == null) {
- return doStart(name, null, workDir);
+ return doStart(name, null, workDir, defaultServiceClassLoader());
} else {
try (InputStream cfgStream = cfgUrl.openStream()) {
- return doStart(name, new String(cfgStream.readAllBytes(), StandardCharsets.UTF_8), workDir);
+ return doStart(name, new String(cfgStream.readAllBytes(), StandardCharsets.UTF_8), workDir, defaultServiceClassLoader());
} catch (IOException e) {
throw new IgniteException("Unable to read user specific configuration.", e);
}
@@ -99,7 +107,8 @@ public class IgnitionImpl implements Ignition {
return doStart(
name,
cfg == null ? null : new String(cfg.readAllBytes(), StandardCharsets.UTF_8),
- workDir
+ workDir,
+ defaultServiceClassLoader()
);
} catch (IOException e) {
throw new IgniteException("Unable to read user specific configuration.", e);
@@ -109,7 +118,7 @@ public class IgnitionImpl implements Ignition {
/** {@inheritDoc} */
@Override
public Ignite start(@NotNull String name, @NotNull Path workDir) {
- return doStart(name, null, workDir);
+ return doStart(name, null, workDir, defaultServiceClassLoader());
}
/** {@inheritDoc} */
@@ -122,6 +131,10 @@ public class IgnitionImpl implements Ignition {
});
}
+ private ClassLoader defaultServiceClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
/**
* Starts an Ignite node with an optional bootstrap configuration from a HOCON file.
*
@@ -130,12 +143,13 @@ public class IgnitionImpl implements Ignition {
* @param workDir Work directory for the started node. Must not be {@code null}.
* @return Started Ignite node.
*/
- private static Ignite doStart(String nodeName, @Nullable String cfgContent, Path workDir) {
+ private static Ignite doStart(String nodeName, @Nullable String cfgContent, Path workDir,
+ @Nullable ClassLoader serviceLoaderClassLoader) {
if (nodeName.isEmpty()) {
throw new IllegalArgumentException("Node name must not be null or empty.");
}
- IgniteImpl nodeToStart = new IgniteImpl(nodeName, workDir);
+ IgniteImpl nodeToStart = new IgniteImpl(nodeName, workDir, serviceLoaderClassLoader);
IgniteImpl prevNode = nodes.putIfAbsent(nodeName, nodeToStart);
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
new file mode 100644
index 0000000..c6d9bec
--- /dev/null
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
@@ -0,0 +1,89 @@
+/*
+ * 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 static java.util.Collections.unmodifiableMap;
+import static java.util.stream.Collectors.flatMapping;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toUnmodifiableList;
+import static java.util.stream.Collectors.toUnmodifiableSet;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.validation.Validator;
+
+/**
+ * {@link ConfigurationModule} that merges a few {@code ConfigurationModule}s.
+ */
+public class CompoundModule implements ConfigurationModule {
+ private final ConfigurationType type;
+ private final List<ConfigurationModule> modules;
+
+ public CompoundModule(ConfigurationType type, Collection<ConfigurationModule> modules) {
+ this.type = type;
+ this.modules = List.copyOf(modules);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return type;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<RootKey<?, ?>> rootKeys() {
+ return unionFromModulesExtractedWith(ConfigurationModule::rootKeys);
+ }
+
+ private <T> List<T> unionFromModulesExtractedWith(Function<? super ConfigurationModule, ? extends Collection<T>> extractor) {
+ return modules.stream()
+ .flatMap(module -> extractor.apply(module).stream())
+ .collect(toUnmodifiableList());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators() {
+ Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> result = modules.stream()
+ .flatMap(module -> module.validators().entrySet().stream())
+ .collect(groupingBy(
+ Map.Entry::getKey,
+ flatMapping(e -> e.getValue().stream(), toUnmodifiableSet())
+ ));
+ return unmodifiableMap(result);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<Class<?>> internalSchemaExtensions() {
+ return unionFromModulesExtractedWith(ConfigurationModule::internalSchemaExtensions);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<Class<?>> polymorphicSchemaExtensions() {
+ return unionFromModulesExtractedWith(ConfigurationModule::polymorphicSchemaExtensions);
+ }
+}
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModules.java b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModules.java
new file mode 100644
index 0000000..06d12d6
--- /dev/null
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/ConfigurationModules.java
@@ -0,0 +1,68 @@
+/*
+ * 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 static java.util.stream.Collectors.toUnmodifiableList;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+
+/**
+ * An ensemble of {@link ConfigurationModule}s used to easily pick node-local and cluster-wide configuration
+ * and merge modules for convenience.
+ */
+public class ConfigurationModules {
+ private final CompoundModule localCompound;
+ private final CompoundModule distributedCompound;
+
+ /**
+ * Creates a new instance of {@link ConfigurationModules} wrapping modules passed to it.
+ *
+ * @param modules modules to wrap; they may be of different types
+ */
+ public ConfigurationModules(List<ConfigurationModule> modules) {
+ localCompound = compoundOfType(ConfigurationType.LOCAL, modules);
+ distributedCompound = compoundOfType(ConfigurationType.DISTRIBUTED, modules);
+ }
+
+ /**
+ * Return a module representing the result of merge of modules of type {@link ConfigurationType#LOCAL}.
+ *
+ * @return node-local configuration merge result
+ */
+ public ConfigurationModule local() {
+ return localCompound;
+ }
+
+ /**
+ * Return a module representing the result of merge of modules of type {@link ConfigurationType#DISTRIBUTED}.
+ *
+ * @return cluster-wide configuration merge result
+ */
+ public ConfigurationModule distributed() {
+ return distributedCompound;
+ }
+
+ private static CompoundModule compoundOfType(ConfigurationType type, Collection<ConfigurationModule> modules) {
+ List<ConfigurationModule> modulesOfGivenType = modules.stream()
+ .filter(module -> module.type() == type)
+ .collect(toUnmodifiableList());
+ return new CompoundModule(type, modulesOfGivenType);
+ }
+}
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProvider.java
similarity index 50%
copy from modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
copy to modules/runner/src/main/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProvider.java
index f2c124e..0b535e2 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/ConfigurationRoot.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProvider.java
@@ -15,36 +15,30 @@
* limitations under the License.
*/
-package org.apache.ignite.configuration.annotation;
+package org.apache.ignite.internal.configuration;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static java.util.stream.Collectors.toUnmodifiableList;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.ServiceLoader.Provider;
/**
- * Annotation that marks underlying class as a root configuration schema. Has basically the same properties as {@link Config} + adds extra
- * properties.
+ * Provides {@link ConfigurationModule}s using {@link ServiceLoader} mechanism.
+ * Uses {@link ConfigurationModule} interface to query the ServiceLoader.
*
- * @see Config
+ * @see ConfigurationModule
*/
-@Target(TYPE)
-@Retention(RUNTIME)
-@Documented
-public @interface ConfigurationRoot {
+public class ServiceLoaderModulesProvider {
/**
- * Returns the unique root name.
+ * Loads modules using {@link ServiceLoader} mechanism.
*
- * @return Unique root name.
+ * @param classLoader the class loader to use
+ * @return modules
*/
- String rootName();
-
- /**
- * Returns the type of the configuration.
- *
- * @return Type of the configuration.
- */
- ConfigurationType type() default ConfigurationType.LOCAL;
+ public List<ConfigurationModule> modules(ClassLoader classLoader) {
+ return ServiceLoader.load(ConfigurationModule.class, classLoader).stream()
+ .map(Provider::get)
+ .collect(toUnmodifiableList());
+ }
}
diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CompoundModuleTest.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CompoundModuleTest.java
new file mode 100644
index 0000000..6dca5d3
--- /dev/null
+++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CompoundModuleTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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 static java.util.Collections.emptyList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.when;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.validation.Validator;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class CompoundModuleTest {
+ @Mock
+ private RootKey<?, ?> rootKeyA;
+ @Mock
+ private RootKey<?, ?> rootKeyB;
+
+ @Mock
+ private Validator<AnnotationA, ?> validatorA;
+ @Mock
+ private Validator<AnnotationB, ?> validatorB;
+
+ @Mock
+ private ConfigurationModule moduleA;
+ @Mock
+ private ConfigurationModule moduleB;
+
+ private ConfigurationModule compound;
+
+ @BeforeEach
+ void createCompoundModule() {
+ compound = new CompoundModule(ConfigurationType.LOCAL, List.of(moduleA, moduleB));
+ }
+
+ @Test
+ void returnsTypePassedViaConstructor() {
+ var compound = new CompoundModule(ConfigurationType.LOCAL, emptyList());
+
+ assertThat(compound.type(), is(ConfigurationType.LOCAL));
+ }
+
+ @Test
+ void returnsUnionOfRootKeysOfItsModules() {
+ when(moduleA.rootKeys()).thenReturn(Set.of(rootKeyA));
+ when(moduleB.rootKeys()).thenReturn(Set.of(rootKeyB));
+
+ assertThat(compound.rootKeys(), containsInAnyOrder(rootKeyA, rootKeyB));
+ }
+
+ @Test
+ void providesUnionOfValidatorsMapsOfItsModulesPerKey() {
+ when(moduleA.validators()).thenReturn(Map.of(AnnotationA.class, Set.of(validatorA)));
+ when(moduleB.validators()).thenReturn(Map.of(AnnotationB.class, Set.of(validatorB)));
+
+ Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators = compound.validators();
+
+ assertThat(validators, hasEntry(AnnotationA.class, Set.of(validatorA)));
+ assertThat(validators, hasEntry(AnnotationB.class, Set.of(validatorB)));
+ }
+
+ @Test
+ void mergesValidatorsUnderTheSameKey() {
+ when(moduleA.validators()).thenReturn(Map.of(AnnotationA.class, Set.of(validatorA)));
+ when(moduleB.validators()).thenReturn(Map.of(AnnotationA.class, Set.of(validatorB)));
+
+ Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators = compound.validators();
+
+ assertThat(validators, hasEntry(AnnotationA.class, Set.of(validatorA, validatorB)));
+ }
+
+ @Test
+ void returnsUnionOfInternalSchemaExtensionsOfItsModules() {
+ when(moduleA.internalSchemaExtensions()).thenReturn(Set.of(ExtensionA.class));
+ when(moduleB.internalSchemaExtensions()).thenReturn(Set.of(ExtensionB.class));
+
+ assertThat(compound.internalSchemaExtensions(), containsInAnyOrder(ExtensionA.class, ExtensionB.class));
+ }
+
+ @Test
+ void returnsUnionOfPolymorphicSchemaExtensionsOfItsModules() {
+ when(moduleA.polymorphicSchemaExtensions()).thenReturn(Set.of(ExtensionA.class));
+ when(moduleB.polymorphicSchemaExtensions()).thenReturn(Set.of(ExtensionB.class));
+
+ assertThat(compound.polymorphicSchemaExtensions(), containsInAnyOrder(ExtensionA.class, ExtensionB.class));
+ }
+
+ private @interface AnnotationA {
+ }
+
+ private @interface AnnotationB {
+ }
+
+ private static class ExtensionA {
+ }
+
+ private static class ExtensionB {
+ }
+}
diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ConfigurationModulesTest.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ConfigurationModulesTest.java
new file mode 100644
index 0000000..aad5901
--- /dev/null
+++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ConfigurationModulesTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 static org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.ignite.configuration.RootKey;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ConfigurationModulesTest {
+ @Mock
+ private RootKey<?, ?> rootKeyA;
+ @Mock
+ private RootKey<?, ?> rootKeyB;
+ @Mock
+ private RootKey<?, ?> rootKeyC;
+ @Mock
+ private RootKey<?, ?> rootKeyD;
+
+ @Mock
+ private ConfigurationModule moduleA;
+ @Mock
+ private ConfigurationModule moduleB;
+ @Mock
+ private ConfigurationModule moduleC;
+ @Mock
+ private ConfigurationModule moduleD;
+
+ @Test
+ void mergesLocalModules() {
+ when(moduleA.type()).thenReturn(LOCAL);
+ when(moduleB.type()).thenReturn(LOCAL);
+ when(moduleC.type()).thenReturn(DISTRIBUTED);
+ when(moduleD.type()).thenReturn(DISTRIBUTED);
+ when(moduleA.rootKeys()).thenReturn(Set.of(rootKeyA));
+ when(moduleB.rootKeys()).thenReturn(Set.of(rootKeyB));
+
+ var modules = new ConfigurationModules(List.of(moduleA, moduleB, moduleC, moduleD));
+
+ assertThat(modules.local().rootKeys(), containsInAnyOrder(rootKeyA, rootKeyB));
+ }
+
+ @Test
+ void mergesDistributedModules() {
+ when(moduleA.type()).thenReturn(LOCAL);
+ when(moduleB.type()).thenReturn(LOCAL);
+ when(moduleC.type()).thenReturn(DISTRIBUTED);
+ when(moduleD.type()).thenReturn(DISTRIBUTED);
+ when(moduleC.rootKeys()).thenReturn(Set.of(rootKeyC));
+ when(moduleD.rootKeys()).thenReturn(Set.of(rootKeyD));
+
+ var modules = new ConfigurationModules(List.of(moduleA, moduleB, moduleC, moduleD));
+
+ assertThat(modules.distributed().rootKeys(), containsInAnyOrder(rootKeyC, rootKeyD));
+ }
+}
diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProviderTest.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProviderTest.java
new file mode 100644
index 0000000..3ffc30f
--- /dev/null
+++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/ServiceLoaderModulesProviderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 static java.util.stream.Collectors.toList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link ServiceLoaderModulesProvider}.
+ */
+class ServiceLoaderModulesProviderTest {
+ private final ServiceLoaderModulesProvider provider = new ServiceLoaderModulesProvider();
+
+ @Test
+ void loadsModulesDefinedAsServicesInMetaInf() {
+ List<ConfigurationModule> testModules = provider.modules(getClass().getClassLoader()).stream()
+ .filter(module -> module.getClass().equals(TestConfigurationModule.class))
+ .collect(toList());
+
+ assertThat(testModules, hasSize(1));
+ }
+
+ @Test
+ void loadsModulesWhenClassLoaderIsNull() {
+ List<ConfigurationModule> testModules = provider.modules(null).stream()
+ .filter(module -> module.getClass().equals(TestConfigurationModule.class))
+ .collect(toList());
+
+ assertThat(testModules, hasSize(1));
+ }
+}
diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationModule.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationModule.java
new file mode 100644
index 0000000..060b1ed
--- /dev/null
+++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/TestConfigurationModule.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;
+
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+
+/**
+ * A test {@link ConfigurationModule} implementation used to test loading by {@link ServiceLoaderModulesProvider}.
+ */
+public class TestConfigurationModule implements ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.LOCAL;
+ }
+}
diff --git a/modules/runner/src/test/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule b/modules/runner/src/test/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
new file mode 100644
index 0000000..685b7b2
--- /dev/null
+++ b/modules/runner/src/test/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.ignite.internal.configuration.TestConfigurationModule
diff --git a/modules/schema/pom.xml b/modules/schema/pom.xml
index 40139cf..eea9904 100644
--- a/modules/schema/pom.xml
+++ b/modules/schema/pom.xml
@@ -51,7 +51,6 @@
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-configuration</artifactId>
- <scope>test</scope>
</dependency>
<!-- 3rd party dependencies -->
@@ -94,6 +93,12 @@
</dependency>
<dependency>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModule.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModule.java
new file mode 100644
index 0000000..32f0e9d
--- /dev/null
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModule.java
@@ -0,0 +1,47 @@
+/*
+ * 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.schema.configuration;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.schemas.table.ColumnTypeValidator;
+import org.apache.ignite.configuration.schemas.table.TableValidator;
+import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+
+/**
+ * {@link ConfigurationModule} for cluster-wide configuration provided by ignite-schema.
+ */
+public class SchemaDistributedConfigurationModule implements ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.DISTRIBUTED;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators() {
+ return Map.of(
+ TableValidator.class, Set.of(TableValidatorImpl.INSTANCE),
+ ColumnTypeValidator.class, Set.of(ColumnTypeValidatorImpl.INSTANCE)
+ );
+ }
+}
diff --git a/modules/schema/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule b/modules/schema/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
new file mode 100644
index 0000000..86abc55
--- /dev/null
+++ b/modules/schema/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.ignite.internal.schema.configuration.SchemaDistributedConfigurationModule
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModuleTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModuleTest.java
new file mode 100644
index 0000000..1891eb2
--- /dev/null
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaDistributedConfigurationModuleTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.schema.configuration;
+
+import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import org.apache.ignite.configuration.schemas.table.ColumnTypeValidator;
+import org.apache.ignite.configuration.schemas.table.TableValidator;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link SchemaDistributedConfigurationModule}.
+ */
+class SchemaDistributedConfigurationModuleTest {
+ private final SchemaDistributedConfigurationModule module = new SchemaDistributedConfigurationModule();
+
+ @Test
+ void typeIsDistributed() {
+ assertThat(module.type(), is(DISTRIBUTED));
+ }
+
+ @Test
+ void hasNoConfigurationRoots() {
+ assertThat(module.rootKeys(), is(empty()));
+ }
+
+ @Test
+ void providesTableValidator() {
+ assertThat(module.validators(), hasEntry(is(TableValidator.class), hasItem(instanceOf(TableValidatorImpl.class))));
+ }
+
+ @Test
+ void providesColumnTypeValidator() {
+ assertThat(module.validators(),
+ hasEntry(is(ColumnTypeValidator.class), hasItem(instanceOf(ColumnTypeValidatorImpl.class))));
+ }
+
+ @Test
+ void providesNoInternalSchemaExtensions() {
+ assertThat(module.internalSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void providesNoPolymorphicSchemaExtensions() {
+ assertThat(module.polymorphicSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void isLoadedByServiceLoader() {
+ Optional<ConfigurationModule> maybeModule = ServiceLoader.load(ConfigurationModule.class).stream()
+ .map(ServiceLoader.Provider::get)
+ .filter(module -> module instanceof SchemaDistributedConfigurationModule)
+ .findAny();
+
+ assertThat(maybeModule, isPresent());
+ }
+}
diff --git a/modules/table/pom.xml b/modules/table/pom.xml
index 88a1f16..6007bcf 100644
--- a/modules/table/pom.xml
+++ b/modules/table/pom.xml
@@ -144,6 +144,18 @@
</dependency>
<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-network</artifactId>
<scope>test</scope>
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModule.java b/modules/table/src/main/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModule.java
new file mode 100644
index 0000000..1eb4286
--- /dev/null
+++ b/modules/table/src/main/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModule.java
@@ -0,0 +1,40 @@
+/*
+ * 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.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.internal.configuration.schema.ExtendedTableConfigurationSchema;
+
+/**
+ * {@link ConfigurationModule} for cluster-wide configuration provided by ignite-table.
+ */
+public class TableDistributedConfigurationModule implements ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.DISTRIBUTED;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<Class<?>> internalSchemaExtensions() {
+ return List.of(ExtendedTableConfigurationSchema.class);
+ }
+}
diff --git a/modules/table/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule b/modules/table/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
new file mode 100644
index 0000000..4e6012e
--- /dev/null
+++ b/modules/table/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.ignite.internal.configuration.TableDistributedConfigurationModule
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModuleTest.java b/modules/table/src/test/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModuleTest.java
new file mode 100644
index 0000000..f9c3273
--- /dev/null
+++ b/modules/table/src/test/java/org/apache/ignite/internal/configuration/TableDistributedConfigurationModuleTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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 static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
+import static org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import org.apache.ignite.internal.configuration.schema.ExtendedTableConfigurationSchema;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link TableDistributedConfigurationModule}.
+ */
+class TableDistributedConfigurationModuleTest {
+ private final TableDistributedConfigurationModule module = new TableDistributedConfigurationModule();
+
+ @Test
+ void typeIsDistributed() {
+ assertThat(module.type(), is(DISTRIBUTED));
+ }
+
+ @Test
+ void hasNoConfigurationRoots() {
+ assertThat(module.rootKeys(), is(empty()));
+ }
+
+ @Test
+ void providesNoValidators() {
+ assertThat(module.validators(), is(anEmptyMap()));
+ }
+
+ @Test
+ void providesExternalTableConfigurationSchemaAsInternalExtension() {
+ assertThat(module.internalSchemaExtensions(), hasItem(ExtendedTableConfigurationSchema.class));
+ }
+
+ @Test
+ void providesNoPolymorphicSchemaExtensions() {
+ assertThat(module.polymorphicSchemaExtensions(), is(empty()));
+ }
+
+ @Test
+ void isLoadedByServiceLoader() {
+ Optional<ConfigurationModule> maybeModule = ServiceLoader.load(ConfigurationModule.class).stream()
+ .map(ServiceLoader.Provider::get)
+ .filter(module -> module instanceof TableDistributedConfigurationModule)
+ .findAny();
+
+ assertThat(maybeModule, isPresent());
+ }
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index 77d226b..09286ef 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -68,6 +68,7 @@
<spoon.framework.version>8.4.0-beta-18</spoon.framework.version>
<typesafe.version>1.4.1</typesafe.version>
<hamcrest.version>2.2</hamcrest.version>
+ <hamcrest.optional.version>2.0.0</hamcrest.optional.version>
<scalecube.version>2.6.12</scalecube.version>
<calcite.version>1.27.0</calcite.version>
<janino.version>3.1.4</janino.version>
@@ -556,6 +557,12 @@
</dependency>
<dependency>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
+ <version>${hamcrest.optional.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>io.scalecube</groupId>
<artifactId>scalecube-cluster</artifactId>
<version>${scalecube.version}</version>