From: Tom Pantelis Date: Tue, 22 Jan 2019 00:50:01 +0000 (-0500) Subject: Implement DOMDataTreeChangeListener.onInitialData X-Git-Tag: release/neon~7 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=refs%2Fchanges%2F02%2F79802%2F3 Implement DOMDataTreeChangeListener.onInitialData When there's no initial data on DTCL registration, we need to invoke onInitialData(). JIRA: CONTROLLER-1878 Change-Id: Ib6e8a822b0a6cdfa54f523cacce9ceb699463585 Signed-off-by: Tom Pantelis --- diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java index c7dfbc914d..c72de945b1 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java @@ -15,6 +15,7 @@ import org.opendaylight.controller.cluster.datastore.messages.DataTreeChangedRep import org.opendaylight.controller.cluster.datastore.messages.DataTreeListenerInfo; import org.opendaylight.controller.cluster.datastore.messages.EnableNotification; import org.opendaylight.controller.cluster.datastore.messages.GetInfo; +import org.opendaylight.controller.cluster.datastore.messages.OnInitialData; import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -39,6 +40,8 @@ final class DataTreeChangeListenerActor extends AbstractUntypedActor { protected void handleReceive(final Object message) { if (message instanceof DataTreeChanged) { dataChanged((DataTreeChanged)message); + } else if (message instanceof OnInitialData) { + onInitialData(); } else if (message instanceof EnableNotification) { enableNotification((EnableNotification) message); } else if (message instanceof GetInfo) { @@ -49,6 +52,17 @@ final class DataTreeChangeListenerActor extends AbstractUntypedActor { } } + @SuppressWarnings("checkstyle:IllegalCatch") + private void onInitialData() { + LOG.debug("{}: Notifying onInitialData to listener {}", logContext, listener); + + try { + this.listener.onInitialData(); + } catch (Exception e) { + LOG.error("{}: Error notifying listener {}", logContext, this.listener, e); + } + } + @SuppressWarnings("checkstyle:IllegalCatch") private void dataChanged(final DataTreeChanged message) { // Do nothing if notifications are not enabled diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DefaultShardDataTreeChangeListenerPublisher.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DefaultShardDataTreeChangeListenerPublisher.java index b71174d0b3..e17e3faae9 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DefaultShardDataTreeChangeListenerPublisher.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DefaultShardDataTreeChangeListenerPublisher.java @@ -63,16 +63,23 @@ final class DefaultShardDataTreeChangeListenerPublisher extends AbstractDOMStore public void registerTreeChangeListener(YangInstanceIdentifier treeId, DOMDataTreeChangeListener listener, Optional initialState, Consumer> onRegistration) { + registerTreeChangeListener(treeId, listener, onRegistration); + + if (initialState.isPresent()) { + notifySingleListener(treeId, listener, initialState.get(), logContext); + } else { + listener.onInitialData(); + } + } + + void registerTreeChangeListener(YangInstanceIdentifier treeId, DOMDataTreeChangeListener listener, + Consumer> onRegistration) { LOG.debug("{}: registerTreeChangeListener: path: {}, listener: {}", logContext, treeId, listener); AbstractDOMDataTreeChangeListenerRegistration registration = super.registerTreeChangeListener(treeId, listener); onRegistration.accept(registration); - - if (initialState.isPresent()) { - notifySingleListener(treeId, listener, initialState.get(), logContext); - } } static void notifySingleListener(YangInstanceIdentifier treeId, DOMDataTreeChangeListener listener, @@ -81,7 +88,10 @@ final class DefaultShardDataTreeChangeListenerPublisher extends AbstractDOMStore DefaultShardDataTreeChangeListenerPublisher publisher = new DefaultShardDataTreeChangeListenerPublisher(logContext); publisher.logContext = logContext; - publisher.registerTreeChangeListener(treeId, listener, Optional.absent(), noop -> { /* NOOP */ }); - publisher.publishChanges(state); + publisher.registerTreeChangeListener(treeId, listener); + + if (!publisher.processCandidateTree(state)) { + listener.onInitialData(); + } } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListener.java index dd3155b8eb..41427608e4 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListener.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListener.java @@ -12,6 +12,7 @@ import akka.actor.ActorSelection; import com.google.common.base.Preconditions; import java.util.Collection; import org.opendaylight.controller.cluster.datastore.messages.DataTreeChanged; +import org.opendaylight.controller.cluster.datastore.messages.OnInitialData; import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; import org.slf4j.Logger; @@ -37,6 +38,12 @@ final class ForwardingDataTreeChangeListener implements DOMDataTreeChangeListene actor.tell(new DataTreeChanged(changes), ActorRef.noSender()); } + @Override + public void onInitialData() { + LOG.debug("Sending OnInitialData to {}", actor); + actor.tell(OnInitialData.INSTANCE, ActorRef.noSender()); + } + @Override public String toString() { return "ForwardingDataTreeChangeListener [actor=" + actor + "]"; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeChangePublisherActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeChangePublisherActor.java index 356300ec4c..927e39ff01 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeChangePublisherActor.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeChangePublisherActor.java @@ -22,7 +22,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; * @author Thomas Pantelis */ public final class ShardDataTreeChangePublisherActor - extends ShardDataTreeNotificationPublisherActor { + extends ShardDataTreeNotificationPublisherActor { private ShardDataTreeChangePublisherActor(final String name, final String logContext) { super(new DefaultShardDataTreeChangeListenerPublisher(logContext), name, logContext); @@ -36,9 +36,11 @@ public final class ShardDataTreeChangePublisherActor if (reg.initialState.isPresent()) { DefaultShardDataTreeChangeListenerPublisher.notifySingleListener(reg.path, reg.listener, reg.initialState.get(), logContext()); + } else { + reg.listener.onInitialData(); } - publisher().registerTreeChangeListener(reg.path, reg.listener, Optional.absent(), reg.onRegistration); + publisher().registerTreeChangeListener(reg.path, reg.listener, reg.onRegistration); } else { super.handleReceive(message); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnInitialData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnInitialData.java new file mode 100644 index 0000000000..d2bd499c08 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnInitialData.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +/** + * Message sent to a data tree change listener actor to indicate there is no initial data. + * + * @author Thomas Pantelis + */ +public final class OnInitialData { + public static final OnInitialData INSTANCE = new OnInitialData(); + + private OnInitialData() { + // Hidden on purpose + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index 7d4c054b3e..b3e8de2d9b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -150,6 +150,18 @@ public class ShardTest extends AbstractShardTest { writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME)); listener.waitForChangeEvents(); + listener.verifyOnInitialDataEvent(); + + final MockDataTreeChangeListener listener2 = new MockDataTreeChangeListener(1); + final ActorRef dclActor2 = actorFactory.createActor(DataTreeChangeListenerActor.props(listener2, + TestModel.TEST_PATH), "testRegisterDataTreeChangeListener-DataTreeChangeListener2"); + + shard.tell(new RegisterDataTreeChangeListener(TestModel.TEST_PATH, dclActor2, false), testKit.getRef()); + + testKit.expectMsgClass(Duration.ofSeconds(3), RegisterDataTreeNotificationListenerReply.class); + + listener2.waitForChangeEvents(); + listener2.verifyNoOnInitialDataEvent(); } @SuppressWarnings("serial") diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java index a441ccad63..de41609ba1 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.cluster.datastore.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.util.concurrent.Uninterruptibles; @@ -21,6 +22,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -33,6 +35,9 @@ public class MockDataTreeChangeListener implements DOMDataTreeChangeListener { private final List changeList = new ArrayList<>(); + private final CountDownLatch onInitialDataLatch = new CountDownLatch(1); + private final AtomicInteger onInitialDataEventCount = new AtomicInteger(); + private volatile CountDownLatch changeLatch; private int expChangeEventCount; @@ -58,6 +63,23 @@ public class MockDataTreeChangeListener implements DOMDataTreeChangeListener { } } + @Override + public void onInitialData() { + onInitialDataEventCount.incrementAndGet(); + onInitialDataLatch.countDown(); + } + + public void verifyOnInitialDataEvent() { + assertTrue("onInitialData was not triggered", + Uninterruptibles.awaitUninterruptibly(onInitialDataLatch, 5, TimeUnit.SECONDS)); + assertEquals("onInitialDataEventCount", 1, onInitialDataEventCount.get()); + } + + public void verifyNoOnInitialDataEvent() { + assertFalse("onInitialData was triggered unexpectedly", + Uninterruptibles.awaitUninterruptibly(onInitialDataLatch, 500, TimeUnit.MILLISECONDS)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) public void waitForChangeEvents(final YangInstanceIdentifier... expPaths) { boolean done = Uninterruptibles.awaitUninterruptibly(changeLatch, 5, TimeUnit.SECONDS);