From 1c2a0d378926c94951387932f5b98fc35b22fa66 Mon Sep 17 00:00:00 2001 From: msunal Date: Thu, 14 Nov 2013 14:50:51 +0100 Subject: [PATCH] Added Json to CompositeNode translation - added JsonReader.java to provide translation from Json (InputStream) to CompositeNode - Json has to be in one of these two formats: {"foo":{....}} for Container foo {"foo":[{....}]} for List foo - added preconditions to translation from Node to Json: Data of Container and List has to be represented as CompositeNode Data of LeafList and Leaf has to be represented as SimpleNode - request for unexisting data has response - not found 404 Change-Id: I9f2a07a0d333b09803606e0e4275bdf39d2cdce5 Signed-off-by: Martin Sunal --- .../controller/sal/rest/impl/JsonMapper.java | 25 +- .../controller/sal/rest/impl/JsonReader.java | 81 +++++++ .../impl/JsonToCompositeNodeProvider.java | 12 +- .../impl/StructuredDataToJsonProvider.java | 11 +- .../impl/StructuredDataToXmlProvider.java | 7 +- .../impl/UnsupportedJsonFormatException.java | 23 ++ .../restconf/impl/CompositeNodeWrapper.java | 220 ++++++++++++++++++ .../sal/restconf/impl/ControllerContext.xtend | 4 + .../sal/restconf/impl/NodeWrapper.java | 17 ++ .../sal/restconf/impl/RestconfImpl.xtend | 41 +++- .../sal/restconf/impl/SimpleNodeWrapper.java | 98 ++++++++ 11 files changed, 520 insertions(+), 19 deletions(-) create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/UnsupportedJsonFormatException.java create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java index 04556bbe54..5a1b42fd80 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java @@ -26,6 +26,7 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition; +import com.google.common.base.Preconditions; import com.google.gson.stream.JsonWriter; class JsonMapper { @@ -34,12 +35,16 @@ class JsonMapper { private final Set foundLists = new HashSet<>(); public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException { + Preconditions.checkNotNull(writer); + Preconditions.checkNotNull(data); + Preconditions.checkNotNull(schema); + writer.beginObject(); if (schema instanceof ContainerSchemaNode) { - writeContainer(writer, (CompositeNode) data, (ContainerSchemaNode) schema); + writeContainer(writer, data, (ContainerSchemaNode) schema); } else if (schema instanceof ListSchemaNode) { - writeList(writer, (CompositeNode) data, (ListSchemaNode) schema); + writeList(writer, data, (ListSchemaNode) schema); } else { throw new UnsupportedDataTypeException( "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); @@ -56,25 +61,33 @@ class JsonMapper { checkNotNull(parentSchema); for (Node child : parent.getChildren()) { - DataSchemaNode childSchema = findSchemaForNode(child, parentSchema.getChildNodes()); + DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); if (childSchema == null) { throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName() + "\" is not conform to schema"); } if (childSchema instanceof ContainerSchemaNode) { + Preconditions.checkState(child instanceof CompositeNode, + "Data representation of Container should be CompositeNode - " + child.getNodeType()); writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema); } else if (childSchema instanceof ListSchemaNode) { if (!foundLists.contains(childSchema)) { + Preconditions.checkState(child instanceof CompositeNode, + "Data representation of List should be CompositeNode - " + child.getNodeType()); foundLists.add((ListSchemaNode) childSchema); writeList(writer, (CompositeNode) child, (ListSchemaNode) childSchema); } } else if (childSchema instanceof LeafListSchemaNode) { if (!foundLeafLists.contains(childSchema)) { + Preconditions.checkState(child instanceof SimpleNode, + "Data representation of LeafList should be SimpleNode - " + child.getNodeType()); foundLeafLists.add((LeafListSchemaNode) childSchema); writeLeafList(writer, (SimpleNode) child, (LeafListSchemaNode) childSchema); } } else if (childSchema instanceof LeafSchemaNode) { + Preconditions.checkState(child instanceof SimpleNode, + "Data representation of LeafList should be SimpleNode - " + child.getNodeType()); writeLeaf(writer, (SimpleNode) child, (LeafSchemaNode) childSchema); } else { throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, " @@ -83,7 +96,7 @@ class JsonMapper { } for (Node child : parent.getChildren()) { - DataSchemaNode childSchema = findSchemaForNode(child, parentSchema.getChildNodes()); + DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); if (childSchema instanceof LeafListSchemaNode) { foundLeafLists.remove((LeafListSchemaNode) childSchema); } else if (childSchema instanceof ListSchemaNode) { @@ -92,7 +105,7 @@ class JsonMapper { } } - private DataSchemaNode findSchemaForNode(Node node, Set dataSchemaNode) { + private DataSchemaNode findFirstSchemaForNode(Node node, Set dataSchemaNode) { for (DataSchemaNode dsn : dataSchemaNode) { if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) { return dsn; @@ -169,7 +182,7 @@ class JsonMapper { writer.nullValue(); writer.endArray(); } else { - writer.value(value); + writer.value(value != null ? value : ""); } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java new file mode 100644 index 0000000000..fa00908e4d --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java @@ -0,0 +1,81 @@ +package org.opendaylight.controller.sal.rest.impl; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Map.Entry; +import java.util.Set; + +import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; +import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; + +import com.google.common.collect.Lists; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +class JsonReader { + + public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedJsonFormatException { + JsonParser parser = new JsonParser(); + + JsonElement rootElement = parser.parse(new InputStreamReader(entityStream)); + if (!rootElement.isJsonObject()) { + throw new UnsupportedJsonFormatException("Root element of Json has to be Object"); + } + + Set> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet(); + if (entrySetsOfRootJsonObject.size() != 1) { + throw new UnsupportedJsonFormatException("Json Object should contain one element"); + } else { + Entry childEntry = Lists.newArrayList(entrySetsOfRootJsonObject).get(0); + String firstElementName = childEntry.getKey(); + JsonElement firstElementType = childEntry.getValue(); + if (firstElementType.isJsonObject()) { // container in yang + return createStructureWithRoot(firstElementName, firstElementType.getAsJsonObject()); + } + if (firstElementType.isJsonArray()) { // list in yang + if (firstElementType.getAsJsonArray().size() == 1) { + JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0); + if (firstElementInArray.isJsonObject()) { + return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject()); + } + throw new UnsupportedJsonFormatException("Array as the first element in Json Object can have only Object element"); + } + } + throw new UnsupportedJsonFormatException("First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."); + } + } + + private CompositeNodeWrapper createStructureWithRoot(String rootObjectName, JsonObject rootObject) { + CompositeNodeWrapper firstNode = new CompositeNodeWrapper(rootObjectName); + for (Entry childOfFirstNode : rootObject.entrySet()) { + addChildToParent(childOfFirstNode.getKey(), childOfFirstNode.getValue(), firstNode); + } + return firstNode; + } + + private void addChildToParent(String childName, JsonElement childType, CompositeNodeWrapper parent) { + if (childType.isJsonObject()) { + CompositeNodeWrapper child = new CompositeNodeWrapper(childName); + parent.addValue(child); + for (Entry childOfChild : childType.getAsJsonObject().entrySet()) { + addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child); + } + } else if (childType.isJsonArray()) { + for (JsonElement childOfChildType : childType.getAsJsonArray()) { + addChildToParent(childName, childOfChildType, parent); + } + } else if (childType.isJsonPrimitive()) { + JsonPrimitive childPrimitive = childType.getAsJsonPrimitive(); + String value = childPrimitive.getAsString(); + SimpleNodeWrapper child = null; + if (value.equals("[null]")) { + child = new SimpleNodeWrapper(childName, null); + } else { + child = new SimpleNodeWrapper(childName, value); + } + parent.addValue(child); + } + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java index cc6d449523..daaedd92b8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java @@ -11,6 +11,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; @@ -21,7 +22,7 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; @Consumes({API+RestconfService.JSON}) public enum JsonToCompositeNodeProvider implements MessageBodyReader { INSTANCE; - + @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return true; @@ -31,8 +32,13 @@ public enum JsonToCompositeNodeProvider implements MessageBodyReader type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { - // TODO Auto-generated method stub - return null; + JsonReader jsonReader = new JsonReader(); + try { + return jsonReader.read(entityStream); + } catch (UnsupportedJsonFormatException e) { + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity(e.getMessage()).build()); + } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java index b0e1e03c6c..4a851a3f6c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java @@ -12,11 +12,13 @@ import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import org.opendaylight.controller.sal.rest.api.RestconfService; import org.opendaylight.controller.sal.restconf.impl.StructuredData; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import com.google.gson.stream.JsonWriter; @@ -40,11 +42,16 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + CompositeNode data = t.getData(); + if (data == null) { + throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); + } + JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8")); writer.setIndent(" "); JsonMapper jsonMapper = new JsonMapper(); - jsonMapper.write(writer, t.getData(), (DataNodeContainer) t.getSchema()); + jsonMapper.write(writer, data, (DataNodeContainer) t.getSchema()); writer.flush(); } - + } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java index 0bce2e2ae0..9f41b571da 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java @@ -51,8 +51,11 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { CompositeNode data = t.getData(); - Document domTree = NodeUtils.buildShadowDomTree(data); + if (data == null) { + throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); + } + Document domTree = NodeUtils.buildShadowDomTree(data); try { TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); @@ -64,7 +67,7 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter, CompositeNode { + + private MutableCompositeNode compositeNode; + + private String localName; + private URI namespace; + private List> values = new ArrayList<>(); + + public CompositeNodeWrapper(String localName) { + this.localName = Preconditions.checkNotNull(localName); + } + + @Override + public String getLocalName() { + if (compositeNode != null) { + return compositeNode.getNodeType().getLocalName(); + } + return localName; + } + + @Override + public URI getNamespace() { + if (compositeNode != null) { + return compositeNode.getNodeType().getNamespace(); + } + return namespace; + } + + @Override + public void setNamespace(URI namespace) { + Preconditions.checkState(compositeNode == null, "Cannot change the object, due to data inconsistencies."); + this.namespace = namespace; + } + + public void addValue(NodeWrapper value) { + Preconditions.checkState(compositeNode == null, "Cannot change the object, due to data inconsistencies."); + values.add(value); + } + + public void removeValue(NodeWrapper value) { + Preconditions.checkState(compositeNode == null, "Cannot change the object, due to data inconsistencies."); + values.remove(value); + } + + public List> getValues() { + Preconditions.checkState(compositeNode == null, "Data can be inconsistent."); + return Collections.unmodifiableList(values); + } + + @Override + public CompositeNode unwrap(CompositeNode parent) { + if (compositeNode == null) { + Preconditions.checkNotNull(namespace); + compositeNode = NodeFactory.createMutableCompositeNode(new QName(namespace, localName), + parent, new ArrayList>(), ModifyAction.CREATE, null); + + List> nodeValues = new ArrayList<>(); + for (NodeWrapper nodeWrapper : values) { + nodeValues.add(nodeWrapper.unwrap(compositeNode)); + } + compositeNode.setValue(nodeValues); + + values = null; + namespace = null; + localName = null; + } + return compositeNode; + } + + @Override + public QName getNodeType() { + return unwrap(null).getNodeType(); + } + + @Override + public CompositeNode getParent() { + return unwrap(null).getParent(); + } + + @Override + public List> getValue() { + return unwrap(null).getValue(); + } + + @Override + public ModifyAction getModificationAction() { + return unwrap(null).getModificationAction(); + } + + @Override + public List> getChildren() { + return unwrap(null).getChildren(); + } + + @Override + public List getCompositesByName(QName children) { + return unwrap(null).getCompositesByName(children); + } + + @Override + public List getCompositesByName(String children) { + return unwrap(null).getCompositesByName(children); + } + + @Override + public List> getSimpleNodesByName(QName children) { + return unwrap(null).getSimpleNodesByName(children); + } + + @Override + public List> getSimpleNodesByName(String children) { + return unwrap(null).getSimpleNodesByName(children); + } + + @Override + public CompositeNode getFirstCompositeByName(QName container) { + return unwrap(null).getFirstCompositeByName(container); + } + + @Override + public SimpleNode getFirstSimpleByName(QName leaf) { + return unwrap(null).getFirstSimpleByName(leaf); + } + + @Override + public MutableCompositeNode asMutable() { + return unwrap(null).asMutable(); + } + + @Override + public QName getKey() { + return unwrap(null).getKey(); + } + + @Override + public List> setValue(List> value) { + return unwrap(null).setValue(value); + } + + @Override + public int size() { + return unwrap(null).size(); + } + + @Override + public boolean isEmpty() { + return unwrap(null).isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return unwrap(null).containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return unwrap(null).containsValue(value); + } + + @Override + public List> get(Object key) { + return unwrap(null).get(key); + } + + @Override + public List> put(QName key, List> value) { + return unwrap(null).put(key, value); + } + + @Override + public List> remove(Object key) { + return unwrap(null).remove(key); + } + + @Override + public void putAll(Map>> m) { + unwrap(null).putAll(m); + } + + @Override + public void clear() { + unwrap(null).clear(); + } + + @Override + public Set keySet() { + return unwrap(null).keySet(); + } + + @Override + public Collection>> values() { + return unwrap(null).values(); + } + + @Override + public Set>>> entrySet() { + return unwrap(null).entrySet(); + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend index 400850103d..624178569d 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend @@ -294,5 +294,9 @@ class ControllerContext implements SchemaServiceListener { def ContainerSchemaNode getRpcOutputSchema(QName name) { qnameToRpc.get(name)?.output; } + + def ContainerSchemaNode getRpcInputSchema(QName name) { + qnameToRpc.get(name)?.input; + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java new file mode 100644 index 0000000000..0a3616e494 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java @@ -0,0 +1,17 @@ +package org.opendaylight.controller.sal.restconf.impl; + +import java.net.URI; + +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; + +public interface NodeWrapper> { + + T unwrap(CompositeNode parent); + + URI getNamespace(); + + void setNamespace(URI namespace); + + String getLocalName(); +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend index 31fbe8ec87..ea3a4fbcfc 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend @@ -1,7 +1,10 @@ package org.opendaylight.controller.sal.restconf.impl -import org.opendaylight.yangtools.yang.data.api.CompositeNode +import java.util.List import org.opendaylight.controller.sal.rest.api.RestconfService +import org.opendaylight.yangtools.yang.data.api.CompositeNode +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode class RestconfImpl implements RestconfService { @@ -44,17 +47,20 @@ class RestconfImpl implements RestconfService { override createConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.toInstanceIdentifier - return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,payload).get(); + val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); } override updateConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.toInstanceIdentifier - return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,payload).get(); + val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); } override invokeRpc(String identifier, CompositeNode payload) { val rpc = identifier.toQName; - val rpcResult = broker.invokeRpc(rpc, payload); + val value = resolveNodeNamespaceBySchema(payload, controllerContext.getRpcInputSchema(rpc)) + val rpcResult = broker.invokeRpc(rpc, value); val schema = controllerContext.getRpcOutputSchema(rpc); return new StructuredData(rpcResult.result, schema); } @@ -81,12 +87,35 @@ class RestconfImpl implements RestconfService { override createOperationalData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.toInstanceIdentifier - return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,payload).get(); + val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); } override updateOperationalData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.toInstanceIdentifier - return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,payload).get(); + val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); + } + + private def CompositeNode resolveNodeNamespaceBySchema(CompositeNode node, DataSchemaNode schema) { + if (node instanceof CompositeNodeWrapper) { + addNamespaceToNodeFromSchemaRecursively(node as CompositeNodeWrapper, schema) + return (node as CompositeNodeWrapper).unwrap(null) + } + return node + } + + private def void addNamespaceToNodeFromSchemaRecursively(NodeWrapper nodeBuilder, DataSchemaNode schema) { + if (nodeBuilder.namespace == null) { + nodeBuilder.namespace = schema.QName.namespace + } + if (nodeBuilder instanceof CompositeNodeWrapper) { + val List> children = (nodeBuilder as CompositeNodeWrapper).getValues + for (child : children) { + addNamespaceToNodeFromSchemaRecursively(child, + (schema as DataNodeContainer).childNodes.findFirst[n|n.QName.localName === child.localName]) + } + } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java new file mode 100644 index 0000000000..50b6ac77e6 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java @@ -0,0 +1,98 @@ +package org.opendaylight.controller.sal.restconf.impl; + +import java.net.URI; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.NodeFactory; + +import com.google.common.base.Preconditions; + +public final class SimpleNodeWrapper implements NodeWrapper>, SimpleNode { + + private SimpleNode simpleNode; + + private String localName; + private String value; + private URI namespace; + + public SimpleNodeWrapper(String localName, String value) { + this.localName = Preconditions.checkNotNull(localName); + this.value = value; + } + + @Override + public String getLocalName() { + if (simpleNode != null) { + return simpleNode.getNodeType().getLocalName(); + } + return localName; + } + + @Override + public URI getNamespace() { + if (simpleNode != null) { + return simpleNode.getNodeType().getNamespace(); + } + return namespace; + } + + @Override + public void setNamespace(URI namespace) { + Preconditions.checkState(simpleNode == null, "Cannot change the object, due to data inconsistencies."); + this.namespace = namespace; + } + + @Override + public SimpleNode unwrap(CompositeNode parent) { + if (simpleNode == null) { + Preconditions.checkNotNull(namespace); + simpleNode = NodeFactory.createImmutableSimpleNode(new QName(namespace, localName), parent, value); + + value = null; + namespace = null; + localName = null; + } + return simpleNode; + } + + @Override + public QName getNodeType() { + return unwrap(null).getNodeType(); + } + + @Override + public CompositeNode getParent() { + return unwrap(null).getParent(); + } + + @Override + public String getValue() { + return unwrap(null).getValue(); + } + + @Override + public ModifyAction getModificationAction() { + return unwrap(null).getModificationAction(); + } + + @Override + public MutableSimpleNode asMutable() { + return unwrap(null).asMutable(); + } + + @Override + public QName getKey() { + return unwrap(null).getKey(); + } + + @Override + public String setValue(String value) { + return unwrap(null).setValue(value); + } + + +} -- 2.36.6