Allow list-type config to be POSTed to subjectkey/subject/configkey endpoint.
Also add validation that the given JSON node is appropriate for the config
type (list vs object).
Change-Id: Ib1c12b538860a6f18b8311c5f5a786608c04beb8
diff --git a/core/store/dist/src/main/java/org/onosproject/store/config/impl/DistributedNetworkConfigStore.java b/core/store/dist/src/main/java/org/onosproject/store/config/impl/DistributedNetworkConfigStore.java
index 5397f54..c3c2bb6 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/config/impl/DistributedNetworkConfigStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/config/impl/DistributedNetworkConfigStore.java
@@ -61,7 +61,11 @@
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
-import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
/**
* Implementation of a distributed network configuration store.
@@ -77,6 +81,10 @@
private static final int MAX_BACKOFF = 10;
private static final String INVALID_CONFIG_JSON =
"JSON node does not contain valid configuration";
+ private static final String INVALID_JSON_LIST =
+ "JSON node is not a list for list type config";
+ private static final String INVALID_JSON_OBJECT =
+ "JSON node is not an object for object type config";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
@@ -262,14 +270,37 @@
* @return config object or null of no factory found or if the specified
* JSON is null
*/
- @SuppressWarnings("unchecked")
private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
JsonNode json) {
+ return createConfig(subject, configClass, json, false);
+ }
+
+ /**
+ * Produces a config from the specified subject, config class and raw JSON.
+ *
+ * The config can optionally be detached, which means it does not contain a
+ * reference to an apply delegate. This means a detached config can not be
+ * applied. This should be used only for passing the config object in the
+ * NetworkConfigEvent.
+ *
+ * @param subject config subject
+ * @param configClass config class
+ * @param json raw JSON data
+ * @param detached whether the config should be detached, that is, should
+ * be created without setting an apply delegate.
+ * @return config object or null of no factory found or if the specified
+ * JSON is null
+ */
+ @SuppressWarnings("unchecked")
+ private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
+ JsonNode json, boolean detached) {
if (json != null) {
ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
if (factory != null) {
+ validateJsonType(json, factory);
C config = factory.createConfig();
- config.init(subject, factory.configKey(), json, mapper, applyDelegate);
+ config.init(subject, factory.configKey(), json, mapper,
+ detached ? null : applyDelegate);
return config;
}
}
@@ -277,30 +308,27 @@
}
/**
- * Produces a detached config from the specified subject, config class and
- * raw JSON.
+ * Validates that the type of the JSON node is appropriate for the type of
+ * configuration. A list type configuration must be created with an
+ * ArrayNode, and an object type configuration must be created with an
+ * ObjectNode.
*
- * A detached config can no longer be applied. This should be used only for
- * passing the config object in the NetworkConfigEvent.
- *
- * @param subject config subject
- * @param configClass config class
- * @param json raw JSON data
- * @return config object or null of no factory found or if the specified
- * JSON is null
+ * @param json JSON node to check
+ * @param factory config factory of configuration
+ * @param <S> subject
+ * @param <C> configuration
+ * @return true if the JSON node type is appropriate for the configuration
*/
- @SuppressWarnings("unchecked")
- private Config createDetachedConfig(Object subject,
- Class configClass, JsonNode json) {
- if (json != null) {
- ConfigFactory factory = factoriesByConfig.get(configClass.getName());
- if (factory != null) {
- Config config = factory.createConfig();
- config.init(subject, factory.configKey(), json, mapper, null);
- return config;
- }
+ private <S, C extends Config<S>> boolean validateJsonType(JsonNode json,
+ ConfigFactory<S, C> factory) {
+ if (factory.isList() && !(json instanceof ArrayNode)) {
+ throw new IllegalArgumentException(INVALID_JSON_LIST);
}
- return null;
+ if (!factory.isList() && !(json instanceof ObjectNode)) {
+ throw new IllegalArgumentException(INVALID_JSON_OBJECT);
+ }
+
+ return true;
}
@@ -380,11 +408,11 @@
Versioned<JsonNode> oldValue = event.oldValue();
Config config = (newValue != null) ?
- createDetachedConfig(subject, configClass, newValue.value()) :
- null;
+ createConfig(subject, configClass, newValue.value(), true) :
+ null;
Config prevConfig = (oldValue != null) ?
- createDetachedConfig(subject, configClass, oldValue.value()) :
- null;
+ createConfig(subject, configClass, oldValue.value(), true) :
+ null;
NetworkConfigEvent.Type type;
switch (event.type()) {
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
index 9e301f5..8c073ad 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
@@ -15,9 +15,12 @@
*/
package org.onosproject.rest.resources;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.SubjectFactory;
+import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -28,13 +31,9 @@
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.onosproject.net.config.Config;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.SubjectFactory;
-import org.onosproject.rest.AbstractWebResource;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
import static org.onlab.util.Tools.emptyIsNotFound;
import static org.onlab.util.Tools.nullIsNotFound;
@@ -263,7 +262,7 @@
@PathParam("configKey") String configKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
- ObjectNode root = (ObjectNode) mapper().readTree(request);
+ JsonNode root = mapper().readTree(request);
service.applyConfig(subjectClassKey,
service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
configKey, root);