Transition ListenerAdapter to ClusteredDOMDataTreeListener 64/63264/14
authorJosh <jhershbe@redhat.com>
Tue, 19 Sep 2017 07:42:39 +0000 (10:42 +0300)
committerJosh <jhershbe@redhat.com>
Sun, 15 Oct 2017 10:50:16 +0000 (13:50 +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/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapterTest.java
restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-del.json
restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-update.json
restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-update.json

index d2fd19307991a4c3acc1c9ec6561fc9a32011396..85263d056b86feb3418e3626dde47dbf0bb832ff 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;
@@ -528,9 +529,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 e6e27ccb701f41ef93d94cb098a2b635ec1e2dfe..d88f4e5b61612b3c440db213f9303ce4d58a3ceb 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
@@ -74,8 +76,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);
@@ -139,8 +141,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);
     }
@@ -152,64 +154,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);
+        }
     }
 
     /**
@@ -238,11 +260,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);
 
@@ -252,7 +273,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 50f2195302fceafb30996a4f4be94d36c47e7c37..70e1c340a63819b3aebd5b960825d9ca944700d0 100644 (file)
@@ -28,20 +28,24 @@ 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;
 import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 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;
@@ -113,6 +117,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", "/modules"));
@@ -327,22 +334,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 e34f9dbda1afe09e2aaf105625b9e28a604a23b4..bbac0c5cf675dbea0ba317762349dfbe5a0fc1f0 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 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"
     }
 }