Implement DOMDataTreeChangeListener.onInitialData 02/79802/3
authorTom Pantelis <tompantelis@gmail.com>
Tue, 22 Jan 2019 00:50:01 +0000 (19:50 -0500)
committerTom Pantelis <tompantelis@gmail.com>
Wed, 23 Jan 2019 01:17:44 +0000 (01:17 +0000)
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 <tompantelis@gmail.com>
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DefaultShardDataTreeChangeListenerPublisher.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListener.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeChangePublisherActor.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnInitialData.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java

index c7dfbc914dcfb1c5f9cb15ed9819e8babca15655..c72de945b15154c67c4c1002cd736a72d56aabaa 100644 (file)
@@ -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
index b71174d0b3efbb007810c58b5c3a5df1cce6b234..e17e3faae94fbc864814423542b87ccbb6e8e333 100644 (file)
@@ -63,16 +63,23 @@ final class DefaultShardDataTreeChangeListenerPublisher extends AbstractDOMStore
     public void registerTreeChangeListener(YangInstanceIdentifier treeId, DOMDataTreeChangeListener listener,
             Optional<DataTreeCandidate> initialState,
             Consumer<ListenerRegistration<DOMDataTreeChangeListener>> onRegistration) {
+        registerTreeChangeListener(treeId, listener, onRegistration);
+
+        if (initialState.isPresent()) {
+            notifySingleListener(treeId, listener, initialState.get(), logContext);
+        } else {
+            listener.onInitialData();
+        }
+    }
+
+    void registerTreeChangeListener(YangInstanceIdentifier treeId, DOMDataTreeChangeListener listener,
+            Consumer<ListenerRegistration<DOMDataTreeChangeListener>> onRegistration) {
         LOG.debug("{}: registerTreeChangeListener: path: {}, listener: {}", logContext, treeId, listener);
 
         AbstractDOMDataTreeChangeListenerRegistration<DOMDataTreeChangeListener> 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();
+        }
     }
 }
index dd3155b8ebc958cddaa063b33d62e2e7b90b1e46..41427608e4e7507113113707c12b15ab39838ff3 100644 (file)
@@ -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 + "]";
index 356300ec4c9d038faddfc3c5c7aa4c2e1fa93d80..927e39ff01bf8be284648abdeda81651b77c1aef 100644 (file)
@@ -22,7 +22,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
  * @author Thomas Pantelis
  */
 public final class ShardDataTreeChangePublisherActor
-        extends ShardDataTreeNotificationPublisherActor<ShardDataTreeChangeListenerPublisher> {
+        extends ShardDataTreeNotificationPublisherActor<DefaultShardDataTreeChangeListenerPublisher> {
 
     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 (file)
index 0000000..d2bd499
--- /dev/null
@@ -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
+    }
+}
index 7d4c054b3e9a4e86728feb9e5936ae9550cee888..b3e8de2d9b70af550bfa0017ed3a0d873125da83 100644 (file)
@@ -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")
index a441ccad633c8884ec4d7383a3398ab1eeb99264..de41609ba178e41bab48bc8530eff491cdfe2e26 100644 (file)
@@ -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<DataTreeCandidate> 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);