* 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
+ * @author Thomas Pantelis
+ * @deprecated Use plain {@link DataTreeChangeListener} and use
+ * {@link DataTreeChangeService#registerDataChangeListener(DataTreeIdentifier, DataChangeListener)}
+ * instead.
*/
+@Deprecated(since = "13.0.0", forRemoval = true)
public interface ClusteredDataTreeChangeListener<T extends DataObject> extends DataTreeChangeListener<T> {
+ // Just a marker interface
}
import org.opendaylight.yangtools.yang.binding.DataObject;
final class DataChangeListenerAdapter<T extends DataObject> extends ForwardingObject
- implements ClusteredDataTreeChangeListener<T> {
+ implements DataTreeChangeListener<T> {
private final DataChangeListener<T> delegate;
DataChangeListenerAdapter(final DataChangeListener<T> delegate) {
import java.util.List;
import org.opendaylight.yangtools.yang.binding.DataObject;
-final class DataListenerAdapter<T extends DataObject> extends ForwardingObject
- implements ClusteredDataTreeChangeListener<T> {
+final class DataListenerAdapter<T extends DataObject> extends ForwardingObject implements DataTreeChangeListener<T> {
private final DataListener<T> delegate;
DataListenerAdapter(final DataListener<T> delegate) {
* @return a Registration object, which may be used to unregister your listener using {@link Registration#close()}
* to stop delivery of change events.
*/
- <T extends DataObject> @NonNull Registration registerDataTreeChangeListener(@NonNull DataTreeIdentifier<T> treeId,
+ <T extends DataObject> @NonNull Registration registerTreeChangeListener(
+ @NonNull DataTreeIdentifier<T> treeId, @NonNull DataTreeChangeListener<T> listener);
+
+ /**
+ * Registers a {@link DataTreeChangeListener} to receive notifications when data changes under a given path in the
+ * conceptual data tree, with legacy semantics, where no events are delivered if this "cluster node" (further
+ * undefined) is a "leader" (also not explicitly undefined).
+ *
+ * <p>
+ * The sole known implementation, the Akka-based datastore, defines the difference in terms of RAFT, suspending
+ * even delivery when the RAFT leader is not local. Even when there may be valid use cases for this, RAFT there
+ * is a storage backend whose lifecycle is disconnected from this object.
+ *
+ * <p>
+ * Aside from the above difference, this method is equivalent to
+ * {@link #registerTreeChangeListener(DataTreeIdentifier, DataTreeChangeListener)}. If you are unable to migrate,
+ * please contact us on <a href="email:discuss@lists.opendaylight.org">the mailing list</a>
+ *
+ * @param treeId Data tree identifier of the subtree which should be watched for changes.
+ * @param listener Listener instance which is being registered
+ * @return A {@link Registration} object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @throws NullPointerException if any of the arguments is {@code null}
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ <T extends DataObject> @NonNull Registration registerLegacyTreeChangeListener(@NonNull DataTreeIdentifier<T> treeId,
@NonNull DataTreeChangeListener<T> listener);
+ /**
+ * Registers a {@link DataTreeChangeListener} to receive notifications when data changes under a given path in the
+ * conceptual data tree.
+ *
+ * <p>
+ * You are able to register for notifications for any node or subtree which can be represented using
+ * {@link DataTreeIdentifier}.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * If there is any pre-existing data in the data tree for the path for which you are registering, you will receive
+ * an initial data change event, which will contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link Registration} object. To "unregister" your listener for changes call the
+ * {@link Registration#close()} method on the returned object.
+ *
+ * <p>
+ * You <b>MUST</b> explicitly unregister your listener when you no longer want to receive 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
+ * @return a Registration object, which may be used to unregister your listener using {@link Registration#close()}
+ * to stop delivery of change events.
+ * @deprecated This interface relies on magic of {@link ClusteredDataTreeChangeListener}. See
+ * {@link #registerLegacyTreeChangeListener(DataTreeIdentifier, DataTreeChangeListener)} for migration
+ * guidance.
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ default <T extends DataObject> @NonNull Registration registerDataTreeChangeListener(
+ final @NonNull DataTreeIdentifier<T> treeId, final @NonNull DataTreeChangeListener<T> listener) {
+ return listener instanceof ClusteredDataTreeChangeListener ? registerTreeChangeListener(treeId, listener)
+ : registerLegacyTreeChangeListener(treeId, listener);
+ }
+
+ /**
+ * Registers a {@link ClusteredDataTreeChangeListener} to receive notifications when data changes under a given path
+ * in the conceptual data tree. This is a migration shorthand for
+ * {@code registerDataTreeListener(treeId, listener)}.
+ *
+ * @param treeId Data tree identifier of the subtree which should be watched for changes.
+ * @param listener Listener instance which is being registered
+ * @return A {@link Registration} object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @throws NullPointerException if any of the arguments is {@code null}
+ * @deprecated Use {@link #registerTreeChangeListener(DataTreeIdentifier, DataTreeChangeListener)} instead.
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ default <T extends DataObject> @NonNull Registration registerDataTreeChangeListener(
+ final @NonNull DataTreeIdentifier<T> treeId, final @NonNull ClusteredDataTreeChangeListener<T> listener) {
+ return registerTreeChangeListener(treeId, listener);
+ }
+
/**
* Registers a {@link DataTreeChangeListener} to receive notifications when data changes under a given path in the
* conceptual data tree.
*/
default <T extends DataObject> @NonNull Registration registerDataListener(
final @NonNull DataTreeIdentifier<T> treeId, final @NonNull DataListener<T> listener) {
- return registerDataTreeChangeListener(checkNotWildcard(treeId), new DataListenerAdapter<>(listener));
+ return registerTreeChangeListener(checkNotWildcard(treeId), new DataListenerAdapter<>(listener));
}
/**
*/
default <T extends DataObject> @NonNull Registration registerDataChangeListener(
final @NonNull DataTreeIdentifier<T> treeId, final @NonNull DataChangeListener<T> listener) {
- return registerDataTreeChangeListener(checkNotWildcard(treeId), new DataChangeListenerAdapter<>(listener));
+ return registerTreeChangeListener(checkNotWildcard(treeId), new DataChangeListenerAdapter<>(listener));
}
private static <T extends DataObject> @NonNull DataTreeIdentifier<T> checkNotWildcard(
+++ /dev/null
-/*
- * Copyright (c) 2018 Inocybe Technologies 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.mdsal.binding.dom.adapter;
-
-import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
-import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.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(final AdapterContext codec,
- final ClusteredDataTreeChangeListener<T> listener, final LogicalDatastoreType store,
- final Class<T> augment) {
- super(codec, listener, store, augment);
- }
-}
}
@Override
- public <T extends DataObject> Registration registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ public <T extends DataObject> Registration registerTreeChangeListener(final DataTreeIdentifier<T> treeId,
final DataTreeChangeListener<T> listener) {
- if (treeChangeService == null) {
+ return getTreeChangeService().registerTreeChangeListener(treeId, listener);
+ }
+
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public <T extends DataObject> Registration registerLegacyTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return getTreeChangeService().registerLegacyTreeChangeListener(treeId, listener);
+ }
+
+ private @NonNull DataTreeChangeService getTreeChangeService() {
+ final var local = treeChangeService;
+ if (local == null) {
throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService.");
}
- return treeChangeService.registerDataTreeChangeListener(treeId, listener);
+ return local;
}
private static class Builder extends BindingDOMAdapterBuilder<DataBroker> {
return new BindingDOMDataBrokerAdapter(adapterContext(), delegates.getInstance(DOMDataBroker.class));
}
}
-
}
import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
-import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
-final class BindingDOMDataChangeListenerAdapter<T extends DataObject> implements ClusteredDOMDataTreeChangeListener {
+final class BindingDOMDataChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
private final AdapterContext adapterContext;
private final DataChangeListener<T> listener;
import org.opendaylight.mdsal.binding.api.DataListener;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
-import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
-final class BindingDOMDataListenerAdapter<T extends DataObject> implements ClusteredDOMDataTreeChangeListener {
+final class BindingDOMDataListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
private final AdapterContext adapterContext;
private final DataListener<T> listener;
import static java.util.Objects.requireNonNull;
import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
* Adapter wrapping Binding {@link DataTreeChangeListener} and exposing it as {@link DOMDataTreeChangeListener}
* and translated DOM events to their Binding equivalent.
*/
-class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
+@NonNullByDefault
+final class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
private final AdapterContext adapterContext;
private final DataTreeChangeListener<T> listener;
private final LogicalDatastoreType store;
- private final Class<T> augment;
+ private final @Nullable Class<? extends Augmentation<?>> augment;
private boolean initialSyncDone;
- BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext,
- final DataTreeChangeListener<T> listener, final LogicalDatastoreType store, final Class<T> augment) {
+ BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext, final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
this.adapterContext = requireNonNull(adapterContext);
this.listener = requireNonNull(listener);
- this.store = requireNonNull(store);
- this.augment = augment;
+ store = treeId.datastore();
+ augment = extractAugment(treeId.path().getTargetType());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static @Nullable Class<? extends Augmentation<?>> extractAugment(final Class<?> target) {
+ return Augmentation.class.isAssignableFrom(target)
+ ? (Class<? extends Augmentation<?>>) target.asSubclass(Augmentation.class) : null;
}
@Override
package org.opendaylight.mdsal.binding.dom.adapter;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
import org.opendaylight.mdsal.binding.api.DataChangeListener;
import org.opendaylight.mdsal.binding.api.DataListener;
import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
/**
}
@Override
- public <T extends DataObject> Registration registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ public <T extends DataObject> Registration registerTreeChangeListener(final DataTreeIdentifier<T> treeId,
final DataTreeChangeListener<T> listener) {
- final var domIdentifier = toDomTreeIdentifier(treeId);
- final var storeType = treeId.datastore();
- final var target = treeId.path().getTargetType();
- final var augment = Augmentation.class.isAssignableFrom(target) ? target : null;
-
- final var domListener = listener instanceof ClusteredDataTreeChangeListener
- ? new BindingClusteredDOMDataTreeChangeListenerAdapter<>(adapterContext(),
- (ClusteredDataTreeChangeListener<T>) listener, storeType, augment)
- : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType, augment);
+ return getDelegate().registerTreeChangeListener(toDomTreeIdentifier(treeId),
+ new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), treeId, listener));
+ }
- return getDelegate().registerDataTreeChangeListener(domIdentifier, domListener);
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public <T extends DataObject> Registration registerLegacyTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return getDelegate().registerLegacyTreeChangeListener(toDomTreeIdentifier(treeId),
+ new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), treeId, listener));
}
@Override
public <T extends DataObject> Registration registerDataListener(final DataTreeIdentifier<T> treeId,
final DataListener<T> listener) {
- return getDelegate().registerDataTreeChangeListener(toDomTreeInstance(treeId),
+ return getDelegate().registerTreeChangeListener(toDomTreeInstance(treeId),
new BindingDOMDataListenerAdapter<>(adapterContext(), listener));
}
@Override
public <T extends DataObject> Registration registerDataChangeListener(final DataTreeIdentifier<T> treeId,
final DataChangeListener<T> listener) {
- return getDelegate().registerDataTreeChangeListener(toDomTreeInstance(treeId),
+ return getDelegate().registerTreeChangeListener(toDomTreeInstance(treeId),
new BindingDOMDataChangeListenerAdapter<>(adapterContext(), listener));
}
@SuppressWarnings({"unchecked", "rawtypes"})
static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
- final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, final Class<T> augment) {
+ final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType,
+ final @Nullable Class<? extends Augmentation<?>> augment) {
final var bindingPath = createBindingPath(serializer, domChange.getRootPath(), augment);
final var codec = serializer.getSubtreeCodec(bindingPath);
final var modification = LazyDataObjectModification.from(codec, domChange.getRootNode());
: new LazyDataTreeModification(DataTreeIdentifier.of(datastoreType, bindingPath), modification);
}
- @SuppressWarnings({"unchecked", "rawtypes"})
static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
- final DOMDataTreeCandidate candidate, final Class<T> augment) {
+ final DOMDataTreeCandidate candidate, final @Nullable Class<T> augment) {
final var domRootPath = candidate.getRootPath();
- final var bindingPath = createBindingPath(serializer, domRootPath.path(), augment);
+ @SuppressWarnings("unchecked")
+ final var bindingPath = (InstanceIdentifier<T>) createBindingPath(serializer, domRootPath.path(), augment);
final var codec = serializer.getSubtreeCodec(bindingPath);
- final var modification = LazyDataObjectModification.from(codec, candidate.getRootNode());
+ @SuppressWarnings("unchecked")
+ final var modification = (DataObjectModification<T>) LazyDataObjectModification.from(codec,
+ candidate.getRootNode());
return modification == null ? null
- : new LazyDataTreeModification(DataTreeIdentifier.of(domRootPath.datastore(), bindingPath), modification);
+ : new LazyDataTreeModification<>(DataTreeIdentifier.of(domRootPath.datastore(), bindingPath), modification);
}
static <T extends DataObject> @NonNull List<DataTreeModification<T>> from(final CurrentAdapterSerializer codec,
final List<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType,
- final Class<T> augment) {
+ final @Nullable Class<? extends Augmentation<?>> augment) {
final var result = new ArrayList<DataTreeModification<T>>(domChanges.size());
for (var domChange : domChanges) {
- final var bindingChange = from(codec, domChange, datastoreType, augment);
+ final var bindingChange = LazyDataTreeModification.<T>from(codec, domChange, datastoreType, augment);
if (bindingChange != null) {
result.add(bindingChange);
}
// codec and mis-report what is actually going on.
@SuppressWarnings({"unchecked", "rawtypes"})
private static @NonNull InstanceIdentifier<?> createBindingPath(final CurrentAdapterSerializer serializer,
- final YangInstanceIdentifier domPath, final Class<?> augment) {
+ final YangInstanceIdentifier domPath, final @Nullable Class<?> augment) {
final var bindingPath = serializer.coerceInstanceIdentifier(domPath);
return augment == null ? bindingPath : bindingPath.augmentation((Class) augment.asSubclass(Augmentation.class));
}
return delegate.newWriteOnlyTransaction();
}
- @Override
- public <T extends DataObject> Registration registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
- final DataTreeChangeListener<T> listener) {
- return delegate.registerDataTreeChangeListener(treeId, listener);
- }
-
@Override
public TransactionChain createTransactionChain() {
return delegate.createTransactionChain();
public TransactionChain createMergingTransactionChain() {
return delegate.createMergingTransactionChain();
}
+
+ @Override
+ public <T extends DataObject> Registration registerTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return delegate.registerTreeChangeListener(treeId, listener);
+ }
+
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public <T extends DataObject> Registration registerLegacyTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return delegate.registerLegacyTreeChangeListener(treeId, listener);
+ }
}
*/
package org.opendaylight.mdsal.binding.dom.adapter;
-import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.eclipse.jdt.annotation.NonNull;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
-import org.opendaylight.mdsal.binding.api.DataTreeChangeService;
import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
import org.opendaylight.yangtools.concepts.Registration;
*
* @author Thomas Pantelis
*/
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class BindingDOMDataTreeChangeServiceAdapterTest {
+@ExtendWith(MockitoExtension.class)
+class BindingDOMDataTreeChangeServiceAdapterTest {
private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
private static final YangInstanceIdentifier TOP_YIID = YangInstanceIdentifier.of(Top.QNAME);
@Mock
private Registration mockDOMReg;
- @Before
+ @BeforeEach
public void setUp() {
doReturn(TOP_YIID).when(services).toYangInstanceIdentifier(TOP_PATH);
}
@Test
- public void testRegisterDataTreeChangeListener() {
- final AdapterContext codec = new ConstantAdapterContext(services);
+ void testRegisterDataTreeChangeListener() {
+ final var codec = new ConstantAdapterContext(services);
- final DataTreeChangeService service = new BindingDOMDataTreeChangeServiceAdapter(codec, mockDOMService);
+ final var service = new BindingDOMDataTreeChangeServiceAdapter(codec, mockDOMService);
- doReturn(mockDOMReg).when(mockDOMService).registerDataTreeChangeListener(
- domDataTreeIdentifier(TOP_YIID),
- any(DOMDataTreeChangeListener.class));
- final DataTreeIdentifier<Top> treeId = DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, TOP_PATH);
- final TestClusteredDataTreeChangeListener mockClusteredListener = new TestClusteredDataTreeChangeListener();
- service.registerDataTreeChangeListener(treeId , mockClusteredListener);
+ doReturn(mockDOMReg).when(mockDOMService).registerTreeChangeListener(domDataTreeIdentifier(TOP_YIID), any());
+ final var treeId = DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, TOP_PATH);
+ service.registerDataTreeChangeListener(treeId, new TestClusteredDataTreeChangeListener());
- verify(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(TOP_YIID),
- isA(ClusteredDOMDataTreeChangeListener.class));
-
- reset(mockDOMService);
- doReturn(mockDOMReg).when(mockDOMService).registerDataTreeChangeListener(
- domDataTreeIdentifier(TOP_YIID), any(DOMDataTreeChangeListener.class));
- final TestDataTreeChangeListener mockNonClusteredListener = new TestDataTreeChangeListener();
- service.registerDataTreeChangeListener(treeId , mockNonClusteredListener);
-
- verify(mockDOMService).registerDataTreeChangeListener(domDataTreeIdentifier(TOP_YIID),
- not(isA(ClusteredDOMDataTreeChangeListener.class)));
+ doReturn(mockDOMReg).when(mockDOMService).registerLegacyTreeChangeListener(domDataTreeIdentifier(TOP_YIID),
+ any());
+ service.registerDataTreeChangeListener(treeId, new TestDataTreeChangeListener());
}
- static DOMDataTreeIdentifier domDataTreeIdentifier(final YangInstanceIdentifier yangID) {
+ private static @NonNull DOMDataTreeIdentifier domDataTreeIdentifier(final YangInstanceIdentifier yangID) {
return argThat(arg -> arg.datastore() == LogicalDatastoreType.CONFIGURATION && yangID.equals(arg.path()));
}
+ @Deprecated(since = "13.0.0", forRemoval = true)
private static final class TestClusteredDataTreeChangeListener implements ClusteredDataTreeChangeListener<Top> {
@Override
public void onDataTreeChanged(final List<DataTreeModification<Top>> changes) {
return delegate().newWriteOnlyTransaction();
}
- @Override
- public <T extends DataObject> Registration registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
- final DataTreeChangeListener<T> listener) {
- return delegate().registerDataTreeChangeListener(treeId, listener);
- }
-
@Override
public TransactionChain createTransactionChain() {
return delegate().createTransactionChain();
public TransactionChain createMergingTransactionChain() {
return delegate().createMergingTransactionChain();
}
+
+ @Override
+ public <T extends DataObject> Registration registerTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return delegate().registerTreeChangeListener(treeId, listener);
+ }
+
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public <T extends DataObject> Registration registerLegacyTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final DataTreeChangeListener<T> listener) {
+ return delegate().registerLegacyTreeChangeListener(treeId, listener);
+ }
}
*/
package org.opendaylight.mdsal.dom.api;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
+
/**
- * ClusteredDOMDataTreeChangeListener is a marker interface to enable data tree change notifications on all
- * instances in a cluster where this listener is registered.
+ * 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.
*
+ * @deprecated Use plain {@link DOMDataTreeChangeListener} and use
+ * {@link DataTreeChangeExtension#registerTreeChangeListener(DOMDataTreeIdentifier,
+ * DOMDataTreeChangeListener)} instead.
* @author Thomas Pantelis
*/
+@Deprecated(since = "13.0.0", forRemoval = true)
public interface ClusteredDOMDataTreeChangeListener extends DOMDataTreeChangeListener {
+ // Just a marker
}
* {@link Registration#close()} to stop delivery of change events.
* @throws NullPointerException if any of the arguments is {@code null}
*/
- Registration registerDataTreeChangeListener(DOMDataTreeIdentifier treeId, DOMDataTreeChangeListener listener);
+ Registration registerTreeChangeListener(DOMDataTreeIdentifier treeId, DOMDataTreeChangeListener listener);
+
+ /**
+ * Registers a {@link DOMDataTreeChangeListener} to receive notifications when data changes under a given path
+ * in the conceptual data tree.
+ *
+ * <p>
+ * You are able to register for notifications for any node or subtree which can be represented using
+ * {@link DOMDataTreeIdentifier}.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * If there is any pre-existing data in the data tree for the path for which you are registering, you will
+ * receive an initial data change event, which will contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link Registration} object. To "unregister" your listener for changes call
+ * the {@link Registration#close()} method on the returned object.
+ *
+ * <p>
+ * You MUST explicitly unregister your listener when you no longer want to receive 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
+ * @return A {@link Registration} object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @throws NullPointerException if any of the arguments is {@code null}
+ * @deprecated This interface relies on magic of {@link ClusteredDOMDataTreeChangeListener}. See
+ * {@link #registerLegacyTreeChangeListener(DOMDataTreeIdentifier, DOMDataTreeChangeListener)} for
+ * migration guidance.
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ default Registration registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ return listener instanceof ClusteredDOMDataTreeChangeListener clustered
+ ? registerDataTreeChangeListener(treeId, clustered)
+ : registerLegacyTreeChangeListener(treeId, listener);
+ }
+
+ /**
+ * Registers a {@link ClusteredDOMDataTreeChangeListener} to receive notifications when data changes under a
+ * given path in the conceptual data tree. This is a migration shorthand for
+ * {@code registerDataTreeListener(treeId, listener)}.
+ *
+ * @param treeId Data tree identifier of the subtree which should be watched for changes.
+ * @param listener Listener instance which is being registered
+ * @return A {@link Registration} object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @throws NullPointerException if any of the arguments is {@code null}
+ * @deprecated Use {@link #registerTreeChangeListener(DOMDataTreeIdentifier, DOMDataTreeChangeListener)}
+ * instead.
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ default Registration registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final ClusteredDOMDataTreeChangeListener listener) {
+ return registerTreeChangeListener(treeId, listener);
+ }
+
+ /**
+ * Registers a {@link DOMDataTreeChangeListener} to receive notifications when data changes under a given path
+ * in the conceptual data tree, with legacy semantics, where no events are delivered if this "cluster node"
+ * (further undefined) is a "leader" (also not explicitly undefined).
+ *
+ * <p>
+ * The sole known implementation, the Akka-based datastore, defines the difference in terms of RAFT, suspending
+ * even delivery when the RAFT leader is not local. Even when there may be valid use cases for this, RAFT there
+ * is a storage backend whose lifecycle is disconnected from this object.
+ *
+ * <p>
+ * Aside from the above difference, this method is equivalent to
+ * {@link #registerTreeChangeListener(DOMDataTreeIdentifier, DOMDataTreeChangeListener)}. If you are unable to
+ * migrate, please contact us on <a href="email:discuss@lists.opendaylight.org">the mailing list</a>
+ *
+ * @param treeId Data tree identifier of the subtree which should be watched for changes.
+ * @param listener Listener instance which is being registered
+ * @return A {@link Registration} object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @throws NullPointerException if any of the arguments is {@code null}
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ Registration registerLegacyTreeChangeListener(DOMDataTreeIdentifier treeId, DOMDataTreeChangeListener listener);
}
}
return changePublisher.registerTreeChangeListener(treeId, listener, dataTree.takeSnapshot());
}
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public Registration registerLegacyTreeChangeListener(final YangInstanceIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ return registerTreeChangeListener(treeId, listener);
+ }
+
@Override
protected void transactionAborted(final SnapshotBackedWriteTransaction<String> tx) {
LOG.debug("Tx: {} is closed.", tx.getIdentifier());
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.mdsal.dom.spi.store.DOMStore;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.concepts.Registration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final var builder = ImmutableList.<Extension>builder();
if (isSupported(datastores, DOMStoreTreeChangePublisher.class)) {
- builder.add((DataTreeChangeExtension) (treeId, listener) -> {
- final var dsType = treeId.datastore();
- if (getTxFactories().get(dsType) instanceof DOMStoreTreeChangePublisher publisher) {
- return publisher.registerTreeChangeListener(treeId.path(), listener);
+ builder.add(new DataTreeChangeExtension() {
+ @Override
+ public Registration registerTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ return getPublisher(treeId.datastore()).registerTreeChangeListener(treeId.path(), listener);
+ }
+
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public Registration registerLegacyTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ return getPublisher(treeId.datastore()).registerLegacyTreeChangeListener(treeId.path(), listener);
+ }
+
+ private DOMStoreTreeChangePublisher getPublisher(final LogicalDatastoreType datastore) {
+ if (getTxFactories().get(datastore) instanceof DOMStoreTreeChangePublisher publisher) {
+ return publisher;
+ }
+ throw new IllegalStateException("Publisher for " + datastore + " data store is not available");
}
- throw new IllegalStateException("Publisher for " + dsType + " data store is not available");
});
}
if (isSupported(datastores, CommitCohortExtension.class)) {
}
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>
+ * This implementation calls {@link #registerTreeChangeListener(YangInstanceIdentifier, DOMDataTreeChangeListener)},
+ * override if necessary.
+ */
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public Registration registerLegacyTreeChangeListener(final YangInstanceIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ return registerTreeChangeListener(treeId, listener);
+ }
+
private void lookupAndNotify(final List<PathArgument> args, final int offset, final Node<RegImpl> node,
final DataTreeCandidate candidate, final Map<Reg, List<DataTreeCandidate>> listenerChanges) {
if (args.size() == offset) {
package org.opendaylight.mdsal.dom.spi.store;
import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
public interface DOMStoreTreeChangePublisher {
/**
* Registers a {@link DOMDataTreeChangeListener} to receive notifications when data changes under a given path in
- * the conceptual data tree.
- *
- * <p>
- * You are able to register for notifications for any node or subtree which can be represented using
- * {@link YangInstanceIdentifier}.
- *
- * <p>
- * 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.
- *
- * <p>
- * 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. 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.
- *
- * <p>
- * This method returns a {@link Registration} object. To "unregister" your listener for changes call the
- * {@link Registration#close()} method on this returned object.
- *
- * <p>
- * You MUST explicitly unregister your listener when you no longer want to receive
- * notifications. This is especially true in OSGi environments, where failure to
- * do so during bundle shutdown can lead to stale listeners being still registered.
+ * the conceptual data tree. See
+ * {@link DataTreeChangeExtension#registerTreeChangeListener(DOMDataTreeIdentifier, DOMDataTreeChangeListener)} for
+ * full semantics.
*
* @param treeId Data tree identifier of the subtree which should be watched for changes.
* @param listener Listener instance which is being registered
*/
@NonNull Registration registerTreeChangeListener(@NonNull YangInstanceIdentifier treeId,
@NonNull DOMDataTreeChangeListener listener);
+
+ /**
+ * Registers a {@link DOMDataTreeChangeListener} to receive notifications when data changes under a given path in
+ * the conceptual data tree. See {@link DataTreeChangeExtension#registerLegacyTreeChangeListener(
+ * DOMDataTreeIdentifier, DOMDataTreeChangeListener)} for full semantics.
+ *
+ * @param treeId Data tree identifier of the subtree which should be watched for changes.
+ * @param listener Listener instance which is being registered
+ * @return A {@link Registration} registration object, which may be used to unregister your listener using
+ * {@link Registration#close()} to stop delivery of change events.
+ * @deprecated Legacy support class
+ */
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ @NonNull Registration registerLegacyTreeChangeListener(@NonNull YangInstanceIdentifier treeId,
+ @NonNull DOMDataTreeChangeListener listener);
}
import java.util.Set;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.mdsal.trace.api.TracingDOMDataBroker;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.Config;
+import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
public <T extends Extension> T extension(final Class<T> type) {
final var ext = delegate.extension(type);
if (DataTreeChangeExtension.class.equals(type) && ext instanceof DataTreeChangeExtension treeChange) {
- return type.cast((DataTreeChangeExtension) (domDataTreeIdentifier, listener) -> {
- final var rootId = domDataTreeIdentifier.path();
- if (isRegistrationWatched(rootId, domDataTreeIdentifier.datastore())) {
- LOG.warn("{} registration (registerDataTreeChangeListener) for {} from {}.",
- listener instanceof ClusteredDOMDataTreeChangeListener ? "Clustered" : "Non-clustered",
+ return type.cast(new DataTreeChangeExtension() {
+ @Override
+ public Registration registerTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ notifyIfWatched("Non-clustered", treeId, listener);
+ return treeChange.registerTreeChangeListener(treeId, listener);
+ }
+
+ @Override
+ @Deprecated(since = "13.0.0", forRemoval = true)
+ public Registration registerLegacyTreeChangeListener(final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ notifyIfWatched("Non-clustered", treeId, listener);
+ return treeChange.registerLegacyTreeChangeListener(treeId, listener);
+ }
+
+ private void notifyIfWatched(final String kind, final DOMDataTreeIdentifier treeId,
+ final DOMDataTreeChangeListener listener) {
+ final var rootId = treeId.path();
+ if (isRegistrationWatched(rootId, treeId.datastore()) && LOG.isWarnEnabled()) {
+ LOG.warn("{} registration (registerDataTreeChangeListener) for {} from {}.", kind,
toPathString(rootId), getStackSummary());
+ }
}
- return treeChange.registerDataTreeChangeListener(domDataTreeIdentifier, listener);
});
}
return ext;