Fix AbstractDOMShardTreeChangePublisher's initial data change event 92/43692/2
authorJakub Morvay <jmorvay@cisco.com>
Mon, 8 Aug 2016 11:25:54 +0000 (13:25 +0200)
committerRobert Varga <nite@hq.sk>
Mon, 15 Aug 2016 08:21:32 +0000 (08:21 +0000)
Strip shard path from listener path, when reading initial data from non
root shard. Also add requirement on DOMStoreTreeChangePublisher
interface to send initial data change event to even when data does not
exist yet.

Change-Id: I90f3aa33764a4812703c37e91378a73270fe19a3
Signed-off-by: Jakub Morvay <jmorvay@cisco.com>
dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisher.java
dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisherTest.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java

index e24cdea9ec59c7d5bb9f256be4b1411a917060b8..307c8ca2086122d7a78fc74318f53dc66a48e8a0 100644 (file)
@@ -29,6 +29,9 @@ import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+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.tree.DataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
@@ -37,6 +40,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -97,10 +103,23 @@ abstract class AbstractDOMShardTreeChangePublisher extends AbstractDOMStoreTreeC
 
     private <L extends DOMDataTreeChangeListener> void initialDataChangeEvent(final YangInstanceIdentifier listenerPath, final L listener) {
         // FIXME Add support for wildcard listeners
-        final Optional<NormalizedNode<?, ?>> preExistingData = dataTree.takeSnapshot().readNode(listenerPath);
+        final Optional<NormalizedNode<?, ?>> preExistingData = dataTree.takeSnapshot()
+                .readNode(YangInstanceIdentifier.create(stripShardPath(listenerPath)));
         final DataTreeCandidate initialCandidate;
+
         if (preExistingData.isPresent()) {
-            initialCandidate = DataTreeCandidates.fromNormalizedNode(listenerPath, preExistingData.get());
+            final NormalizedNode<?, ?> data = preExistingData.get();
+            Preconditions.checkState(data instanceof DataContainerNode,
+                    "Expected DataContainer node, but was {}", data.getClass());
+            // if we are listening on root of some shard we still get
+            // empty normalized node, root is always present
+            if (((DataContainerNode) data).getValue().isEmpty()) {
+                initialCandidate = DataTreeCandidates.newDataTreeCandidate(listenerPath,
+                        new EmptyDataTreeCandidateNode(data.getIdentifier()));
+            } else {
+                initialCandidate = DataTreeCandidates.fromNormalizedNode(listenerPath,
+                        translateRootShardIdentifierToListenerPath(listenerPath, preExistingData.get()));
+            }
         } else {
             initialCandidate = DataTreeCandidates.newDataTreeCandidate(listenerPath,
                     new EmptyDataTreeCandidateNode(listenerPath.getLastPathArgument()));
@@ -109,6 +128,24 @@ abstract class AbstractDOMShardTreeChangePublisher extends AbstractDOMStoreTreeC
         listener.onDataTreeChanged(Collections.singleton(initialCandidate));
     }
 
+    private NormalizedNode<?, ?> translateRootShardIdentifierToListenerPath(final YangInstanceIdentifier listenerPath,
+                                                                            final NormalizedNode<?, ?> node) {
+        if (listenerPath.isEmpty()) {
+            return node;
+        }
+
+        final NormalizedNodeBuilder nodeBuilder;
+        if (node instanceof ContainerNode) {
+            nodeBuilder = ImmutableContainerNodeBuilder.create().withValue(((ContainerNode) node).getValue());
+        } else if (node instanceof MapEntryNode) {
+            nodeBuilder = ImmutableMapEntryNodeBuilder.create().withValue(((MapEntryNode) node).getValue());
+        } else {
+            throw new IllegalArgumentException("Expected ContainerNode or MapEntryNode, but was " + node.getClass());
+        }
+        nodeBuilder.withNodeIdentifier(listenerPath.getLastPathArgument());
+        return nodeBuilder.build();
+    }
+
     private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupContextWithoutSubshards(final YangInstanceIdentifier listenerPath, final DOMDataTreeListenerWithSubshards listener) {
         LOG.debug("Registering root listener at {}", listenerPath);
         final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(listenerPath.getPathArguments());
index e1635dc7876fb1d7f74fabdde3ed2a8fc7df9a11..15fd602b900252f67123bf2b191799cc995d0af9 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.dom.store.inmemory;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doNothing;
@@ -37,10 +36,12 @@ import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistrat
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 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.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 
 public class AbstractDOMShardTreeChangePublisherTest extends AbstractDOMShardTreeChangePublisher {
 
@@ -72,7 +73,10 @@ public class AbstractDOMShardTreeChangePublisherTest extends AbstractDOMShardTre
         final DOMDataTreeChangeListener domDataTreeChangeListener = mock(DOMDataTreeChangeListener.class);
         final ListenerRegistration listenerRegistration = mock(ListenerRegistration.class);
         final DataTreeSnapshot initialSnapshot = mock(DataTreeSnapshot.class);
-        final NormalizedNode<?, ?> initialData = mock(NormalizedNode.class);
+        final DataContainerNode initialData =
+                ImmutableContainerNodeBuilder.create()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("test")))
+                        .build();
         doReturn(initialSnapshot).when(DATA_TREE).takeSnapshot();
         doReturn(Optional.of(initialData)).when(initialSnapshot).readNode(any());
         doNothing().when(domDataTreeChangeListener).onDataTreeChanged(any());
@@ -92,7 +96,7 @@ public class AbstractDOMShardTreeChangePublisherTest extends AbstractDOMShardTre
         initialChange.forEach(dataTreeCandidate ->
                 assertEquals(dataTreeCandidate.getRootPath(), YANG_INSTANCE_IDENTIFIER));
         initialChange.forEach(dataTreeCandidate ->
-                assertSame(dataTreeCandidate.getRootNode().getDataAfter().get(), initialData));
+                assertEquals(dataTreeCandidate.getRootNode().getModificationType(), ModificationType.UNMODIFIED));
     }
 
     @Test(expected = UnsupportedOperationException.class)
