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/03/24 08:51:19 UTC
[ignite-3] branch main updated: IGNITE-14371 JSON representation
for configuration & partial code for JSON update requests parsing. (#72)
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 2e6ea88 IGNITE-14371 JSON representation for configuration & partial code for JSON update requests parsing. (#72)
2e6ea88 is described below
commit 2e6ea8850509a59a686abac44f0be2c5918cd3f8
Author: ibessonov <be...@gmail.com>
AuthorDate: Wed Mar 24 11:51:14 2021 +0300
IGNITE-14371 JSON representation for configuration & partial code for JSON update requests parsing. (#72)
---
.../ignite/configuration/ConfigurationChanger.java | 10 +
.../configuration/ConfigurationRegistry.java | 42 ++++
.../apache/ignite/configuration/Configurator.java | 28 ---
.../configuration/annotation/ConfigValue.java | 4 -
.../ignite/configuration/internal/SuperRoot.java | 6 +
.../internal/util/ConfigurationUtil.java | 17 +-
.../configuration/tree/ConfigurationVisitor.java | 2 +-
.../ignite/configuration/tree/NamedListNode.java | 12 +-
modules/rest/pom.xml | 1 +
.../java/org/apache/ignite/rest/RestModule.java | 31 +--
.../configuration/RestConfigurationSchema.java | 15 +-
.../rest/presentation/json/JsonConverter.java | 236 ++++++++++++++++++++-
.../rest/presentation/json/JsonPresentation.java | 14 +-
.../rest/presentation/json/JsonConverterTest.java | 211 ++++++++++++++++++
.../json/TestConfigurationStorage.java | 63 ++++++
.../ignite/configuration/ConfigurationModule.java | 2 +-
16 files changed, 624 insertions(+), 70 deletions(-)
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
index 67196ee..7990151 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationChanger.java
@@ -217,6 +217,16 @@ public class ConfigurationChanger {
}
/** */
+ public SuperRoot mergedSuperRoot() {
+ SuperRoot mergedSuperRoot = new SuperRoot(rootKeys);
+
+ for (StorageRoots storageRoots : storagesRootsMap.values())
+ mergedSuperRoot.append(storageRoots.roots);
+
+ return mergedSuperRoot;
+ }
+
+ /** */
private CompletableFuture<Void> change(SuperRoot changes, Class<? extends ConfigurationStorage> storageType) {
ConfigurationStorage storage = storageInstances.get(storageType);
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
index 8d4bcc5..5770249 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/ConfigurationRegistry.java
@@ -17,9 +17,12 @@
package org.apache.ignite.configuration;
+import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import javax.validation.constraints.Max;
@@ -27,10 +30,16 @@ import javax.validation.constraints.Min;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.internal.DynamicConfiguration;
import org.apache.ignite.configuration.internal.RootKeyImpl;
+import org.apache.ignite.configuration.internal.SuperRoot;
+import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
+import org.apache.ignite.configuration.internal.util.KeyNotFoundException;
import org.apache.ignite.configuration.internal.validation.MaxValidator;
import org.apache.ignite.configuration.internal.validation.MinValidator;
import org.apache.ignite.configuration.storage.ConfigurationStorage;
+import org.apache.ignite.configuration.tree.ConfigurationSource;
+import org.apache.ignite.configuration.tree.ConfigurationVisitor;
import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.TraversableTreeNode;
import org.apache.ignite.configuration.validation.Validator;
/** */
@@ -72,6 +81,39 @@ public class ConfigurationRegistry {
}
/**
+ * Convert configuration subtree into a user-defined representation.
+ *
+ * @param path Path to configuration subtree. Can be empty, can't be {@code null}.
+ * @param visitor Visitor that will be applied to the subtree and build the representation.
+ * @param <T> Type of the representation.
+ * @return User-defined representation constructed by {@code visitor}.
+ * @throws IllegalArgumentException If {@code path} is not found in current configuration.
+ */
+ public <T> T represent(List<String> path, ConfigurationVisitor<T> visitor) throws IllegalArgumentException {
+ SuperRoot mergedSuperRoot = changer.mergedSuperRoot();
+
+ Object node;
+ try {
+ node = ConfigurationUtil.find(path, mergedSuperRoot);
+ }
+ catch (KeyNotFoundException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+
+ if (node instanceof TraversableTreeNode)
+ return ((TraversableTreeNode)node).accept(null, visitor);
+
+ assert node == null || node instanceof Serializable;
+
+ return visitor.visitLeafNode(null, (Serializable)node);
+ }
+
+ /** */
+ public CompletableFuture<?> change(List<String> path, ConfigurationSource changesSource) {
+ throw new UnsupportedOperationException("IGNITE-14372 Not implemented yet.");
+ }
+
+ /**
* Method to instantiate a new {@link RootKey} for your configuration root. Invoked in generated code only.
* Does not register this root anywhere, used for static object initialization only.
*
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/Configurator.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/Configurator.java
deleted file mode 100644
index 05c1ebe..0000000
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/Configurator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.configuration;
-
-import org.apache.ignite.configuration.internal.DynamicConfiguration;
-
-/**
- * Convenient wrapper for configuration root. Provides access to configuration tree, stores validators, performs actions
- * on configuration such as initialized, change and view.
- * @param <T> Type of configuration root.
- */
-public class Configurator<T extends DynamicConfiguration<?, ?, ?>> {
-}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
index b593674..e4fbc42 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/annotation/ConfigValue.java
@@ -40,8 +40,4 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
@Retention(SOURCE)
@Documented
public @interface ConfigValue {
- /**
- * @return The name of the configuration.
- */
- String value() default "";
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
index a83852b..326c3cd 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/SuperRoot.java
@@ -63,6 +63,12 @@ public final class SuperRoot extends InnerNode {
}
/** */
+ public void append(SuperRoot otherRoot) {
+ //TODO IGNITE-14372 Revisit API of the super root.
+ roots.putAll(otherRoot.roots);
+ }
+
+ /** */
public InnerNode getRoot(RootKey<?, ?> rootKey) {
return roots.get(rootKey.key());
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
index 53fd2f2..e92659c 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/internal/util/ConfigurationUtil.java
@@ -255,8 +255,16 @@ public class ConfigurationUtil {
assert val == null || val instanceof Map || val instanceof Serializable;
- if (val == null)
- node.construct(key, null);
+ if (val == null) {
+ if (node instanceof NamedListNode) {
+ // Given that this particular method is applied to modify existing trees rather than
+ // creating new trees, a "hack" is required in this place. "construct" is designed to create
+ // "change" objects, thus it would just nullify named list element instead of deleting it.
+ ((NamedListNode<?>)node).forceDelete(key);
+ }
+ else
+ node.construct(key, null);
+ }
else if (val instanceof Map)
node.construct(key, new InnerConfigurationSource((Map<String, ?>)val));
else {
@@ -595,7 +603,10 @@ public class ConfigurationUtil {
for (String key : srcNode.namedListKeys()) {
InnerNode node = srcNode.get(key);
- dstNode.construct(key, node == null ? null : new PatchInnerConfigurationSource(node));
+ if (node == null)
+ ((NamedListNode<?>)dstNode).forceDelete(key); // Same as in fillFromPrefixMap.
+ else
+ dstNode.construct(key, new PatchInnerConfigurationSource(node));
}
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
index bf2e697..646c649 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/ConfigurationVisitor.java
@@ -39,7 +39,7 @@ public interface ConfigurationVisitor<T> {
* @param key Name of the node retrieved from its holder object.
* @param node Inner configuration node.
*/
- default T visitInnerNode(String key, InnerNode node) {
+ default T visitInnerNode(String key, InnerNode node) { //TODO IGNITE-14372 Pass interface, not implementation.
return null;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
index 530544c..718e0b0 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/tree/NamedListNode.java
@@ -95,6 +95,16 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
return this;
}
+ /**
+ * Deletes named list element.
+ *
+ * @param key Element's key.
+ */
+ public void forceDelete(String key) {
+ map.remove(key);
+ }
+
+ /** {@inheritDoc} */
@Override public NamedListChange<N, N> create(String key, Consumer<N> valConsumer) {
Objects.requireNonNull(valConsumer, "valConsumer");
@@ -111,7 +121,7 @@ public final class NamedListNode<N extends InnerNode> implements NamedListView<N
/** {@inheritDoc} */
@Override public void construct(String key, ConfigurationSource src) {
if (src == null)
- map.remove(key);
+ map.put(key, null);
else {
N val = map.get(key);
diff --git a/modules/rest/pom.xml b/modules/rest/pom.xml
index 8cf8376..3af59e5 100644
--- a/modules/rest/pom.xml
+++ b/modules/rest/pom.xml
@@ -83,6 +83,7 @@
<artifactId>netty-codec-http</artifactId>
</dependency>
+ <!-- Test dependencies. -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
diff --git a/modules/rest/src/main/java/org/apache/ignite/rest/RestModule.java b/modules/rest/src/main/java/org/apache/ignite/rest/RestModule.java
index 6124abd..a434eaa 100644
--- a/modules/rest/src/main/java/org/apache/ignite/rest/RestModule.java
+++ b/modules/rest/src/main/java/org/apache/ignite/rest/RestModule.java
@@ -17,6 +17,7 @@
package org.apache.ignite.rest;
+import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
@@ -29,13 +30,16 @@ import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
-import java.util.Collections;
+import java.util.List;
import java.util.Map;
import org.apache.ignite.configuration.ConfigurationRegistry;
+import org.apache.ignite.configuration.internal.util.ConfigurationUtil;
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
import org.apache.ignite.rest.configuration.RestConfiguration;
+import org.apache.ignite.rest.configuration.RestView;
import org.apache.ignite.rest.netty.RestApiInitializer;
import org.apache.ignite.rest.presentation.ConfigurationPresentation;
+import org.apache.ignite.rest.presentation.json.JsonConverter;
import org.apache.ignite.rest.presentation.json.JsonPresentation;
import org.apache.ignite.rest.routes.Router;
import org.slf4j.Logger;
@@ -50,7 +54,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
*/
public class RestModule {
/** */
- private static final int DFLT_PORT = 10300;
+ public static final int DFLT_PORT = 10300;
/** */
private static final String CONF_URL = "/management/v1/configuration/";
@@ -76,7 +80,7 @@ public class RestModule {
public void prepareStart(ConfigurationRegistry sysCfg, Reader moduleConfReader) {
sysConf = sysCfg;
- presentation = new JsonPresentation(Collections.emptyMap());
+ presentation = new JsonPresentation();
// FormatConverter converter = new JsonConverter();
//
@@ -98,7 +102,11 @@ public class RestModule {
.get(CONF_URL + ":" + PATH_PARAM, (req, resp) -> {
String cfgPath = req.queryParams().get(PATH_PARAM);
try {
- resp.json(presentation.representByPath(cfgPath));
+ List<String> path = ConfigurationUtil.split(cfgPath);
+
+ JsonElement json = sysConf.represent(path, JsonConverter.jsonVisitor());
+
+ resp.json(json);
}
catch (IllegalArgumentException pathE) {
ErrorResult eRes = new ErrorResult("CONFIG_PATH_UNRECOGNIZED", pathE.getMessage());
@@ -149,14 +157,16 @@ public class RestModule {
/** */
private void startRestEndpoint(Router router) throws InterruptedException {
- Integer desiredPort = sysConf.getConfiguration(RestConfiguration.KEY).port().value();
- Integer portRange = sysConf.getConfiguration(RestConfiguration.KEY).portRange().value();
+ RestView restConfigurationView = sysConf.getConfiguration(RestConfiguration.KEY).value();
+
+ int desiredPort = restConfigurationView.port();
+ int portRange = restConfigurationView.portRange();
int port = 0;
- if (portRange == null || portRange == 0) {
+ if (portRange == 0) {
try {
- port = (desiredPort != null ? desiredPort : DFLT_PORT);
+ port = desiredPort;
}
catch (RuntimeException e) {
log.warn("Failed to start REST endpoint: ", e);
@@ -209,9 +219,4 @@ public class RestModule {
workerGrp.shutdownGracefully();
}
}
-
- /** */
- public String configRootKey() {
- return "rest";
- }
}
diff --git a/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java b/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
index 5cd7bc1..0003eae 100644
--- a/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
+++ b/modules/rest/src/main/java/org/apache/ignite/rest/configuration/RestConfigurationSchema.java
@@ -17,19 +17,26 @@
package org.apache.ignite.rest.configuration;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
import org.apache.ignite.configuration.annotation.Value;
+import static org.apache.ignite.rest.RestModule.DFLT_PORT;
+
/**
* Configuration schema for REST endpoint subtree.
*/
@ConfigurationRoot(rootName = "rest")
public class RestConfigurationSchema {
/** */
- @Value
- public int port;
+ @Min(1024)
+ @Max(0xFFFF)
+ @Value(hasDefault = true)
+ public final int port = DFLT_PORT;
/** */
- @Value
- public int portRange;
+ @Min(0)
+ @Value(hasDefault = true)
+ public final int portRange = 0;
}
diff --git a/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonConverter.java b/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonConverter.java
index 555e406..abe070f 100644
--- a/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonConverter.java
+++ b/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonConverter.java
@@ -17,13 +17,30 @@
package org.apache.ignite.rest.presentation.json;
-import java.io.Reader;
-
import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.io.Reader;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Optional;
+import org.apache.ignite.configuration.tree.ConfigurationSource;
+import org.apache.ignite.configuration.tree.ConfigurationVisitor;
+import org.apache.ignite.configuration.tree.ConstructableTreeNode;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.NamedListNode;
import org.apache.ignite.rest.presentation.FormatConverter;
+import org.jetbrains.annotations.NotNull;
/** */
public class JsonConverter implements FormatConverter {
@@ -35,6 +52,221 @@ public class JsonConverter implements FormatConverter {
return gson.toJson(obj);
}
+ /** */
+ public static ConfigurationVisitor<JsonElement> jsonVisitor() {
+ return new ConfigurationVisitor<JsonElement>() {
+ /** */
+ private final Deque<JsonObject> jsonObjectsStack = new ArrayDeque<>();
+
+ /** {@inheritDoc} */
+ @Override public JsonElement visitLeafNode(String key, Serializable val) {
+ JsonElement jsonLeaf = toJsonLeaf(val);
+
+ addToParent(key, jsonLeaf);
+
+ return jsonLeaf;
+ }
+
+ /** {@inheritDoc} */
+ @Override public JsonElement visitInnerNode(String key, InnerNode node) {
+ JsonObject innerJsonNode = new JsonObject();
+
+ jsonObjectsStack.push(innerJsonNode);
+
+ node.traverseChildren(this);
+
+ jsonObjectsStack.pop();
+
+ addToParent(key, innerJsonNode);
+
+ return innerJsonNode;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <N extends InnerNode> JsonElement visitNamedListNode(String key, NamedListNode<N> node) {
+ JsonObject namedListJsonNode = new JsonObject();
+
+ jsonObjectsStack.push(namedListJsonNode);
+
+ for (String subkey : node.namedListKeys())
+ node.get(subkey).accept(subkey, this);
+
+ jsonObjectsStack.pop();
+
+ addToParent(key, namedListJsonNode);
+
+ return namedListJsonNode;
+ }
+
+ /** */
+ @NotNull private JsonElement toJsonLeaf(Serializable val) {
+ if (val == null)
+ return JsonNull.INSTANCE;
+
+ Class<? extends Serializable> valClass = val.getClass();
+
+ if (!valClass.isArray())
+ return toJsonPrimitive(val);
+
+ JsonArray jsonArray = new JsonArray();
+
+ for (int i = 0; i < Array.getLength(val); i++)
+ jsonArray.add(toJsonPrimitive(Array.get(val, i)));
+
+ return jsonArray;
+ }
+
+ /** */
+ @NotNull private JsonElement toJsonPrimitive(Object val) {
+ if (val == null)
+ return JsonNull.INSTANCE;
+
+ if (val instanceof Boolean)
+ return new JsonPrimitive((Boolean)val);
+
+ if (val instanceof String)
+ return new JsonPrimitive((String)val);
+
+ if (val instanceof Number)
+ return new JsonPrimitive((Number)val);
+
+ assert false : val;
+
+ throw new IllegalArgumentException(val.getClass().getCanonicalName());
+ }
+
+ /**
+ * Add subelement to the paretn JSON object if it exists.
+ *
+ * @param key Key for the passed JSON element.
+ * @param jsonElement JSON element to add to the parent.
+ */
+ private void addToParent(String key, JsonElement jsonElement) {
+ if (!jsonObjectsStack.isEmpty())
+ jsonObjectsStack.peek().add(key, jsonElement);
+ }
+ };
+ }
+
+ /** */
+ public static ConfigurationSource jsonSource(JsonElement jsonElement) {
+ //TODO IGNITE-14372 Finish this implementation.
+ return null;
+ }
+
+ private static class JsonObjectConfigurationSource implements ConfigurationSource {
+ /** Shared. */
+ private final List<String> path;
+
+ /** */
+ private final JsonObject jsonObject;
+
+ private JsonObjectConfigurationSource(List<String> path, JsonObject jsonObject) {
+ this.path = path;
+ this.jsonObject = jsonObject;
+ }
+
+ @Override public <T> T unwrap(Class<T> clazz) {
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Implement.
+ }
+
+ @Override public void descend(ConstructableTreeNode node) {
+ for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
+ String key = entry.getKey();
+
+ path.add(key);
+
+ JsonElement jsonElement = entry.getValue();
+
+ try {
+ if (jsonElement.isJsonArray() || jsonElement.isJsonPrimitive())
+ node.construct(key, new JsonPrimitiveConfigurationSource(path, jsonElement));
+ else if (jsonElement.isJsonNull()) {
+ node.construct(key, null);
+ }
+ else {
+ assert jsonElement.isJsonObject();
+
+ List<String> path = new ArrayList<>(this.path.size() + 1);
+ path.addAll(this.path);
+ path.add(key);
+
+ node.construct(key, new JsonObjectConfigurationSource(path, jsonElement.getAsJsonObject()));
+ }
+ }
+ catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+ }
+
+ path.remove(path.size() - 1);
+ }
+ }
+ }
+
+ private static class JsonPrimitiveConfigurationSource implements ConfigurationSource {
+ private final List<String> path;
+
+ private final JsonElement jsonLeaf;
+
+ private JsonPrimitiveConfigurationSource(List<String> path, JsonElement jsonLeaf) {
+ this.path = path;
+ this.jsonLeaf = jsonLeaf;
+ }
+
+ @Override public <T> T unwrap(Class<T> clazz) {
+ if (clazz.isArray() != jsonLeaf.isJsonArray())
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+
+ return null;
+ }
+
+ @Override public void descend(ConstructableTreeNode node) {
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+ }
+
+ private <T> T unwrap(JsonPrimitive jsonPrimitive, Class<T> clazz) {
+ assert !clazz.isArray();
+
+ if (clazz == String.class) {
+ if (!jsonPrimitive.isString())
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+
+ return clazz.cast(jsonPrimitive.getAsString());
+ }
+
+ if (Number.class.isAssignableFrom(clazz)) {
+ if (!jsonPrimitive.isNumber())
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+
+ if (clazz == Double.class)
+ return clazz.cast(jsonPrimitive.getAsDouble());
+
+ if (clazz == Long.class)
+ return clazz.cast(jsonPrimitive.getAsLong());
+
+ if (clazz == Integer.class) {
+ long longValue = jsonPrimitive.getAsLong();
+
+ if (longValue < Integer.MIN_VALUE || longValue > Integer.MAX_VALUE)
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+
+ return clazz.cast((int)longValue);
+ }
+
+ throw new AssertionError(clazz);
+ }
+
+ if (clazz == Boolean.class) {
+ if (!jsonPrimitive.isBoolean())
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+
+ return clazz.cast(jsonPrimitive.getAsBoolean());
+ }
+
+ throw new IllegalArgumentException(""); //TODO IGNITE-14372 Update comment.
+ }
+ }
+
/** {@inheritDoc} */
@Override public String convertTo(String rootName, Object src) {
Map<String, Object> res = new HashMap<>();
diff --git a/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonPresentation.java b/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonPresentation.java
index 46de08b..db5020d 100644
--- a/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonPresentation.java
+++ b/modules/rest/src/main/java/org/apache/ignite/rest/presentation/json/JsonPresentation.java
@@ -19,8 +19,6 @@ package org.apache.ignite.rest.presentation.json;
import java.util.Collections;
import java.util.Map;
-import org.apache.ignite.configuration.Configurator;
-import org.apache.ignite.configuration.internal.DynamicConfiguration;
import org.apache.ignite.rest.presentation.ConfigurationPresentation;
/** */
@@ -29,11 +27,7 @@ public class JsonPresentation implements ConfigurationPresentation<String> {
private final JsonConverter converter = new JsonConverter();
/** */
- private final Map<String, Configurator<? extends DynamicConfiguration<?, ?, ?>>> configsMap;
-
- /** */
- public JsonPresentation(Map<String, Configurator<? extends DynamicConfiguration<?, ?, ?>>> configsMap) {
- this.configsMap = configsMap;
+ public JsonPresentation() {
}
/** {@inheritDoc} */
@@ -70,12 +64,6 @@ public class JsonPresentation implements ConfigurationPresentation<String> {
throw new IllegalArgumentException("Invalid request, no root in request: " + configUpdate);
}
- Configurator<? extends DynamicConfiguration<?, ?, ?>> configurator = configsMap.get(root);
-
- if (configurator == null) {
- throw new IllegalArgumentException("Invalid request, configuration root not found: " + configUpdate);
- }
-
// Object updateObj = converter.convertFrom(configUpdate, root, configurator.getChangeType());
// configurator.set(BaseSelectors.find(root), updateObj);
diff --git a/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/JsonConverterTest.java b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/JsonConverterTest.java
new file mode 100644
index 0000000..f855ec8
--- /dev/null
+++ b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/JsonConverterTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.rest.presentation.json;
+
+import com.google.gson.JsonNull;
+import java.util.List;
+import org.apache.ignite.configuration.ConfigurationRegistry;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static com.google.gson.JsonParser.parseString;
+import static java.util.Collections.emptyList;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.ignite.rest.presentation.json.JsonConverter.jsonVisitor;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/** */
+public class JsonConverterTest {
+ /** */
+ @ConfigurationRoot(rootName = "root", storage = TestConfigurationStorage.class)
+ public static class JsonRootConfigurationSchema {
+ /** */
+ @NamedConfigValue
+ public JsonArraysConfigurationSchema arraysList;
+
+ /** */
+ @NamedConfigValue
+ public JsonPrimitivesConfigurationSchema primitivesList;
+ }
+
+ /** */
+ @Config
+ public static class JsonArraysConfigurationSchema {
+ /** */
+ @Value(hasDefault = true)
+ public boolean[] booleans = {false};
+
+ /** */
+ @Value(hasDefault = true)
+ public int[] ints = {0};
+
+ /** */
+ @Value(hasDefault = true)
+ public long[] longs = {0L};
+
+ /** */
+ @Value(hasDefault = true)
+ public double[] doubles = {0d};
+
+ /** */
+ @Value(hasDefault = true)
+ public String[] strings = {""};
+ }
+
+ /** */
+ @Config
+ public static class JsonPrimitivesConfigurationSchema {
+ /** */
+ @Value(hasDefault = true)
+ public boolean booleanVal = false;
+
+ /** */
+ @Value(hasDefault = true)
+ public int intVal = 0;
+
+ /** */
+ @Value(hasDefault = true)
+ public long longVal = 0L;
+
+ /** */
+ @Value(hasDefault = true)
+ public double doubleVal = 0d;
+
+ /** */
+ @Value(hasDefault = true)
+ public String stringVal = "";
+ }
+
+ /** */
+ private final ConfigurationRegistry registry = new ConfigurationRegistry();
+
+ /** */
+ private JsonRootConfiguration configuration;
+
+ /** */
+ @BeforeEach
+ public void before() {
+ registry.registerRootKey(JsonRootConfiguration.KEY);
+
+ registry.registerStorage(new TestConfigurationStorage());
+
+ configuration = registry.getConfiguration(JsonRootConfiguration.KEY);
+ }
+
+ /** */
+ @Test
+ public void toJson() throws Exception {
+ assertEquals(
+ parseString("{'root':{'arraysList':{},'primitivesList':{}}}"),
+ registry.represent(emptyList(), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("{'arraysList':{},'primitivesList':{}}"),
+ registry.represent(List.of("root"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("{}"),
+ registry.represent(List.of("root", "arraysList"), jsonVisitor())
+ );
+
+ configuration.change(cfg -> cfg
+ .changeArraysList(arraysList -> arraysList
+ .create("name", arrays -> {})
+ )
+ .changePrimitivesList(primitivesList -> primitivesList
+ .create("name", primitives -> {})
+ )
+ ).get(1, SECONDS);
+
+ assertEquals(
+ parseString("{'name':{'booleans':[false],'ints':[0],'longs':[0],'doubles':[0.0],'strings':['']}}"),
+ registry.represent(List.of("root", "arraysList"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("{'booleanVal':false,'intVal':0,'longVal':0,'doubleVal':0.0,'stringVal':''}"),
+ registry.represent(List.of("root", "primitivesList", "name"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("[false]"),
+ registry.represent(List.of("root", "arraysList", "name", "booleans"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("[0]"),
+ registry.represent(List.of("root", "arraysList", "name", "ints"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("[0]"),
+ registry.represent(List.of("root", "arraysList", "name", "longs"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("[0.0]"),
+ registry.represent(List.of("root", "arraysList", "name", "doubles"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("['']"),
+ registry.represent(List.of("root", "arraysList", "name", "strings"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("false"),
+ registry.represent(List.of("root", "primitivesList", "name", "booleanVal"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("0"),
+ registry.represent(List.of("root", "primitivesList", "name", "intVal"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("0"),
+ registry.represent(List.of("root", "primitivesList", "name", "longVal"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("0.0"),
+ registry.represent(List.of("root", "primitivesList", "name", "doubleVal"), jsonVisitor())
+ );
+
+ assertEquals(
+ parseString("''"),
+ registry.represent(List.of("root", "primitivesList", "name", "stringVal"), jsonVisitor())
+ );
+
+ assertThrows(IllegalArgumentException.class, () -> registry.represent(List.of("doot"), jsonVisitor()));
+
+ assertThrows(IllegalArgumentException.class, () -> registry.represent(List.of("root", "x"), jsonVisitor()));
+
+ assertEquals(
+ JsonNull.INSTANCE,
+ registry.represent(List.of("root", "primitivesList", "foo"), jsonVisitor())
+ );
+ }
+}
diff --git a/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/TestConfigurationStorage.java b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/TestConfigurationStorage.java
new file mode 100644
index 0000000..e175d54
--- /dev/null
+++ b/modules/rest/src/test/java/org/apache/ignite/rest/presentation/json/TestConfigurationStorage.java
@@ -0,0 +1,63 @@
+/*
+ * 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.rest.presentation.json;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.configuration.storage.ConfigurationStorage;
+import org.apache.ignite.configuration.storage.ConfigurationStorageListener;
+import org.apache.ignite.configuration.storage.Data;
+import org.apache.ignite.configuration.storage.StorageException;
+
+/** */
+public class TestConfigurationStorage implements ConfigurationStorage {
+ /** */
+ private final Set<ConfigurationStorageListener> listeners = new HashSet<>();
+
+ /** {@inheritDoc} */
+ @Override public Data readAll() throws StorageException {
+ return new Data(Collections.emptyMap(), 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public CompletableFuture<Boolean> write(Map<String, Serializable> newValues, long version) {
+ for (ConfigurationStorageListener listener : listeners)
+ listener.onEntriesChanged(new Data(newValues, version + 1));
+
+ return CompletableFuture.completedFuture(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Set<String> keys() throws StorageException {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addListener(ConfigurationStorageListener listener) {
+ listeners.add(listener);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeListener(ConfigurationStorageListener listener) {
+ listeners.remove(listener);
+ }
+}
diff --git a/modules/runner/src/main/java/org/apache/ignite/configuration/ConfigurationModule.java b/modules/runner/src/main/java/org/apache/ignite/configuration/ConfigurationModule.java
index 1847d6f..d6c9195 100644
--- a/modules/runner/src/main/java/org/apache/ignite/configuration/ConfigurationModule.java
+++ b/modules/runner/src/main/java/org/apache/ignite/configuration/ConfigurationModule.java
@@ -23,7 +23,7 @@ import java.io.Reader;
* Module is responsible for preparing configuration when module is started.
*
* Preparing configuration includes reading it from configuration file, parsing it and initializing
- * {@link Configurator} object.
+ * {@link ConfigurationRegistry} object.
*/
public class ConfigurationModule {
// /** */