This module contains the API classes and the implementation for the Ignite Configuration framework. The idea is to provide the so-called Unified Configuration — a common way of configuring both local Ignite nodes and remote Ignite clusters. The original concept is described in IEP-55.
Type-safe schema of a configuration, which is used for generating public API interfaces and internal implementations to avoid writing boilerplate code.
All schema classes must end with the ConfigurationSchema
suffix.
ConfigurationRegistry
is the entry point of the module. It is used to register root keys, validators, storages and to start / stop the component. Refer to the class javadocs for more details.
All Ignite configuration instances can be represented by a forest, where every node has a name, usually referred to as a key. RootKey
interface represents a type-safe object that holds the key of the root node of the configuration tree.
Instances of this interface are generated automatically and are mandatory for registering the configuration roots.
An example configuration schema may look like the following:
@ConfigurationRoot(rootName = "root", type = ConfigurationType.LOCAL) public static class ParentConfigurationSchema { @NamedConfigValue private NamedElementConfigurationSchema elements; @ConfigValue private ChildConfigurationSchema child; } @Config public static class ChildConfigurationSchema { @Value(hasDefault = true) public String str1 = "foobar"; @Value @Immutable public String str2; }
@ConfigurationRoot
marks the root schema. It contains the following properties:
type
property, which can either be LOCAL
or DISTRIBUTED
. This property dictates the storage type used to persist the schema — Vault
or Metastorage
. Vault
stores data locally, while Metastorage
is a distributed system that should store only cluster-wide configuration properties;rootName
property assigns a key to the root node of the tree that will represent the corresponding configuration schema;@Config
is similar to the @ConfigurationRoot
but represents an inner configuration node;
@ConfigValue
marks a nested schema field. Cyclic dependencies are not allowed;
@NamedConfigValue
is similar to @ConfigValue
, but such fields represent a collection of properties, not a single instance. Every element of the collection will have a String
name, similar to a Map
. NamedListConfiguration
interface is used to represent this field in the generated configuration classes.
@Value
annotation marks the leaf values. hasDefault
property can be used to set default values for fields: if set to true
, the default value will be used to initialize the annotated configuration field in case no value has been provided explicitly. This annotation can only be present on fields of the Java primitive or String
type.
All leaves must be public and corresponding configuration values must not be null;
@Immutable
annotation can only be present on fields marked with the @Value
annotation. Annotated fields cannot be changed after they have been initialized (either manually or by assigning a default value).
Configuration interfaces are generated at compile time. For the example above, the following code would be generated:
public interface ParentConfiguration extends ConfigurationTree<ParentView, ParentChange> { RootKey<ParentConfiguration, ParentView> KEY = ...; NamedConfigurationTree<NamedElementConfiguration, NamedElementView, NamedElementChange> elements(); ChildConfiguration child(); ParentView value(); Future<Void> change(Consumer<ParentChange> change); } public interface ChildConfiguration extends ConfigurationTree<ChildView, ChildChange> { ConfigurationValue<String> str(); ChildView value(); Future<Void> change(Consumer<ChildChange> change); }
KEY
constant uniquely identifies the configuration root;child()
method can be used to access the child node;value()
method creates a corresponding snapshot (an immutable view) of the configuration node;change()
method should be used to update the values in the configuration tree.value()
methods return a read-only view of the configuration tree, represented by a special set of View interfaces. For the example above, the following interfaces would be generated:
public interface ParentView { NamedListView<? extends NamedElementView> elements(); ChildView child(); } public interface ChildView { String str(); }
To modify the configuration tree, one should use the change
method, which executes the update requests asynchronously and in a transactional manner. Update requests are represented by a set of Change
interfaces. For the example above, the following interfaces would be generated:
public interface ParentChange { ParentChange changeElements(Consumer<NamedListChange<NamedElementChange>> elements); ParentChange changeChild(Consumer<ChildChange> child); } public interface ChildChange { ChildChange changeStr(String str); }
For example, to update all child nodes of the parent configuration in a single transaction:
ParentConfiguration parentCfg = ...; parentConfiguration.change(parent -> parent.changeChild(child -> child.changeStr("newStr1") ) ).get(); ChildConfiguration childCfg = parentCfg.child(); childCfg.changeStr("newStr2").get();
It is possible to execute several change requests for different roots in a single transaction, but all these roots must have the same storage type. However, this is only possible using the command line tool via the REST API, there's no public Java API at the moment.