Bug 4651: Add ClusteredDataTreeChangeListener interface and binding adapter 42/29942/2
authorTom Pantelis <tpanteli@brocade.com>
Thu, 19 Nov 2015 06:33:58 +0000 (01:33 -0500)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 20 Nov 2015 12:17:52 +0000 (12:17 +0000)
Change-Id: I1254a73570ded65925374021341f6900b9a7bdf9
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingClusteredDOMDataTreeChangeListenerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapterTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java

diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataTreeChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..5575414
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, 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.md.sal.binding.api;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * ClusteredDataTreeChangeListener is a marker interface to enable data tree change notifications on all
+ * instances in a cluster where this listener is registered.
+ * <p>
+ * Applications should implement ClusteredDataTreeChangeListener instead of {@link DataTreeChangeListener},
+ * if they want to listen for data tree change notifications on any node of a clustered data store.
+ * {@link DataTreeChangeListener} enables notifications only at the leader of the data store.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <T> the DataObject type
+ */
+public interface ClusteredDataTreeChangeListener<T extends DataObject> extends DataTreeChangeListener<T> {
+
+}
index 93ab968451be9882e91379c867ae11a13dc111ad..d52835b7ed090dc4972b83b366ae859c35899997 100644 (file)
@@ -17,6 +17,9 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
  * data tree changes. This interface differs from {@link DataChangeListener}
  * in that it provides a cursor-based view of the change, which has potentially
  * lower overhead and allow more flexible consumption of change event.
