Transition ListenerAdapter to ClusteredDOMDataTreeListener 02/64402/1
authorJosh <jhershbe@redhat.com>
Tue, 19 Sep 2017 07:42:39 +0000 (10:42 +0300)
committerJosh <jhershbe@redhat.com>
Tue, 17 Oct 2017 13:46:46 +0000 (16:46 +0300)
Solves this: https://bugs.opendaylight.org/show_bug.cgi?id=9147
This is in general an issue with ListenerAdapter which does not
currently work in a clustered deployment.

ListenerAdapterTest needed to be adjusted for two reasons:
1. There was a bug in ListenerAdapter where if leafNodesOnly
was set the leafNodesOnly logic was not applied in delete
notifications. The new ListenerAdapter code fixes that so
ListenerAdapterTest needed to be adjusted.
2. In DomDataTreeEtc, merge operations cause the key node
to count as "WRITE"-en for notifications even if it was not
changed. ListenerAdapterTest was adjusted accordingly. Bug
for this here: https://bugs.opendaylight.org/show_bug.cgi?id=9179

Change-Id: I3c40e02551d8f04354489e5ffb8c66f111bb3b96
Signed-off-by: Josh <jhershbe@redhat.com>
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapterTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java
restconf/sal-rest-connector/src/test/resources/listener-adapter-test/notif-leaves-del.json
restconf/sal-rest-connector/src/test/resources/listener-adapter-test/notif-leaves-update.json
restconf/sal-rest-connector/src/test/resources/listener-adapter-test/notif-update.json

index 6b4787064c1dc2e2e572c8aa818c49ab28d8032c..63f49e320c48888b2ef8ac2b7ebb5b936d97e9b8 100644 (file)
@@ -29,10 +29,11 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
@@ -520,9 +521,15 @@ public class BrokerFacade {
         }
 
         final YangInstanceIdentifier path = listener.getPath();
-        final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
-                datastore, path, listener, scope);
-
+        DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+                                    this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        if (changeService == null) {
+            throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
+                                                        + this.domDataBroker);
+        }
+        DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
+        ListenerRegistration<ListenerAdapter> registration =
+                                    changeService.registerDataTreeChangeListener(root, listener);
         listener.setRegistration(registration);
     }
 
index 44f6a06924f7a98088eb7450e30b146868da2b65..83afe4d4caf6b3048ea386ba55586fd9eb251d75 100644 (file)
@@ -7,16 +7,16 @@
  */
 package org.opendaylight.netconf.sal.streams.listeners;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
+import javax.annotation.Nonnull;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.dom.DOMResult;
 import org.json.XML;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -28,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;
@@ -42,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);
 
@@ -50,7 +52,7 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData
     private final String streamName;
     private final NotificationOutputType outputType;
 
