From 898916bcccbae02956efa6175118c7521080a2d2 Mon Sep 17 00:00:00 2001 From: Martin Ciglan Date: Tue, 19 Jan 2016 23:42:06 +0100 Subject: [PATCH] Bug 3934: Websockets: Scope ONE doesn't work correctly 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 --- .../streams/listeners/ListenerAdapter.java | 107 ++++++++++++++++-- 1 file changed, 99 insertions(+), 8 deletions(-) diff --git a/opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java b/opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java index 802416d494..9599dbbd88 100644 --- a/opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java +++ b/opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java @@ -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> 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> 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> change) { - addValuesFromDataToElement(doc, change.getCreatedData().keySet(), dataChangedNotificationEventElement, - Operation.CREATED); + final AsyncDataChangeEvent> 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>> data, final Element element, final Operation operation, final SchemaContext + schemaContext, final DataSchemaContextTree dataSchemaContextTree) { + if (data == null || data.isEmpty()) { + return; + } + for (Entry> 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> 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. -- 2.36.6