From: Jakub Morvay Date: Mon, 8 Aug 2016 11:25:54 +0000 (+0200) Subject: Fix AbstractDOMShardTreeChangePublisher's initial data change event X-Git-Tag: release/boron~14 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=dde934b76af0fcc902d79b316e96e6d9ff3c6509 Fix AbstractDOMShardTreeChangePublisher's initial data change event 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 --- diff --git a/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisher.java b/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisher.java index e24cdea9ec..307c8ca208 100644 --- a/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisher.java +++ b/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisher.java @@ -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 void initialDataChangeEvent(final YangInstanceIdentifier listenerPath, final L listener) { // FIXME Add support for wildcard listeners - final Optional> preExistingData = dataTree.takeSnapshot().readNode(listenerPath); + final Optional> 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 AbstractDOMDataTreeChangeListenerRegistration setupContextWithoutSubshards(final YangInstanceIdentifier listenerPath, final DOMDataTreeListenerWithSubshards listener) { LOG.debug("Registering root listener at {}", listenerPath); final RegistrationTreeNode> node = findNodeFor(listenerPath.getPathArguments()); diff --git a/dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisherTest.java b/dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisherTest.java index e1635dc787..15fd602b90 100644 --- a/dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisherTest.java +++ b/dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/AbstractDOMShardTreeChangePublisherTest.java @@ -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) diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java index 0d21f7dd02..b1680be1fe 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java @@ -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}. *

- * * 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. *

* 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. *

* 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 ListenerRegistration registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener); + @Nonnull + ListenerRegistration registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener); }