Added Json to CompositeNode translation 36/2736/2
authormsunal <msunal@cisco.com>
Thu, 14 Nov 2013 13:50:51 +0000 (14:50 +0100)
committermsunal <msunal@cisco.com>
Thu, 14 Nov 2013 16:29:50 +0000 (17:29 +0100)
- 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 <msunal@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/UnsupportedJsonFormatException.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java [new file with mode: 0644]

index 04556bbe547a8d22acd9d8584be1ff4386c1ed5f..5a1b42fd8009d074613407088c94359bac5dc6d9 100644 (file)
@@ -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<ListSchemaNode> 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> dataSchemaNode) {
+    private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> 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 (file)
index 0000000..fa00908
--- /dev/null
@@ -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<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
+        if (entrySetsOfRootJsonObject.size() != 1) {
+            throw new UnsupportedJsonFormatException("Json Object should contain one element");
+        } else {
+            Entry<String, JsonElement> 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<String, JsonElement> 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<String, JsonElement> 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);
+        }
+    }
+}
index cc6d449523671cfdca429ea1bb31779726a57b9c..daaedd92b8d6c85f05f26365b90f6bbe68f7d5d6 100644 (file)
@@ -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<CompositeNode> {
     INSTANCE;
-
+    
     @Override
     public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
         return true;
@@ -31,8 +32,13 @@ public enum JsonToCompositeNodeProvider implements MessageBodyReader<CompositeNo
     public CompositeNode readFrom(Class<CompositeNode> type, Type genericType, Annotation[] annotations,
             MediaType mediaType, MultivaluedMap<String, String> 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());
+        }
     }
 
 }
index b0e1e03c6c2715c0b99f271619d387f721d39884..4a851a3f6ca531f6042b9d20df7f4d9ba3cd3517 100644 (file)
@@ -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<Structured
     public void writeTo(StructuredData t, Class<?> type, Type genericType, Annotation[] annotations,
             MediaType mediaType, MultivaluedMap<String, Object> 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();
     }
-    
+
 }
index 0bce2e2ae0f87843e33ccb162da5186a40fd7b33..9f41b571dade2dc5eb806e350a6e229d17598ded 100644 (file)
@@ -51,8 +51,11 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredD
             MediaType mediaType, MultivaluedMap<String, Object> 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<StructuredD
             transformer.transform(new DOMSource(domTree), new StreamResult(entityStream));
         } catch (TransformerException e) {
             logger.error("Error during translation of Document to OutputStream", e);
-            new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
+            throw new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
         }
     }
 
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/UnsupportedJsonFormatException.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/UnsupportedJsonFormatException.java
new file mode 100644 (file)
index 0000000..dccf29b
--- /dev/null
@@ -0,0 +1,23 @@
+package org.opendaylight.controller.sal.rest.impl;
+
+public class UnsupportedJsonFormatException extends Exception {
+
+    private static final long serialVersionUID = -1741388894406313402L;
+
+    public UnsupportedJsonFormatException() {
+        super();
+    }
+
+    public UnsupportedJsonFormatException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UnsupportedJsonFormatException(String message) {
+        super(message);
+    }
+
+    public UnsupportedJsonFormatException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java
new file mode 100644 (file)
index 0000000..da07bba
--- /dev/null
@@ -0,0 +1,220 @@
+package org.opendaylight.controller.sal.restconf.impl;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.MutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+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 CompositeNodeWrapper implements NodeWrapper<CompositeNode>, CompositeNode {
+
+    private MutableCompositeNode compositeNode;
+
+    private String localName;
+    private URI namespace;
+    private List<NodeWrapper<?>> 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<CompositeNode> value) {
+        Preconditions.checkState(compositeNode == null, "Cannot change the object, due to data inconsistencies.");
+        values.remove(value);
+    }
+    
+    public List<NodeWrapper<?>> 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<Node<?>>(), ModifyAction.CREATE, null);
+            
+            List<Node<?>> 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<Node<?>> getValue() {
+        return unwrap(null).getValue();
+    }
+
+    @Override
+    public ModifyAction getModificationAction() {
+        return unwrap(null).getModificationAction();
+    }
+
+    @Override
+    public List<Node<?>> getChildren() {
+        return unwrap(null).getChildren();
+    }
+
+    @Override
+    public List<CompositeNode> getCompositesByName(QName children) {
+        return unwrap(null).getCompositesByName(children);
+    }
+
+    @Override
+    public List<CompositeNode> getCompositesByName(String children) {
+        return unwrap(null).getCompositesByName(children);
+    }
+
+    @Override
+    public List<SimpleNode<?>> getSimpleNodesByName(QName children) {
+        return unwrap(null).getSimpleNodesByName(children);
+    }
+
+    @Override
+    public List<SimpleNode<?>> 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<Node<?>> setValue(List<Node<?>> 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<Node<?>> get(Object key) {
+        return unwrap(null).get(key);
+    }
+
+    @Override
+    public List<Node<?>> put(QName key, List<Node<?>> value) {
+        return unwrap(null).put(key, value);
+    }
+
+    @Override
+    public List<Node<?>> remove(Object key) {
+        return unwrap(null).remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends QName, ? extends List<Node<?>>> m) {
+        unwrap(null).putAll(m);
+    }
+
+    @Override
+    public void clear() {
+        unwrap(null).clear();
+    }
+
+    @Override
+    public Set<QName> keySet() {
+        return unwrap(null).keySet();
+    }
+
+    @Override
+    public Collection<List<Node<?>>> values() {
+        return unwrap(null).values();
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<QName, List<Node<?>>>> entrySet() {
+        return unwrap(null).entrySet();
+    }
+
+}
index 400850103d7be35f72beeb39088a1618a23feff5..624178569d4e71551507f7733cec7c93bf4b032c 100644 (file)
@@ -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 (file)
index 0000000..0a3616e
--- /dev/null
@@ -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 extends Node<?>> {
+
+    T unwrap(CompositeNode parent);
+    
+    URI getNamespace();
+
+    void setNamespace(URI namespace);
+    
+    String getLocalName();
+}
\ No newline at end of file
index 31fbe8ec876876b0d677ffaa90df95fddbdfa91a..ea3a4fbcfcbae90e848c39e505008dd701613b26 100644 (file)
@@ -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<NodeWrapper<?>> 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 (file)
index 0000000..50b6ac7
--- /dev/null
@@ -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<?>>, SimpleNode<String> {
+    
+    private SimpleNode<String> 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<String> 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<String> asMutable() {
+        return unwrap(null).asMutable();
+    }
+
+    @Override
+    public QName getKey() {
+        return unwrap(null).getKey();
+    }
+
+    @Override
+    public String setValue(String value) {
+        return unwrap(null).setValue(value);
+    }
+
+
+}