Thomas Vachuska
Committed by Gerrit Code Review

Adding ability for Config to be backed by generic JsonNode, i.e. either ObjectNode or ArrayNode.

Change-Id: I5f9ec423cd5f23f61c97a57073d9d11071c47997
......@@ -46,7 +46,7 @@ public class BgpConfig extends Config<ApplicationId> {
public Set<BgpSpeakerConfig> bgpSpeakers() {
Set<BgpSpeakerConfig> speakers = Sets.newHashSet();
JsonNode speakersNode = node.get(SPEAKERS);
JsonNode speakersNode = object.get(SPEAKERS);
speakersNode.forEach(jsonNode -> {
Set<IpAddress> listenAddresses = Sets.newHashSet();
jsonNode.path(PEERS).forEach(addressNode ->
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.cli.cfg;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Argument;
......@@ -51,17 +52,17 @@ public class NetworkConfigCommand extends AbstractShellCommand {
@Override
protected void execute() {
service = get(NetworkConfigService.class);
ObjectNode root = new ObjectMapper().createObjectNode();
JsonNode root = new ObjectMapper().createObjectNode();
if (isNullOrEmpty(subjectKey)) {
addAll(root);
addAll((ObjectNode) root);
} else {
SubjectFactory subjectFactory = service.getSubjectFactory(subjectKey);
if (isNullOrEmpty(subject)) {
addSubjectClass(root, subjectFactory);
addSubjectClass((ObjectNode) root, subjectFactory);
} else {
Object s = subjectFactory.createSubject(subject);
if (isNullOrEmpty(configKey)) {
addSubject(root, s);
addSubject((ObjectNode) root, s);
} else {
root = getSubjectConfig(getConfig(s, subjectKey, configKey));
}
......@@ -89,7 +90,7 @@ public class NetworkConfigCommand extends AbstractShellCommand {
service.getConfigs(s).forEach(c -> root.set(c.key(), c.node()));
}
private ObjectNode getSubjectConfig(Config config) {
private JsonNode getSubjectConfig(Config config) {
return config != null ? config.node() : null;
}
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.net.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
......@@ -41,8 +42,12 @@ public abstract class Config<S> {
protected S subject;
protected String key;
protected ObjectNode node;
protected JsonNode node;
protected ObjectNode object;
protected ArrayNode array;
protected ObjectMapper mapper;
protected ConfigApplyDelegate delegate;
/**
......@@ -50,15 +55,17 @@ public abstract class Config<S> {
*
* @param subject configuration subject
* @param key configuration key
* @param node JSON object node where configuration data is stored
* @param node JSON node where configuration data is stored
* @param mapper JSON object mapper
* @param delegate delegate context
*/
public void init(S subject, String key, ObjectNode node, ObjectMapper mapper,
public void init(S subject, String key, JsonNode node, ObjectMapper mapper,
ConfigApplyDelegate delegate) {
this.subject = checkNotNull(subject);
this.key = key;
this.node = checkNotNull(node);
this.object = node instanceof ObjectNode ? (ObjectNode) node : null;
this.array = node instanceof ArrayNode ? (ArrayNode) node : null;
this.mapper = checkNotNull(mapper);
this.delegate = checkNotNull(delegate);
}
......@@ -88,8 +95,8 @@ public abstract class Config<S> {
*
* @return JSON node backing the configuration
*/
public ObjectNode node() {
return node;
public JsonNode node() {
return object;
}
/**
......@@ -110,7 +117,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected String get(String name, String defaultValue) {
return node.path(name).asText(defaultValue);
return object.path(name).asText(defaultValue);
}
/**
......@@ -122,9 +129,9 @@ public abstract class Config<S> {
*/
protected Config<S> setOrClear(String name, String value) {
if (value != null) {
node.put(name, value);
object.put(name, value);
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -137,7 +144,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected boolean get(String name, boolean defaultValue) {
return node.path(name).asBoolean(defaultValue);
return object.path(name).asBoolean(defaultValue);
}
/**
......@@ -149,9 +156,9 @@ public abstract class Config<S> {
*/
protected Config<S> setOrClear(String name, Boolean value) {
if (value != null) {
node.put(name, value.booleanValue());
object.put(name, value.booleanValue());
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -164,7 +171,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected int get(String name, int defaultValue) {
return node.path(name).asInt(defaultValue);
return object.path(name).asInt(defaultValue);
}
/**
......@@ -176,9 +183,9 @@ public abstract class Config<S> {
*/
protected Config<S> setOrClear(String name, Integer value) {
if (value != null) {
node.put(name, value.intValue());
object.put(name, value.intValue());
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -191,7 +198,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected long get(String name, long defaultValue) {
return node.path(name).asLong(defaultValue);
return object.path(name).asLong(defaultValue);
}
/**
......@@ -203,9 +210,9 @@ public abstract class Config<S> {
*/
protected Config<S> setOrClear(String name, Long value) {
if (value != null) {
node.put(name, value.longValue());
object.put(name, value.longValue());
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -218,7 +225,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected double get(String name, double defaultValue) {
return node.path(name).asDouble(defaultValue);
return object.path(name).asDouble(defaultValue);
}
/**
......@@ -230,9 +237,9 @@ public abstract class Config<S> {
*/
protected Config<S> setOrClear(String name, Double value) {
if (value != null) {
node.put(name, value.doubleValue());
object.put(name, value.doubleValue());
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -247,7 +254,7 @@ public abstract class Config<S> {
* @return property value or default value
*/
protected <E extends Enum<E>> E get(String name, E defaultValue, Class<E> enumClass) {
return Enum.valueOf(enumClass, node.path(name).asText(defaultValue.toString()));
return Enum.valueOf(enumClass, object.path(name).asText(defaultValue.toString()));
}
/**
......@@ -260,9 +267,9 @@ public abstract class Config<S> {
*/
protected <E extends Enum> Config<S> setOrClear(String name, E value) {
if (value != null) {
node.put(name, value.toString());
object.put(name, value.toString());
} else {
node.remove(name);
object.remove(name);
}
return this;
}
......@@ -277,7 +284,7 @@ public abstract class Config<S> {
*/
protected <T> List<T> getList(String name, Function<String, T> function) {
List<T> list = Lists.newArrayList();
ArrayNode arrayNode = (ArrayNode) node.path(name);
ArrayNode arrayNode = (ArrayNode) object.path(name);
arrayNode.forEach(i -> list.add(function.apply(i.asText())));
return list;
}
......@@ -293,11 +300,11 @@ public abstract class Config<S> {
*/
protected <T> Config<S> setOrClear(String name, Collection<T> collection) {
if (collection == null) {
node.remove(name);
object.remove(name);
} else {
ArrayNode arrayNode = mapper.createArrayNode();
collection.forEach(i -> arrayNode.add(i.toString()));
node.set(name, arrayNode);
object.set(name, arrayNode);
}
return this;
}
......
......@@ -30,6 +30,7 @@ public abstract class ConfigFactory<S, C extends Config<S>> {
private final SubjectFactory<S> subjectFactory;
private final Class<C> configClass;
private final String configKey;
private final boolean isList;
/**
* Creates a new configuration factory for the specified class of subjects
......@@ -38,14 +39,37 @@ public abstract class ConfigFactory<S, C extends Config<S>> {
* composite JSON trees.
*
* @param subjectFactory subject factory
* @param configClass configuration class
* @param configKey configuration class key
* @param configClass configuration class
* @param configKey configuration class key
*/
protected ConfigFactory(SubjectFactory<S> subjectFactory,
Class<C> configClass, String configKey) {
this(subjectFactory, configClass, configKey, false);
}
/**
* Creates a new configuration factory for the specified class of subjects
* capable of generating the configurations of the specified class. The
* subject and configuration class keys are used merely as keys for use in
* composite JSON trees.
* <p>
* Note that configurations backed by JSON array are not easily extensible
* at the top-level as they are inherently limited to holding an ordered
* list of items.
* </p>
*
* @param subjectFactory subject factory
* @param configClass configuration class
* @param configKey configuration class key
* @param isList true to indicate backing by JSON array
*/
protected ConfigFactory(SubjectFactory<S> subjectFactory,
Class<C> configClass, String configKey,
boolean isList) {
this.subjectFactory = subjectFactory;
this.configClass = configClass;
this.configKey = configKey;
this.isList = isList;
}
/**
......@@ -85,4 +109,14 @@ public abstract class ConfigFactory<S, C extends Config<S>> {
*/
public abstract C createConfig();
/**
* Indicates whether the configuration is a list and should be backed by
* a JSON array rather than JSON object.
*
* @return true if backed by JSON array
*/
public boolean isList() {
return isList;
}
}
......
......@@ -15,7 +15,7 @@
*/
package org.onosproject.net.config;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.annotations.Beta;
import org.onosproject.event.ListenerService;
......@@ -130,7 +130,7 @@ public interface NetworkConfigService
* @return configuration or null if one is not available
*/
<S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass,
ObjectNode json);
JsonNode json);
/**
* Clears any configuration for the specified subject and configuration
......
......@@ -15,7 +15,7 @@
*/
package org.onosproject.net.config;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import org.onosproject.store.Store;
import java.util.Set;
......@@ -115,7 +115,7 @@ public interface NetworkConfigStore extends Store<NetworkConfigEvent, NetworkCon
* @return configuration object
*/
<S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass,
ObjectNode json);
JsonNode json);
/**
* Clears the configuration of the given class for the specified subject.
......
......@@ -33,7 +33,7 @@ public class OpticalPortConfig extends Config<ConnectPoint> {
* @return the port type, or null if invalid or unset
*/
public Port.Type type() {
JsonNode type = node.path(TYPE);
JsonNode type = object.path(TYPE);
if (type.isMissingNode()) {
return null;
}
......@@ -72,7 +72,7 @@ public class OpticalPortConfig extends Config<ConnectPoint> {
}
private String getStringValue(String field) {
JsonNode name = node.path(field);
JsonNode name = object.path(field);
return name.isMissingNode() ? "" : name.asText();
}
......@@ -84,7 +84,7 @@ public class OpticalPortConfig extends Config<ConnectPoint> {
* @return an Optional that may contain a frequency value.
*/
public Optional<Long> staticLambda() {
JsonNode sl = node.path(STATIC_LAMBDA);
JsonNode sl = object.path(STATIC_LAMBDA);
if (sl.isMissingNode()) {
return Optional.empty();
}
......@@ -98,7 +98,7 @@ public class OpticalPortConfig extends Config<ConnectPoint> {
* @return a port speed value whose default is 0.
*/
public Optional<Integer> speed() {
JsonNode s = node.path(SPEED);
JsonNode s = object.path(SPEED);
if (s.isMissingNode()) {
return Optional.empty();
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
......@@ -14,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.config;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Set;
......@@ -71,7 +69,7 @@ public class NetworkConfigServiceAdapter implements NetworkConfigService {
}
@Override
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
return null;
}
......
......@@ -15,7 +15,7 @@
*/
package org.onosproject.net.config.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
......@@ -197,7 +197,7 @@ public class NetworkConfigManager
}
@Override
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
checkNotNull(subject, NULL_SUBJECT_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.applyConfig(subject, configClass, json);
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.store.config.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
......@@ -77,12 +78,12 @@ public class DistributedNetworkConfigStore
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
private ConsistentMap<ConfigKey, ObjectNode> configs;
private ConsistentMap<ConfigKey, JsonNode> configs;
private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
private final ObjectMapper mapper = new ObjectMapper();
private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
private final MapEventListener<ConfigKey, ObjectNode> listener = new InternalMapListener();
private final MapEventListener<ConfigKey, JsonNode> listener = new InternalMapListener();
@Activate
public void activate() {
......@@ -93,7 +94,7 @@ public class DistributedNetworkConfigStore
TextNode.class, BooleanNode.class,
LongNode.class, DoubleNode.class, ShortNode.class, IntNode.class);
configs = storageService.<ConfigKey, ObjectNode>consistentMapBuilder()
configs = storageService.<ConfigKey, JsonNode>consistentMapBuilder()
.withSerializer(Serializer.using(kryoBuilder.build()))
.withName("onos-network-configs")
.withRelaxedReadConsistency()
......@@ -168,21 +169,24 @@ public class DistributedNetworkConfigStore
@Override
public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
// TODO: need to identify and address the root cause for timeouts.
Versioned<ObjectNode> json = Tools.retryable(configs::get, ConsistentMapException.class, 1, MAX_BACKOFF)
.apply(key(subject, configClass));
Versioned<JsonNode> json = Tools.retryable(configs::get, ConsistentMapException.class, 1, MAX_BACKOFF)
.apply(key(subject, configClass));
return json != null ? createConfig(subject, configClass, json.value()) : null;
}
@Override
public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Versioned<ObjectNode> json = configs.computeIfAbsent(key(subject, configClass),
k -> mapper.createObjectNode());
ConfigFactory<S, C> factory = getConfigFactory(configClass);
Versioned<JsonNode> json = configs.computeIfAbsent(key(subject, configClass),
k -> factory.isList() ?
mapper.createArrayNode() :
mapper.createObjectNode());
return createConfig(subject, configClass, json.value());
}
@Override
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
return createConfig(subject, configClass,
configs.putAndGet(key(subject, configClass), json).value());
}
......@@ -203,7 +207,7 @@ public class DistributedNetworkConfigStore
*/
@SuppressWarnings("unchecked")
private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
ObjectNode json) {
JsonNode json) {
if (json != null) {
ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
if (factory != null) {
......@@ -259,9 +263,9 @@ public class DistributedNetworkConfigStore
}
}
private class InternalMapListener implements MapEventListener<ConfigKey, ObjectNode> {
private class InternalMapListener implements MapEventListener<ConfigKey, JsonNode> {
@Override
public void event(MapEvent<ConfigKey, ObjectNode> event) {
public void event(MapEvent<ConfigKey, JsonNode> event) {
NetworkConfigEvent.Type type;
switch (event.type()) {
case INSERT:
......
......@@ -50,7 +50,8 @@ public class InterfaceConfig extends Config<ConnectPoint> {
Set<Interface> interfaces = Sets.newHashSet();
try {
for (JsonNode intfNode : node.path(INTERFACES)) {
// TODO: rework this to take advantage of ArrayNode backing
for (JsonNode intfNode : object.path(INTERFACES)) {
Set<InterfaceIpAddress> ips = getIps(intfNode);
if (ips.isEmpty()) {
throw new ConfigException(IP_MISSING_ERROR);
......