index 0d21f7dd02f1b0200a5c1ee0a452365ee3190257..b1680be1fe13ececd6e6e6091a7c13243bd6d7b8 100644 (file)
@@ -7,9 +7,8 @@
  */
 package org.opendaylight.mdsal.dom.spi.store;
 
-import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
-
 import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
@@ -26,15 +25,15 @@ public interface DOMStoreTreeChangePublisher {
      * You are able to register for notifications  for any node or subtree
      * which can be represented using {@link YangInstanceIdentifier}.
      * <p>
-     *
      * You are able to register for data change notifications for a subtree or leaf
      * even if it does not exist. You will receive notification once that node is
      * created.
      * <p>
      * If there is any pre-existing data in data tree on path for which you are
      * registering, you will receive initial data change event, which will
-     * contain all pre-existing data, marked as created.
-     *
+     * contain all pre-existing data, marked as created. If the data at the supplied
+     * path does not exist, you will also receive initial data change event, which will
+     * contain empty data tree modification, marked as unmodified.
      * <p>
      * This method returns a {@link ListenerRegistration} object. To
      * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
@@ -44,14 +43,13 @@ public interface DOMStoreTreeChangePublisher {
      * notifications. This is especially true in OSGi environments, where failure to
      * do so during bundle shutdown can lead to stale listeners being still registered.
      *
-     * @param treeId
-     *            Data tree identifier of the subtree which should be watched for
-     *            changes.
-     * @param listener
-     *            Listener instance which is being registered
+     * @param treeId   Data tree identifier of the subtree which should be watched for
+     *                 changes.
+     * @param listener Listener instance which is being registered
      * @return Listener registration object, which may be used to unregister
-     *         your listener using {@link ListenerRegistration#close()} to stop
-     *         delivery of change events.
+     * your listener using {@link ListenerRegistration#close()} to stop
+     * delivery of change events.
      */
-    @Nonnull <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener);
+    @Nonnull
+    <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener);
 }