+ * <p>
+ * Note: this interface enables notifications only at the leader of the data store, if clustered. If you want
+ * notifications on all instances in a cluster, use the {@link ClusteredDataTreeChangeListener}.
  */
 public interface DataTreeChangeListener<T extends DataObject> extends EventListener {
     /**
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingClusteredDOMDataTreeChangeListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingClusteredDOMDataTreeChangeListenerAdapter.java
new file mode 100644 (file)
index 0000000..406af3a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, 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.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Adapter wrapping Binding {@link ClusteredDataTreeChangeListener} and exposing
+ * it as {@link ClusteredDOMDataTreeChangeListener} and translated DOM events
+ * to their Binding equivalent.
+ *
+ * @author Thomas Pantelis
+ */
+final class BindingClusteredDOMDataTreeChangeListenerAdapter<T extends DataObject>
+        extends BindingDOMDataTreeChangeListenerAdapter<T> implements ClusteredDOMDataTreeChangeListener {
+    BindingClusteredDOMDataTreeChangeListenerAdapter(BindingToNormalizedNodeCodec codec,
+            ClusteredDataTreeChangeListener<T> listener, LogicalDatastoreType store) {
+        super(codec, listener, store);
+    }
+}
index ab1348fda2c4c85f794cd9b16024fd94ce483569..ecf5db048c5d220a5902aa538a49ad6fdad239ca 100644 (file)
@@ -22,7 +22,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
  * to their Binding equivalent.
  *
  */
-final class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
+class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
 
     private final BindingToNormalizedNodeCodec codec;
     private final DataTreeChangeListener<T> listener;
index 02daa4559aba652ade0b7662403aa0431e27a4fd..d693e66b95e5aebb4e6c8de0951b6f0161f3334d 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.controller.md.sal.binding.impl;
 
 import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
@@ -48,8 +49,16 @@ final class BindingDOMDataTreeChangeServiceAdapter implements DataTreeChangeServ
     public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
             final DataTreeIdentifier<T> treeId, final L listener) {
         final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
-        final BindingDOMDataTreeChangeListenerAdapter<T> domListener = new BindingDOMDataTreeChangeListenerAdapter<>(codec,listener, treeId.getDatastoreType());
-        final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        final BindingDOMDataTreeChangeListenerAdapter<T> domListener =
+                listener instanceof ClusteredDataTreeChangeListener ?
+                        new BindingClusteredDOMDataTreeChangeListenerAdapter<T>(
+                                codec, (ClusteredDataTreeChangeListener)listener, treeId.getDatastoreType()) :
+                        new BindingDOMDataTreeChangeListenerAdapter<T>(codec, listener, treeId.getDatastoreType());
+
+        final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg =
+                dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
         return new BindingDataTreeChangeListenerRegistration<>(listener,domReg);
     }
 
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapterTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapterTest.java
new file mode 100644 (file)
index 0000000..894b80e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, 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.md.sal.binding.impl;
+
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import java.util.Collection;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Unit tests for BindingDOMDataTreeChangeServiceAdapter.
+ *
+ * @author Thomas Pantelis
+ */
+public class BindingDOMDataTreeChangeServiceAdapterTest {
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+    @Mock
+    private DOMDataTreeChangeService mockDOMService;
+
+    @Mock
+    private GeneratedClassLoadingStrategy classLoadingStrategy;
+
+    @Mock
+    private BindingNormalizedNodeCodecRegistry codecRegistry;
+
+    @Mock
+    private YangInstanceIdentifier mockYangID;
+
+    @SuppressWarnings("rawtypes")
+    @Mock
+    private ListenerRegistration mockDOMReg;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mockYangID).when(codecRegistry).toYangInstanceIdentifier(TOP_PATH);
+    }
+
+    @Test
+    public void testRegisterDataTreeChangeListener() {
+        BindingToNormalizedNodeCodec codec = new BindingToNormalizedNodeCodec(classLoadingStrategy, codecRegistry);
+
+        DataTreeChangeService service = BindingDOMDataTreeChangeServiceAdapter.create(codec, mockDOMService);
+
+        doReturn(mockDOMReg).when(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(mockYangID),
+                any(DOMDataTreeChangeListener.class));
+        DataTreeIdentifier<Top> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, TOP_PATH);
+        TestClusteredDataTreeChangeListener mockClusteredListener = new TestClusteredDataTreeChangeListener();
+        service.registerDataTreeChangeListener(treeId , mockClusteredListener);
+
+        verify(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(mockYangID),
+                isA(ClusteredDOMDataTreeChangeListener.class));
+
+        reset(mockDOMService);
+        doReturn(mockDOMReg).when(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(mockYangID),
+                any(DOMDataTreeChangeListener.class));
+        TestDataTreeChangeListener mockNonClusteredListener = new TestDataTreeChangeListener();
+        service.registerDataTreeChangeListener(treeId , mockNonClusteredListener);
+
+        verify(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(mockYangID),
+                not(isA(ClusteredDOMDataTreeChangeListener.class)));
+    }
+
+    static DOMDataTreeIdentifier domDataTreeIdentifier(final YangInstanceIdentifier yangID) {
+        return Matchers.argThat(new ArgumentMatcher<DOMDataTreeIdentifier>() {
+            @Override
+            public boolean matches(Object argument) {
+                DOMDataTreeIdentifier treeId = (DOMDataTreeIdentifier) argument;
+                return treeId.getDatastoreType() == LogicalDatastoreType.CONFIGURATION &&
+                        yangID.equals(treeId.getRootIdentifier());
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, yangID));
+            }
+        });
+    }
+
+    private static class TestClusteredDataTreeChangeListener implements ClusteredDataTreeChangeListener<Top> {
+        @Override
+        public void onDataTreeChanged(Collection<DataTreeModification<Top>> changes) {
+        }
+    }
+
+    private static class TestDataTreeChangeListener implements DataTreeChangeListener<Top> {
+        @Override
+        public void onDataTreeChanged(Collection<DataTreeModification<Top>> changes) {
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataTreeChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..5da80e4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, 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.md.sal.dom.api;
+
+/**
+ *  ClusteredDOMDataTreeChangeListener is a marker interface to enable data tree change notifications on all
+ * instances in a cluster where this listener is registered.
+ * <p>
+ * Applications should implement ClusteredDOMDataTreeChangeListener instead of {@link DOMDataTreeChangeListener},
+ * if they want to listen for data tree change notifications on any node of a clustered data store.
+ * {@link DOMDataTreeChangeListener} enables notifications only at the leader of the data store.
+ *
+ * @author Thomas Pantelis
+ */
+public interface ClusteredDOMDataTreeChangeListener extends DOMDataTreeChangeListener {
+
+}
index 257879070d22ad160d702714a5dd23ebcb0108b1..b922a5db29b4cd89d7d282137677438db7106701 100644 (file)
@@ -17,6 +17,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
  * data tree changes. This interface differs from {@link DOMDataChangeListener}
  * in that it provides a cursor-based view of the change, which has potentially
  * lower overhead.
+ * <p>
+ * Note: this interface enables notifications only at the leader of the data store, if clustered. If you want
+ * notifications on all instances in a cluster, use the {@link ClusteredDOMDataTreeChangeListener}.
  */
 public interface DOMDataTreeChangeListener extends EventListener {
     /**