Bug 3934: Websockets: Scope ONE doesn't work correctly 07/33607/1
authorMartin Ciglan <mciglan@cisco.com>
Tue, 19 Jan 2016 22:42:06 +0000 (23:42 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Wed, 27 Jan 2016 09:16:47 +0000 (09:16 +0000)
When processing an event which contains a capture of changes in a data subtree,
path and operation are captured to DOM Element, but data itself is ignored.

Change-Id: I11e6a5be5f5f4a025cf3edeb181140c573227a94
Signed-off-by: Martin Ciglan <mciglan@cisco.com>
(cherry picked from commit 898916bcccbae02956efa6175118c7521080a2d2)

opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java

index 802416d49421542e603bd4745fd32c78e26f113e..9599dbbd88f3f277dbfc0901c17c3cc7dd6ff712 100644 (file)
@@ -16,6 +16,7 @@ import io.netty.channel.Channel;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import io.netty.util.internal.ConcurrentSet;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
 import java.text.SimpleDateFormat;
@@ -23,6 +24,7 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.Executors;
@@ -30,10 +32,14 @@ import java.util.regex.Pattern;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
@@ -46,6 +52,13 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -91,8 +104,6 @@ public class ListenerAdapter implements DOMDataChangeListener {
 
     @Override
     public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-        // TODO Auto-generated method stub
-
         if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty()
                 || !change.getRemovedPaths().isEmpty()) {
             final String xml = prepareXmlFrom(change);
@@ -212,6 +223,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
      * @return Data in printable form.
      */
     private String prepareXmlFrom(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        final SchemaContext schemaContext = ControllerContext.getInstance().getGlobalSchema();
+        final DataSchemaContextTree dataContextTree =  DataSchemaContextTree.from(schemaContext);
         final Document doc = createDocument();
         final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
                 "notification");
@@ -223,7 +236,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
 
         final Element dataChangedNotificationEventElement = doc.createElementNS(
                 "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
-        addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change);
+        addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change,
+                schemaContext, dataContextTree);
         notificationElement.appendChild(dataChangedNotificationEventElement);
 
         try {
@@ -281,12 +295,16 @@ public class ListenerAdapter implements DOMDataChangeListener {
      */
     private void addValuesToDataChangedNotificationEventElement(final Document doc,
             final Element dataChangedNotificationEventElement,
-            final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-        addValuesFromDataToElement(doc, change.getCreatedData().keySet(), dataChangedNotificationEventElement,
-                Operation.CREATED);
+            final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change,
+            final SchemaContext  schemaContext, final DataSchemaContextTree dataSchemaContextTree) {
+
+        addCreatedChangedValuesFromDataToElement(doc, change.getCreatedData().entrySet(),
+                dataChangedNotificationEventElement,
+                Operation.CREATED, schemaContext, dataSchemaContextTree);
         if (change.getCreatedData().isEmpty()) {
-            addValuesFromDataToElement(doc, change.getUpdatedData().keySet(), dataChangedNotificationEventElement,
-                    Operation.UPDATED);
+            addCreatedChangedValuesFromDataToElement(doc, change.getUpdatedData().entrySet(),
+                    dataChangedNotificationEventElement,
+                    Operation.UPDATED, schemaContext, dataSchemaContextTree);
         }
         addValuesFromDataToElement(doc, change.getRemovedPaths(), dataChangedNotificationEventElement,
                 Operation.DELETED);
@@ -317,6 +335,20 @@ public class ListenerAdapter implements DOMDataChangeListener {
         }
     }
 
+    private void addCreatedChangedValuesFromDataToElement(final Document doc, final Set<Entry<YangInstanceIdentifier,
+                NormalizedNode<?,?>>> data, final Element element, final Operation operation, final SchemaContext
+            schemaContext, final DataSchemaContextTree dataSchemaContextTree) {
+        if (data == null || data.isEmpty()) {
+            return;
+        }
+        for (Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry : data) {
+            if (!ControllerContext.getInstance().isNodeMixin(entry.getKey())) {
+                final Node node = createCreatedChangedDataChangeEventElement(doc, entry, operation, schemaContext,
+                        dataSchemaContextTree);
+                element.appendChild(node);
+            }
+        }
+    }
 
     /**
      * Creates changed event element from data.
@@ -342,6 +374,65 @@ public class ListenerAdapter implements DOMDataChangeListener {
         return dataChangeEventElement;
     }
 
+    private Node createCreatedChangedDataChangeEventElement(final Document doc, final Entry<YangInstanceIdentifier,
+            NormalizedNode<?, ?>> entry, final Operation operation, final SchemaContext
+            schemaContext, final DataSchemaContextTree dataSchemaContextTree) {
+        final Element dataChangeEventElement = doc.createElement("data-change-event");
+        final Element pathElement = doc.createElement("path");
+        final YangInstanceIdentifier path = entry.getKey();
+        addPathAsValueToElement(path, pathElement);
+        dataChangeEventElement.appendChild(pathElement);
+
+        final Element operationElement = doc.createElement("operation");
+        operationElement.setTextContent(operation.value);
+        dataChangeEventElement.appendChild(operationElement);
+
+        try {
+            final DOMResult domResult = writeNormalizedNode((NormalizedNodeContainer<?,?,?>) entry.getValue(), path,
+                    schemaContext, dataSchemaContextTree);
+            final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
+            dataChangeEventElement.appendChild(result);
+        } catch (IOException e) {
+            LOG.error("Error in writer ", e);
+        } catch (XMLStreamException e) {
+            LOG.error("Error processing stream", e);
+        }
+
+        return dataChangeEventElement;
+    }
+
+    private static DOMResult writeNormalizedNode(final NormalizedNodeContainer<?,?,?> normalized, final
+        YangInstanceIdentifier path, final SchemaContext context, final DataSchemaContextTree dataSchemaContextTree) throws
+            IOException, XMLStreamException {
+        final XMLOutputFactory XML_FACTORY = XMLOutputFactory.newFactory();
+        final Document doc = XmlDocumentUtils.getDocument();
+        final DOMResult result = new DOMResult(doc, "data");
+        NormalizedNodeWriter normalizedNodeWriter = null;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, dataSchemaContextTree
+                    .getChild(path).getDataSchemaNode().getPath().getParent());
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            normalizedNodeWriter.write(normalized);
+
+            normalizedNodeWriter.flush();
+        } finally {
+            if (normalizedNodeWriter != null) {
+                normalizedNodeWriter.close();
+            }
+            if (normalizedNodeStreamWriter != null) {
+                normalizedNodeStreamWriter.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+
+        return result;
+    }
 
     /**
      * Adds path as value to element.