-    private AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change;
+    private Collection<DataTreeCandidate> dataTreeCandidates;
 
     /**
      * Creates new {@link ListenerAdapter} listener specified by path and stream
@@ -76,8 +78,8 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData
     }
 
     @Override
-    public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-        this.change = change;
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> dataTreeCandidates) {
+        this.dataTreeCandidates = dataTreeCandidates;
         final String xml = prepareXml();
         if (checkQueryParams(xml, this)) {
             prepareAndPostData(xml);
@@ -141,8 +143,8 @@ 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,
-                schemaContext, dataContextTree);
+        addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement,
+                                                            this.dataTreeCandidates, schemaContext, dataContextTree);
         notificationElement.appendChild(dataChangedNotificationEventElement);
         return transformDoc(doc);
     }
@@ -154,64 +156,84 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData
      *            {@link Document}
      * @param dataChangedNotificationEventElement
      *            {@link Element}
-     * @param change
-     *            {@link AsyncDataChangeEvent}
+     * @param dataTreeCandidates
+     *            {@link DataTreeCandidate}
      */
     private void addValuesToDataChangedNotificationEventElement(final Document doc,
             final Element dataChangedNotificationEventElement,
-            final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change,
+            final Collection<DataTreeCandidate> 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);
-    }
-
-    /**
-     * Adds values from data to element.
-     *
-     * @param doc
-     *            {@link Document}
-     * @param data
-     *            Set of {@link YangInstanceIdentifier}.
-     * @param element
-     *            {@link Element}
-     * @param operation
-     *            {@link Operation}
-     */
-    private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data,
-            final Element element, final Operation operation) {
-        if ((data == null) || data.isEmpty()) {
-            return;
-        }
-        for (final YangInstanceIdentifier path : data) {
-            if (!ControllerContext.getInstance().isNodeMixin(path)) {
-                final Node node = createDataChangeEventElement(doc, path, operation);
-                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<Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> data, final Element element,
-            final Operation operation, final SchemaContext schemaContext,
-            final DataSchemaContextTree dataSchemaContextTree) {
-        if ((data == null) || data.isEmpty()) {
+    private void addNodeToDataChangeNotificationEventElement(final Document doc,
+                             final Element dataChangedNotificationEventElement, DataTreeCandidateNode candidateNode,
+                             YangInstanceIdentifier parentYiid, SchemaContext schemaContext,
+                             DataSchemaContextTree dataSchemaContextTree) {
+
+        Optional<NormalizedNode<?,?>> optionalNormalizedNode = Optional.absent();
+        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<YangInstanceIdentifier, NormalizedNode<?, ?>> entry : data) {
-            if (!ControllerContext.getInstance().isNodeMixin(entry.getKey())
-                    && (!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 = ControllerContext.getInstance().isNodeMixin(yiid);
+        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);
+                    break;
+                case UNMODIFIED:
+                default:
+                    break;
+            }
+            if (node != null) {
+                dataChangedNotificationEventElement.appendChild(node);
             }
         }
+
+        for (DataTreeCandidateNode childNode : candidateNode.getChildNodes()) {
+            addNodeToDataChangeNotificationEventElement(doc, dataChangedNotificationEventElement, childNode,
+                                                                        yiid, schemaContext, dataSchemaContextTree);
+        }
     }
 
     /**
@@ -240,11 +262,10 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements DOMData
     }
 
     private Node createCreatedChangedDataChangeEventElement(final Document doc,
-            final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry, final Operation operation,
+            YangInstanceIdentifier path, 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);
         dataChangeEventElement.appendChild(pathElement);
 
@@ -254,7 +275,6 @@ 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();
             } else {
index 4bcfd5454551e8712eecccf2ce552f944960468c..7b5155f14718b7f095dc2f83edec78eac258c176 100644 (file)
@@ -28,8 +28,9 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -64,6 +65,8 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
+
 /**
  * Subscribe to stream util class.
  *
@@ -336,10 +339,14 @@ public final class SubscribeToStreamUtil {
         }
 
         final YangInstanceIdentifier path = listener.getPath();
-        final ListenerRegistration<DOMDataChangeListener> registration =
-                domDataBroker.registerDataChangeListener(ds, path, listener, scope);
+        DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+                        domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        if (changeService == null) {
+            throw new UnsupportedOperationException("DOMDataTreeChangeService not supported by DOMDataBroker");
+        }
+        DOMDataTreeIdentifier loc = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path);
+        listener.setRegistration(changeService.registerDataTreeChangeListener(loc, listener));
 
-        listener.setRegistration(registration);
     }
 
     /**
index b52689f1c889e6b4b49a32092d0a25c3298dfb0a..3ef3bb78c7e012ab6c939365c1f827d1834125c9 100644 (file)
@@ -28,6 +28,8 @@ import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+
+import java.util.HashMap;
 import java.util.concurrent.Future;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,9 +42,10 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
@@ -116,6 +119,9 @@ public class BrokerFacadeTest {
         when(this.domDataBroker.newReadOnlyTransaction()).thenReturn(this.readTransaction);
         when(this.domDataBroker.newWriteOnlyTransaction()).thenReturn(this.writeTransaction);
         when(this.domDataBroker.newReadWriteTransaction()).thenReturn(this.rwTransaction);
+        HashMap extensions = new HashMap();
+        extensions.put(DOMDataTreeChangeService.class, Mockito.mock(DOMDataTreeChangeService.class));
+        when(this.domDataBroker.getSupportedExtensions()).thenReturn(extensions);
 
         ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext("/full-versions/test-module"));
     }
@@ -329,22 +335,23 @@ public class BrokerFacadeTest {
                 NotificationOutputType.XML);
 
         @SuppressWarnings("unchecked")
-        final ListenerRegistration<DOMDataChangeListener> mockRegistration = mock(ListenerRegistration.class);
+        final ListenerRegistration<ListenerAdapter> mockRegistration = mock(ListenerRegistration.class);
 
-        when(this.domDataBroker.registerDataChangeListener(any(LogicalDatastoreType.class), eq(this.instanceID),
-                eq(listener), eq(DataChangeScope.BASE))).thenReturn(mockRegistration);
+        DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+                this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        DOMDataTreeIdentifier loc = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, this.instanceID);
+        when(changeService.registerDataTreeChangeListener(eq(loc), eq(listener))).thenReturn(mockRegistration);
 
         this.brokerFacade.registerToListenDataChanges(
                 LogicalDatastoreType.CONFIGURATION, DataChangeScope.BASE, listener);
 
-        verify(this.domDataBroker).registerDataChangeListener(
-                LogicalDatastoreType.CONFIGURATION, this.instanceID, listener, DataChangeScope.BASE);
+        verify(changeService).registerDataTreeChangeListener(loc, listener);
 
         assertEquals("isListening", true, listener.isListening());
 
         this.brokerFacade.registerToListenDataChanges(
                 LogicalDatastoreType.CONFIGURATION, DataChangeScope.BASE, listener);
-        verifyNoMoreInteractions(this.domDataBroker);
+        verifyNoMoreInteractions(changeService);
     }
 
     /**
index e993fb490796b67601e43406fa15a2264e7db892..2ae60d67f20dfdf1f2a31a2e4b3c532fe9dd6619 100644 (file)
@@ -23,7 +23,6 @@ import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
@@ -37,7 +36,7 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 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.tree.DataTreeCandidate;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class RestconfImplNotificationSubscribingTest {
@@ -209,11 +208,10 @@ public class RestconfImplNotificationSubscribingTest {
 
         subscribe(list);
 
-        final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change =
-                Mockito.mock(AsyncDataChangeEvent.class);
+        ArrayList<DataTreeCandidate> candidates = new ArrayList<DataTreeCandidate>(0);
         Instant startOrig = listener.getStart();
         Assert.assertNotNull(startOrig);
-        listener.onDataChanged(change);
+        listener.onDataTreeChanged(candidates);
 
         startOrig = listener.getStart();
         Assert.assertNull(startOrig);
index 4249f53ecbd5e807231ad6d10e0b111d04f0f7dd..41bc13146ff92471a51f744f3d1a46a4bb35d897 100644 (file)
@@ -24,9 +24,10 @@ import org.junit.Test;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
@@ -38,8 +39,11 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 import org.skyscreamer.jsonassert.JSONAssert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
+    private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapterTest.class);
 
     private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
     private static final String JSON_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.json";
@@ -89,6 +93,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
                 }
                 Thread.currentThread().sleep(200);
             }
+            LOG.debug("Comparing {} {}", json, lastNotification);
             JSONAssert.assertEquals(json, withFakeDate(lastNotification), false);
             this.lastNotification = null;
         }
@@ -112,11 +117,12 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
 
     @Test
     public void testJsonNotifsLeaves() throws Exception {
-
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
                                         NotificationOutputTypeGrouping.NotificationOutputType.JSON, true);
-        domDataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID, adapter,
-                                                        AsyncDataBroker.DataChangeScope.SUBTREE);
+        DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+                domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        changeService.registerDataTreeChangeListener(root, adapter);
 
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
@@ -127,7 +133,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder.setMyLeaf12("Bertha");
+        builder = new MyList1Builder().setKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
         writeTransaction.submit();
         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
@@ -140,11 +146,12 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
 
     @Test
     public void testJsonNotifs() throws Exception {
-
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
                 NotificationOutputTypeGrouping.NotificationOutputType.JSON, false);
-        domDataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID, adapter,
-                AsyncDataBroker.DataChangeScope.SUBTREE);
+        DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+                domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        changeService.registerDataTreeChangeListener(root, adapter);
 
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
@@ -155,7 +162,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         adapter.assertGot(getNotifJson(JSON_NOTIF_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder.setMyLeaf12("Bertha");
+        builder = new MyList1Builder().setKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
         writeTransaction.submit();
         adapter.assertGot(getNotifJson(JSON_NOTIF_UPDATE));
index 330d5a24c461ff0b0bfb273741c85d7db0891b0b..7c0ad9a01f035712d2a3200e1038143c1289736d 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFaile
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
@@ -97,6 +98,12 @@ public class RestconfStreamsSubscriptionServiceImplTest {
         final ListenerRegistration<DOMDataChangeListener> listener = mock(ListenerRegistration.class);
         doReturn(dataBroker).when(this.dataBrokerHandler).get();
         doReturn(listener).when(dataBroker).registerDataChangeListener(any(), any(), any(), any());
+        DOMDataTreeChangeService changeService = Mockito.mock(DOMDataTreeChangeService.class);
+        Mockito.when(changeService.registerDataTreeChangeListener(any(), any()))
+                                                            .thenReturn(Mockito.mock(ListenerRegistration.class));
+        HashMap extensions = new HashMap();
+        extensions.put(DOMDataTreeChangeService.class, Mockito.mock(DOMDataTreeChangeService.class));
+        Mockito.when(dataBroker.getSupportedExtensions()).thenReturn(extensions);
         final MultivaluedMap<String, String> map = Mockito.mock(MultivaluedMap.class);
         final Set<Entry<String, List<String>>> set = new HashSet<>();
         Mockito.when(map.entrySet()).thenReturn(set);
index bffe4c3515cff73d095d964125c17f42aea1ffd5..5d9e9f1fb237bbdf91f7e09c175525595a441958 100644 (file)
@@ -2,26 +2,22 @@
     "notification": {
         "data-changed-notification": {
             "data-change-event": [
-                {
-                    "operation": "deleted",
-                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
-                },
                 {
                     "operation": "deleted",
                     "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11"
                 },
                 {
                     "operation": "deleted",
-                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']"
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name"
                 },
                 {
                     "operation": "deleted",
-                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name"
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
                 }
             ],
             "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
         },
-        "eventTime": "2017-09-17T14:03:35.261+03:00",
+        "eventTime": "2017-09-18T15:30:16.099+03:00",
         "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0"
     }
 }
index 0adf702b59116fe5f35d3cc5594364702322585b..ffef66035fa9fd8ba082a30a6130097eaa942d5c 100644 (file)
@@ -1,19 +1,31 @@
 {
     "notification": {
         "data-changed-notification": {
-            "data-change-event": {
-                "data": {
-                    "my-leaf12": {
-                        "content": "Bertha",
-                        "xmlns": "instance:identifier:patch:module"
-                    }
+            "data-change-event": [
+                {
+                    "data": {
+                        "my-leaf12": {
+                            "content": "Bertha",
+                            "xmlns": "instance:identifier:patch:module"
+                        }
+                    },
+                    "operation": "created",
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
                 },
-                "operation": "created",
-                "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
-            },
+                {
+                    "data": {
+                        "name": {
+                            "content": "Althea",
+                            "xmlns": "instance:identifier:patch:module"
+                        }
+                    },
+                    "operation": "updated",
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name"
+                }
+            ],
             "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
         },
-        "eventTime": "2017-09-17T13:56:47.032+03:00",
+        "eventTime": "2017-09-18T14:20:54.82+03:00",
         "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0"
     }
 }
index 065d6205a2ac838e441ba204149f5a52042f29b7..b2957ea99f8a3f96df0eee6e1db398e10d40eb17 100644 (file)
@@ -2,16 +2,6 @@
     "notification": {
         "data-changed-notification": {
             "data-change-event": [
-                {
-                    "data": {
-                        "my-leaf12": {
-                            "content": "Bertha",
-                            "xmlns": "instance:identifier:patch:module"
-                        }
-                    },
-                    "operation": "created",
-                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
-                },
                 {
                     "data": {
                         "patch-cont": {
                     },
                     "operation": "updated",
                     "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']"
+                },
+                {
+                    "data": {
+                        "my-leaf12": {
+                            "content": "Bertha",
+                            "xmlns": "instance:identifier:patch:module"
+                        }
+                    },
+                    "operation": "created",
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12"
+                },
+                {
+                    "data": {
+                        "name": {
+                            "content": "Althea",
+                            "xmlns": "instance:identifier:patch:module"
+                        }
+                    },
+                    "operation": "updated",
+                    "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name"
                 }
             ],
             "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
         },
-        "eventTime": "2017-09-17T14:15:05.839+03:00",
+        "eventTime": "2017-09-18T15:52:25.213+03:00",
         "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0"
     }
 }