From 68f0b724084bcf42d775ad1f6e40c02dfcedf3d3 Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Thu, 19 Nov 2015 01:33:58 -0500 Subject: [PATCH] Bug 4651: Add ClusteredDataTreeChangeListener interface and binding adapter Change-Id: I1254a73570ded65925374021341f6900b9a7bdf9 Signed-off-by: Tom Pantelis --- .../api/ClusteredDataTreeChangeListener.java | 26 ++++ .../binding/api/DataTreeChangeListener.java | 3 + ...teredDOMDataTreeChangeListenerAdapter.java | 28 ++++ ...ndingDOMDataTreeChangeListenerAdapter.java | 2 +- ...indingDOMDataTreeChangeServiceAdapter.java | 13 +- ...ngDOMDataTreeChangeServiceAdapterTest.java | 123 ++++++++++++++++++ .../ClusteredDOMDataTreeChangeListener.java | 22 ++++ .../dom/api/DOMDataTreeChangeListener.java | 3 + 8 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataTreeChangeListener.java create mode 100644 opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingClusteredDOMDataTreeChangeListenerAdapter.java create mode 100644 opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapterTest.java create mode 100644 opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataTreeChangeListener.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 index 0000000000..557541424e --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataTreeChangeListener.java @@ -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. + *

+ * 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 the DataObject type + */ +public interface ClusteredDataTreeChangeListener extends DataTreeChangeListener { + +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java index 93ab968451..d52835b7ed 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java @@ -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. + *

+ * 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 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 index 0000000000..406af3a60e --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingClusteredDOMDataTreeChangeListenerAdapter.java @@ -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 + extends BindingDOMDataTreeChangeListenerAdapter implements ClusteredDOMDataTreeChangeListener { + BindingClusteredDOMDataTreeChangeListenerAdapter(BindingToNormalizedNodeCodec codec, + ClusteredDataTreeChangeListener listener, LogicalDatastoreType store) { + super(codec, listener, store); + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java index ab1348fda2..ecf5db048c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java @@ -22,7 +22,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; * to their Binding equivalent. * */ -final class BindingDOMDataTreeChangeListenerAdapter implements DOMDataTreeChangeListener { +class BindingDOMDataTreeChangeListenerAdapter implements DOMDataTreeChangeListener { private final BindingToNormalizedNodeCodec codec; private final DataTreeChangeListener listener; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java index 02daa4559a..d693e66b95 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java @@ -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 > ListenerRegistration registerDataTreeChangeListener( final DataTreeIdentifier treeId, final L listener) { final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId); - final BindingDOMDataTreeChangeListenerAdapter domListener = new BindingDOMDataTreeChangeListenerAdapter<>(codec,listener, treeId.getDatastoreType()); - final ListenerRegistration> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + final BindingDOMDataTreeChangeListenerAdapter domListener = + listener instanceof ClusteredDataTreeChangeListener ? + new BindingClusteredDOMDataTreeChangeListenerAdapter( + codec, (ClusteredDataTreeChangeListener)listener, treeId.getDatastoreType()) : + new BindingDOMDataTreeChangeListenerAdapter(codec, listener, treeId.getDatastoreType()); + + final ListenerRegistration> 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 index 0000000000..894b80e56f --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapterTest.java @@ -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_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 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() { + @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 { + @Override + public void onDataTreeChanged(Collection> changes) { + } + } + + private static class TestDataTreeChangeListener implements DataTreeChangeListener { + @Override + public void onDataTreeChanged(Collection> 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 index 0000000000..5da80e4907 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataTreeChangeListener.java @@ -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. + *

+ * 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 { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java index 257879070d..b922a5db29 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java @@ -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. + *

+ * 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 { /** -- 2.36.6