From: Robert Varga Date: Thu, 18 Jan 2024 20:33:28 +0000 (+0100) Subject: Deprecate Clustered(DOM)DataTreeChangeListener X-Git-Tag: v13.0.0~4 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=b8a896e6dfc47374f99efee157683dc8a7ef8860 Deprecate Clustered(DOM)DataTreeChangeListener The semantics of ClusteredDOMDataTreeChangeListener is the default we want to expose from DataTreeChangeExtension. Any integration with clusters, or any other details, really, should happen through a dedicated DOMDataBroker.Extension. Redefine DataTreeChangeExtension, retaining compatibility methods with migration guidance. This cascades to ClusteredDataTreeChangeListener, which is just a mdsal-binding-api mirror of the DOM concept. JIRA: MDSAL-819 Change-Id: Ice4e1db64d34241115226d532b226cbdff8e045c Signed-off-by: Ruslan Kashapov Signed-off-by: Robert Varga --- diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ClusteredDataTreeChangeListener.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ClusteredDataTreeChangeListener.java index a7effef645..c2d81b2d42 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ClusteredDataTreeChangeListener.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ClusteredDataTreeChangeListener.java @@ -18,9 +18,13 @@ import org.opendaylight.yangtools.yang.binding.DataObject; * 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 + * @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 extends DataTreeChangeListener { + // Just a marker interface } diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataChangeListenerAdapter.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataChangeListenerAdapter.java index 53895f7a20..de43d4057d 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataChangeListenerAdapter.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataChangeListenerAdapter.java @@ -14,7 +14,7 @@ import java.util.List; import org.opendaylight.yangtools.yang.binding.DataObject; final class DataChangeListenerAdapter extends ForwardingObject - implements ClusteredDataTreeChangeListener { + implements DataTreeChangeListener { private final DataChangeListener delegate; DataChangeListenerAdapter(final DataChangeListener delegate) { diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataListenerAdapter.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataListenerAdapter.java index 8d6dc87f52..8510763cdc 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataListenerAdapter.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataListenerAdapter.java @@ -13,8 +13,7 @@ import com.google.common.collect.ForwardingObject; import java.util.List; import org.opendaylight.yangtools.yang.binding.DataObject; -final class DataListenerAdapter extends ForwardingObject - implements ClusteredDataTreeChangeListener { +final class DataListenerAdapter extends ForwardingObject implements DataTreeChangeListener { private final DataListener delegate; DataListenerAdapter(final DataListener delegate) { diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataTreeChangeService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataTreeChangeService.java index 8ab7aa61ab..b5b1d0c1ee 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataTreeChangeService.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataTreeChangeService.java @@ -45,9 +45,92 @@ public interface DataTreeChangeService extends BindingService { * @return a Registration object, which may be used to unregister your listener using {@link Registration#close()} * to stop delivery of change events. */ - @NonNull Registration registerDataTreeChangeListener(@NonNull DataTreeIdentifier treeId, + @NonNull Registration registerTreeChangeListener( + @NonNull DataTreeIdentifier treeId, @NonNull DataTreeChangeListener 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). + * + *

+ * 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. + * + *

+ * Aside from the above difference, this method is equivalent to + * {@link #registerTreeChangeListener(DataTreeIdentifier, DataTreeChangeListener)}. If you are unable to migrate, + * please contact us on the mailing list + * + * @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) + @NonNull Registration registerLegacyTreeChangeListener(@NonNull DataTreeIdentifier treeId, @NonNull DataTreeChangeListener listener); + /** + * Registers a {@link DataTreeChangeListener} to receive notifications when data changes under a given path in the + * conceptual data tree. + * + *

+ * You are able to register for notifications for any node or subtree which can be represented using + * {@link DataTreeIdentifier}. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * This method returns a {@link Registration} object. To "unregister" your listener for changes call the + * {@link Registration#close()} method on the returned object. + * + *

+ * 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 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 @NonNull Registration registerDataTreeChangeListener( + final @NonNull DataTreeIdentifier treeId, final @NonNull DataTreeChangeListener 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 @NonNull Registration registerDataTreeChangeListener( + final @NonNull DataTreeIdentifier treeId, final @NonNull ClusteredDataTreeChangeListener listener) { + return registerTreeChangeListener(treeId, listener); + } + /** * Registers a {@link DataTreeChangeListener} to receive notifications when data changes under a given path in the * conceptual data tree. @@ -76,7 +159,7 @@ public interface DataTreeChangeService extends BindingService { */ default @NonNull Registration registerDataListener( final @NonNull DataTreeIdentifier treeId, final @NonNull DataListener listener) { - return registerDataTreeChangeListener(checkNotWildcard(treeId), new DataListenerAdapter<>(listener)); + return registerTreeChangeListener(checkNotWildcard(treeId), new DataListenerAdapter<>(listener)); } /** @@ -106,7 +189,7 @@ public interface DataTreeChangeService extends BindingService { */ default @NonNull Registration registerDataChangeListener( final @NonNull DataTreeIdentifier treeId, final @NonNull DataChangeListener listener) { - return registerDataTreeChangeListener(checkNotWildcard(treeId), new DataChangeListenerAdapter<>(listener)); + return registerTreeChangeListener(checkNotWildcard(treeId), new DataChangeListenerAdapter<>(listener)); } private static @NonNull DataTreeIdentifier checkNotWildcard( diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java deleted file mode 100644 index d3e9cfc9ac..0000000000 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 - extends BindingDOMDataTreeChangeListenerAdapter implements ClusteredDOMDataTreeChangeListener { - BindingClusteredDOMDataTreeChangeListenerAdapter(final AdapterContext codec, - final ClusteredDataTreeChangeListener listener, final LogicalDatastoreType store, - final Class augment) { - super(codec, listener, store, augment); - } -} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java index 3b848b2ee7..14d4dbc706 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java @@ -74,12 +74,24 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull } @Override - public Registration registerDataTreeChangeListener(final DataTreeIdentifier treeId, + public Registration registerTreeChangeListener(final DataTreeIdentifier treeId, final DataTreeChangeListener listener) { - if (treeChangeService == null) { + return getTreeChangeService().registerTreeChangeListener(treeId, listener); + } + + @Override + @Deprecated(since = "13.0.0", forRemoval = true) + public Registration registerLegacyTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener 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 { @@ -97,5 +109,4 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull return new BindingDOMDataBrokerAdapter(adapterContext(), delegates.getInstance(DOMDataBroker.class)); } } - } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataChangeListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataChangeListenerAdapter.java index 371a56a7a1..d6f5abb5b5 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataChangeListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataChangeListenerAdapter.java @@ -15,12 +15,12 @@ import org.opendaylight.mdsal.binding.api.DataChangeListener; 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 implements ClusteredDOMDataTreeChangeListener { +final class BindingDOMDataChangeListenerAdapter implements DOMDataTreeChangeListener { private final AdapterContext adapterContext; private final DataChangeListener listener; diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataListenerAdapter.java index 03f716ce4a..c9dac18b54 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataListenerAdapter.java @@ -14,13 +14,13 @@ import org.eclipse.jdt.annotation.NonNull; 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 implements ClusteredDOMDataTreeChangeListener { +final class BindingDOMDataListenerAdapter implements DOMDataTreeChangeListener { private final AdapterContext adapterContext; private final DataListener listener; diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java index 65de385c4b..f46529d466 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java @@ -10,9 +10,13 @@ package org.opendaylight.mdsal.binding.dom.adapter; 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; @@ -20,20 +24,27 @@ 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 implements DOMDataTreeChangeListener { +@NonNullByDefault +final class BindingDOMDataTreeChangeListenerAdapter implements DOMDataTreeChangeListener { private final AdapterContext adapterContext; private final DataTreeChangeListener listener; private final LogicalDatastoreType store; - private final Class augment; + private final @Nullable Class> augment; private boolean initialSyncDone; - BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext, - final DataTreeChangeListener listener, final LogicalDatastoreType store, final Class augment) { + BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext, final DataTreeIdentifier treeId, + final DataTreeChangeListener 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> extractAugment(final Class target) { + return Augmentation.class.isAssignableFrom(target) + ? (Class>) target.asSubclass(Augmentation.class) : null; } @Override diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java index 9ec4e275ec..4a4e5e1ff9 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java @@ -8,7 +8,6 @@ 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; @@ -17,7 +16,6 @@ import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; 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; /** @@ -35,32 +33,31 @@ final class BindingDOMDataTreeChangeServiceAdapter extends AbstractBindingAdapte } @Override - public Registration registerDataTreeChangeListener(final DataTreeIdentifier treeId, + public Registration registerTreeChangeListener(final DataTreeIdentifier treeId, final DataTreeChangeListener 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) 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 Registration registerLegacyTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener listener) { + return getDelegate().registerLegacyTreeChangeListener(toDomTreeIdentifier(treeId), + new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), treeId, listener)); } @Override public Registration registerDataListener(final DataTreeIdentifier treeId, final DataListener listener) { - return getDelegate().registerDataTreeChangeListener(toDomTreeInstance(treeId), + return getDelegate().registerTreeChangeListener(toDomTreeInstance(treeId), new BindingDOMDataListenerAdapter<>(adapterContext(), listener)); } @Override public Registration registerDataChangeListener(final DataTreeIdentifier treeId, final DataChangeListener listener) { - return getDelegate().registerDataTreeChangeListener(toDomTreeInstance(treeId), + return getDelegate().registerTreeChangeListener(toDomTreeInstance(treeId), new BindingDOMDataChangeListenerAdapter<>(adapterContext(), listener)); } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java index adc4636f5a..4e486c65d4 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java @@ -43,7 +43,8 @@ final class LazyDataTreeModification implements DataTreeMo @SuppressWarnings({"unchecked", "rawtypes"}) static @Nullable DataTreeModification from(final CurrentAdapterSerializer serializer, - final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, final Class augment) { + final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, + final @Nullable Class> augment) { final var bindingPath = createBindingPath(serializer, domChange.getRootPath(), augment); final var codec = serializer.getSubtreeCodec(bindingPath); final var modification = LazyDataObjectModification.from(codec, domChange.getRootNode()); @@ -51,23 +52,25 @@ final class LazyDataTreeModification implements DataTreeMo : new LazyDataTreeModification(DataTreeIdentifier.of(datastoreType, bindingPath), modification); } - @SuppressWarnings({"unchecked", "rawtypes"}) static @Nullable DataTreeModification from(final CurrentAdapterSerializer serializer, - final DOMDataTreeCandidate candidate, final Class augment) { + final DOMDataTreeCandidate candidate, final @Nullable Class augment) { final var domRootPath = candidate.getRootPath(); - final var bindingPath = createBindingPath(serializer, domRootPath.path(), augment); + @SuppressWarnings("unchecked") + final var bindingPath = (InstanceIdentifier) 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) 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 @NonNull List> from(final CurrentAdapterSerializer codec, final List domChanges, final LogicalDatastoreType datastoreType, - final Class augment) { + final @Nullable Class> augment) { final var result = new ArrayList>(domChanges.size()); for (var domChange : domChanges) { - final var bindingChange = from(codec, domChange, datastoreType, augment); + final var bindingChange = LazyDataTreeModification.from(codec, domChange, datastoreType, augment); if (bindingChange != null) { result.add(bindingChange); } @@ -81,7 +84,7 @@ final class LazyDataTreeModification implements DataTreeMo // 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)); } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiDataBroker.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiDataBroker.java index 24787cb012..dd5e420375 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiDataBroker.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiDataBroker.java @@ -53,12 +53,6 @@ public final class OSGiDataBroker extends AbstractAdaptedService imp return delegate.newWriteOnlyTransaction(); } - @Override - public Registration registerDataTreeChangeListener(final DataTreeIdentifier treeId, - final DataTreeChangeListener listener) { - return delegate.registerDataTreeChangeListener(treeId, listener); - } - @Override public TransactionChain createTransactionChain() { return delegate.createTransactionChain(); @@ -68,4 +62,17 @@ public final class OSGiDataBroker extends AbstractAdaptedService imp public TransactionChain createMergingTransactionChain() { return delegate.createMergingTransactionChain(); } + + @Override + public Registration registerTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener listener) { + return delegate.registerTreeChangeListener(treeId, listener); + } + + @Override + @Deprecated(since = "13.0.0", forRemoval = true) + public Registration registerLegacyTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener listener) { + return delegate.registerLegacyTreeChangeListener(treeId, listener); + } } diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapterTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapterTest.java index 1a00a51693..060150d70a 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapterTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapterTest.java @@ -7,30 +7,24 @@ */ 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; @@ -42,8 +36,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; * * @author Thomas Pantelis */ -@RunWith(MockitoJUnitRunner.StrictStubs.class) -public class BindingDOMDataTreeChangeServiceAdapterTest { +@ExtendWith(MockitoExtension.class) +class BindingDOMDataTreeChangeServiceAdapterTest { private static final InstanceIdentifier TOP_PATH = InstanceIdentifier.create(Top.class); private static final YangInstanceIdentifier TOP_YIID = YangInstanceIdentifier.of(Top.QNAME); @@ -56,41 +50,31 @@ public class BindingDOMDataTreeChangeServiceAdapterTest { @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 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 { @Override public void onDataTreeChanged(final List> changes) { diff --git a/binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java b/binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java index 40332c6c21..478aea5cc4 100644 --- a/binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java +++ b/binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java @@ -41,12 +41,6 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D return delegate().newWriteOnlyTransaction(); } - @Override - public Registration registerDataTreeChangeListener(final DataTreeIdentifier treeId, - final DataTreeChangeListener listener) { - return delegate().registerDataTreeChangeListener(treeId, listener); - } - @Override public TransactionChain createTransactionChain() { return delegate().createTransactionChain(); @@ -56,4 +50,17 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D public TransactionChain createMergingTransactionChain() { return delegate().createMergingTransactionChain(); } + + @Override + public Registration registerTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener listener) { + return delegate().registerTreeChangeListener(treeId, listener); + } + + @Override + @Deprecated(since = "13.0.0", forRemoval = true) + public Registration registerLegacyTreeChangeListener(final DataTreeIdentifier treeId, + final DataTreeChangeListener listener) { + return delegate().registerLegacyTreeChangeListener(treeId, listener); + } } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/ClusteredDOMDataTreeChangeListener.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/ClusteredDOMDataTreeChangeListener.java index 23f9c18697..aa639380c6 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/ClusteredDOMDataTreeChangeListener.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/ClusteredDOMDataTreeChangeListener.java @@ -7,16 +7,23 @@ */ 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. * *

* 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 } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java index 34f822c3fa..e21974d358 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java @@ -129,6 +129,91 @@ public interface DOMDataBroker extends DOMService + * You are able to register for notifications for any node or subtree which can be represented using + * {@link DOMDataTreeIdentifier}. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * This method returns a {@link Registration} object. To "unregister" your listener for changes call + * the {@link Registration#close()} method on the returned object. + * + *

+ * 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). + * + *

+ * 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. + * + *

+ * Aside from the above difference, this method is equivalent to + * {@link #registerTreeChangeListener(DOMDataTreeIdentifier, DOMDataTreeChangeListener)}. If you are unable to + * migrate, please contact us on the mailing list + * + * @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); } } diff --git a/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataStore.java b/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataStore.java index abcfd7a965..42f141960b 100644 --- a/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataStore.java +++ b/dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataStore.java @@ -153,6 +153,13 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype impl 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 tx) { LOG.debug("Tx: {} is closed.", tx.getIdentifier()); diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractDOMDataBroker.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractDOMDataBroker.java index d1be9449ea..ae82fcea69 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractDOMDataBroker.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractDOMDataBroker.java @@ -14,10 +14,13 @@ import java.util.Map; 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; @@ -34,12 +37,26 @@ public abstract class AbstractDOMDataBroker extends AbstractDOMForwardedTransact final var builder = ImmutableList.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)) { diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java index 08994b821c..0b38025b2a 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java @@ -139,6 +139,20 @@ public abstract class AbstractDOMStoreTreeChangePublisher } } + /** + * {@inheritDoc} + * + *

+ * 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 args, final int offset, final Node node, final DataTreeCandidate candidate, final Map> listenerChanges) { if (args.size() == offset) { diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java index 7ddfcf7ab9..bbf7c184e3 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java @@ -8,7 +8,9 @@ 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; @@ -19,30 +21,9 @@ 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. - * - *

- * You are able to register for notifications for any node or subtree which can be represented using - * {@link YangInstanceIdentifier}. - * - *

- * 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. - * - *

- * 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. - * - *

- * This method returns a {@link Registration} object. To "unregister" your listener for changes call the - * {@link Registration#close()} method on this returned object. - * - *

- * 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 @@ -51,4 +32,19 @@ public interface DOMStoreTreeChangePublisher { */ @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); } diff --git a/trace/mdsal-trace-impl/src/main/java/org/opendaylight/mdsal/trace/impl/TracingBroker.java b/trace/mdsal-trace-impl/src/main/java/org/opendaylight/mdsal/trace/impl/TracingBroker.java index b7b528b636..1a56d625e7 100644 --- a/trace/mdsal-trace-impl/src/main/java/org/opendaylight/mdsal/trace/impl/TracingBroker.java +++ b/trace/mdsal-trace-impl/src/main/java/org/opendaylight/mdsal/trace/impl/TracingBroker.java @@ -16,14 +16,16 @@ import java.util.List; 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; @@ -301,14 +303,30 @@ public class TracingBroker implements TracingDOMDataBroker { public T extension(final Class 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;