X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Frestconf-nb-rfc8040%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Fnb%2Frfc8040%2Fstreams%2Flisteners%2FListenerAdapter.java;h=386f4d07af329ad94f3f03fdf4828a151a337fbf;hb=e2f149583de143e6d02c5ee90d41619ecbaf6b49;hp=7e2d89b6ffd8056a8eddc4e8e63312828d2934b4;hpb=31cdc343cbeb04eebcfc6775608b3e1a97da3768;p=netconf.git diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java index 7e2d89b6ff..386f4d07af 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java @@ -7,18 +7,17 @@ */ package org.opendaylight.restconf.nb.rfc8040.streams.listeners; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.google.common.base.Preconditions; -import com.google.common.base.Throwables; import java.io.IOException; +import java.time.Instant; +import java.util.Collection; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; +import java.util.Optional; import javax.xml.stream.XMLStreamException; import javax.xml.transform.dom.DOMResult; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; -import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.json.XML; +import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener; import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -29,6 +28,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -43,7 +44,7 @@ import org.w3c.dom.Node; * {@link ListenerAdapter} is responsible to track events, which occurred by * changing data in data source. */ -public class ListenerAdapter extends AbstractCommonSubscriber implements DOMDataChangeListener { +public class ListenerAdapter extends AbstractCommonSubscriber implements ClusteredDOMDataTreeChangeListener { private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapter.class); @@ -51,8 +52,6 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData private final String streamName; private final NotificationOutputType outputType; - private AsyncDataChangeEvent> change; - /** * Creates new {@link ListenerAdapter} listener specified by path and stream * name and register for subscribing. @@ -71,15 +70,19 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData this.outputType = Preconditions.checkNotNull(outputType); this.path = Preconditions.checkNotNull(path); - Preconditions.checkArgument((streamName != null) && !streamName.isEmpty()); + Preconditions.checkArgument(streamName != null && !streamName.isEmpty()); this.streamName = streamName; } @Override - public void onDataChanged(final AsyncDataChangeEvent> change) { - this.change = change; - final String xml = prepareXml(); - if (checkQueryParams(xml, this)) { + public void onDataTreeChanged(final Collection dataTreeCandidates) { + final Instant now = Instant.now(); + if (!checkStartStop(now, this)) { + return; + } + + final String xml = prepareXml(dataTreeCandidates); + if (checkFilter(xml)) { prepareAndPostData(xml); } } @@ -116,13 +119,7 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData private void prepareAndPostData(final String xml) { final Event event = new Event(EventType.NOTIFY); if (this.outputType.equals(NotificationOutputType.JSON)) { - try { - final JsonNode node = new XmlMapper().readTree(xml.getBytes()); - event.setData(node.toString()); - } catch (final IOException e) { - LOG.error("Error parsing XML {}", xml, e); - Throwables.propagate(e); - } + event.setData(XML.toJSONObject(xml).toString()); } else { event.setData(xml); } @@ -136,9 +133,11 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData /** * Prepare data in printable form and transform it to String. * + * @param dataTreeCandidates the DataTreeCandidates to transform + * * @return Data in printable form. */ - private String prepareXml() { + private String prepareXml(final Collection dataTreeCandidates) { final SchemaContext schemaContext = schemaHandler.get(); final DataSchemaContextTree dataContextTree = DataSchemaContextTree.from(schemaContext); final Document doc = createDocument(); @@ -147,7 +146,7 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData final Element dataChangedNotificationEventElement = doc.createElementNS( "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification"); - addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, this.change, + addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, dataTreeCandidates, schemaContext, dataContextTree); notificationElement.appendChild(dataChangedNotificationEventElement); return transformDoc(doc); @@ -155,73 +154,82 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData /** * Adds values to data changed notification event element. - * - * @param doc - * {@link Document} - * @param dataChangedNotificationEventElement - * {@link Element} - * @param change - * {@link AsyncDataChangeEvent} */ + @SuppressWarnings("checkstyle:hiddenField") private void addValuesToDataChangedNotificationEventElement(final Document doc, final Element dataChangedNotificationEventElement, - final AsyncDataChangeEvent> change, + final Collection dataTreeCandidates, final SchemaContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) { - addCreatedChangedValuesFromDataToElement(doc, change.getCreatedData().entrySet(), - dataChangedNotificationEventElement, Operation.CREATED, schemaContext, dataSchemaContextTree); - - addCreatedChangedValuesFromDataToElement(doc, change.getUpdatedData().entrySet(), - dataChangedNotificationEventElement, Operation.UPDATED, schemaContext, dataSchemaContextTree); - - addValuesFromDataToElement(doc, change.getRemovedPaths(), dataChangedNotificationEventElement, - Operation.DELETED, schemaContext, dataSchemaContextTree); - } - - /** - * Adds values from data to element. - * - * @param doc - * {@link Document} - * @param data - * Set of {@link YangInstanceIdentifier}. - * @param element - * {@link Element} - * @param operation - * {@link Operation} - * @param schemaContext - * schema context - * @param dataSchemaContextTree - * data schema context tree - */ - private void addValuesFromDataToElement(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 (final YangInstanceIdentifier path : data) { - if (!dataSchemaContextTree.getChild(path).isMixin()) { - final Node node = createDataChangeEventElement(doc, path, operation, schemaContext); - element.appendChild(node); + for (DataTreeCandidate dataTreeCandidate : dataTreeCandidates) { + DataTreeCandidateNode candidateNode = dataTreeCandidate.getRootNode(); + if (candidateNode == null) { + continue; } + YangInstanceIdentifier yiid = dataTreeCandidate.getRootPath(); + addNodeToDataChangeNotificationEventElement(doc, dataChangedNotificationEventElement, candidateNode, + yiid.getParent(), schemaContext, dataSchemaContextTree); } } - private void addCreatedChangedValuesFromDataToElement(final Document doc, - final Set>> data, final Element element, - final Operation operation, final SchemaContext schemaContext, + private void addNodeToDataChangeNotificationEventElement(final Document doc, + final Element dataChangedNotificationEventElement, final DataTreeCandidateNode candidateNode, + final YangInstanceIdentifier parentYiid, final SchemaContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) { - if ((data == null) || data.isEmpty()) { + + Optional> optionalNormalizedNode = Optional.empty(); + switch (candidateNode.getModificationType()) { + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + optionalNormalizedNode = candidateNode.getDataAfter(); + break; + case DELETE: + case DISAPPEARED: + optionalNormalizedNode = candidateNode.getDataBefore(); + break; + case UNMODIFIED: + default: + break; + } + + if (!optionalNormalizedNode.isPresent()) { + LOG.error("No node present in notification for {}", candidateNode); return; } - for (final Entry> entry : data) { - if (!dataSchemaContextTree.getChild(entry.getKey()).isMixin() - && (!getLeafNodesOnly() || entry.getValue() instanceof LeafNode)) { - final Node node = createCreatedChangedDataChangeEventElement(doc, entry, operation, schemaContext, - dataSchemaContextTree); - element.appendChild(node); + + NormalizedNode normalizedNode = optionalNormalizedNode.get(); + YangInstanceIdentifier yiid = YangInstanceIdentifier.builder(parentYiid) + .append(normalizedNode.getIdentifier()).build(); + + boolean isNodeMixin = dataSchemaContextTree.getChild(yiid).isMixin(); + boolean isSkippedNonLeaf = getLeafNodesOnly() && !(normalizedNode instanceof LeafNode); + if (!isNodeMixin && !isSkippedNonLeaf) { + Node node = null; + switch (candidateNode.getModificationType()) { + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + Operation op = candidateNode.getDataBefore().isPresent() ? Operation.UPDATED : Operation.CREATED; + node = createCreatedChangedDataChangeEventElement(doc, yiid, normalizedNode, op, + schemaContext, dataSchemaContextTree); + break; + case DELETE: + case DISAPPEARED: + node = createDataChangeEventElement(doc, yiid, Operation.DELETED, schemaContext); + break; + case UNMODIFIED: + default: + break; } + if (node != null) { + dataChangedNotificationEventElement.appendChild(node); + } + } + + for (DataTreeCandidateNode childNode : candidateNode.getChildNodes()) { + addNodeToDataChangeNotificationEventElement(doc, dataChangedNotificationEventElement, childNode, + yiid, schemaContext, dataSchemaContextTree); } } @@ -238,11 +246,11 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData * schema context * @return {@link Node} node represented by changed event element. */ - private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, + private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier eventPath, final Operation operation, final SchemaContext schemaContext) { final Element dataChangeEventElement = doc.createElement("data-change-event"); final Element pathElement = doc.createElement("path"); - addPathAsValueToElement(path, pathElement, schemaContext); + addPathAsValueToElement(eventPath, pathElement, schemaContext); dataChangeEventElement.appendChild(pathElement); final Element operationElement = doc.createElement("operation"); @@ -253,12 +261,11 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData } private Node createCreatedChangedDataChangeEventElement(final Document doc, - final Entry> entry, final Operation operation, + final YangInstanceIdentifier eventPath, final NormalizedNode normalized, 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, schemaContext); + addPathAsValueToElement(eventPath, pathElement, schemaContext); dataChangeEventElement.appendChild(pathElement); final Element operationElement = doc.createElement("operation"); @@ -267,11 +274,10 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData try { SchemaPath nodePath; - final NormalizedNode normalized = entry.getValue(); - if ((normalized instanceof MapEntryNode) || (normalized instanceof UnkeyedListEntryNode)) { - nodePath = dataSchemaContextTree.getChild(path).getDataSchemaNode().getPath(); + if (normalized instanceof MapEntryNode || normalized instanceof UnkeyedListEntryNode) { + nodePath = dataSchemaContextTree.getChild(eventPath).getDataSchemaNode().getPath(); } else { - nodePath = dataSchemaContextTree.getChild(path).getDataSchemaNode().getPath().getParent(); + nodePath = dataSchemaContextTree.getChild(eventPath).getDataSchemaNode().getPath().getParent(); } final DOMResult domResult = writeNormalizedNode(normalized, schemaContext, nodePath); final Node result = doc.importNode(domResult.getNode().getFirstChild(), true); @@ -290,7 +296,7 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData /** * Adds path as value to element. * - * @param path + * @param eventPath * Path to data in data store. * @param element * {@link Element} @@ -298,11 +304,11 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData * schema context */ @SuppressWarnings("rawtypes") - private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element, + private void addPathAsValueToElement(final YangInstanceIdentifier eventPath, final Element element, final SchemaContext schemaContext) { final StringBuilder textContent = new StringBuilder(); - for (final PathArgument pathArgument : path.getPathArguments()) { + for (final PathArgument pathArgument : eventPath.getPathArguments()) { if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) { continue; } @@ -310,8 +316,9 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), schemaContext); if (pathArgument instanceof NodeIdentifierWithPredicates) { final Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); - for (final QName keyValue : predicates.keySet()) { - final String predicateValue = String.valueOf(predicates.get(keyValue)); + for (final Entry entry : predicates.entrySet()) { + final QName keyValue = entry.getKey(); + final String predicateValue = String.valueOf(entry.getValue()); textContent.append("["); writeIdentifierWithNamespacePrefix(element, textContent, keyValue, schemaContext); textContent.append("='"); @@ -343,8 +350,7 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData */ private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qualifiedName, final SchemaContext schemaContext) { - final Module module = schemaContext.findModuleByNamespaceAndRevision(qualifiedName.getNamespace(), - qualifiedName.getRevision()); + final Module module = schemaContext.findModule(qualifiedName.getModule()).get(); textContent.append(module.getName()); textContent.append(":");