Deprecate Clustered(DOM)DataTreeChangeListener 16/107916/8
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 18 Jan 2024 20:33:28 +0000 (21:33 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 19 Jan 2024 11:13:10 +0000 (12:13 +0100)
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 <ruslan.kashapov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
21 files changed:
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ClusteredDataTreeChangeListener.java
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataChangeListenerAdapter.java
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataListenerAdapter.java
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataTreeChangeService.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java [deleted file]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataChangeListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiDataBroker.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapterTest.java
binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/ClusteredDOMDataTreeChangeListener.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java
dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataStore.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractDOMDataBroker.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStoreTreeChangePublisher.java
trace/mdsal-trace-impl/src/main/java/org/opendaylight/mdsal/trace/impl/TracingBroker.java

index a7effef645ee46f256f32a835dba9c1bbf01cc99..c2d81b2d425bf1549376f01d9792ca72592ff5d9 100644 (file)
@@ -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 <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
 }
index 53895f7a20bb40094fe96c2436de521031ef8df0..de43d4057d847cced76829601c3fb0144e3526fb 100644 (file)
@@ -14,7 +14,7 @@ import java.util.List;
 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) {
index 8d6dc87f52582cd0191e3a2eaed98660a1b98449..8510763cdcc0293a1771ec4c08444029165cca10 100644 (file)
@@ -13,8 +13,7 @@ import com.google.common.collect.ForwardingObject;
 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) {
index 8ab7aa61abf9117049e0940f0b318b1d42b1fe3b..b5b1d0c1ee16d93ea03d439c328149bc19eb42c3 100644 (file)
@@ -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.
      */
-    <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.
@@ -76,7 +159,7 @@ public interface DataTreeChangeService extends BindingService {
      */
     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));
     }
 
     /**
@@ -106,7 +189,7 @@ public interface DataTreeChangeService extends BindingService {
      */
     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(
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 (file)
index d3e9cfc..0000000
+++ /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<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);
-    }
-}
index 3b848b2ee76996945961788128d17421ce18dd9c..14d4dbc7064848574c01a3527ce004fee08c12b0 100644 (file)
@@ -74,12 +74,24 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull
     }
 
     @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> {
@@ -97,5 +109,4 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull
             return new BindingDOMDataBrokerAdapter(adapterContext(), delegates.getInstance(DOMDataBroker.class));
         }
     }
-
 }
index 371a56a7a10d9266704a0df7f6ad813fdf3669c9..d6f5abb5b5645232f6ac3bc037280a8078f86bce 100644 (file)
@@ -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<T extends DataObject> implements ClusteredDOMDataTreeChangeListener {
+final class BindingDOMDataChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
     private final AdapterContext adapterContext;
     private final DataChangeListener<T> listener;
 
index 03f716ce4a010ffd7a7916a21802f8faf898cb72..c9dac18b540d57f6197f1a7940e59e05b9edae5b 100644 (file)
@@ -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<T extends DataObject> implements ClusteredDOMDataTreeChangeListener {
+final class BindingDOMDataListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
     private final AdapterContext adapterContext;
     private final DataListener<T> listener;
 
index 65de385c4b4c3e1ee836093d7ec0ecfdc3686021..f46529d46673433625c02e431ab5a8f054f8c4c9 100644 (file)
@@ -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<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
index 9ec4e275ecdfa8694782e87c3273b5c31543af58..4a4e5e1ff9720ec10527fb5d238bca66c67fc41e 100644 (file)
@@ -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 <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));
     }
 
index adc4636f5a226697387bfa16a6c921d76e0b54d3..4e486c65d411fe0b89faea2e3c00e3a8872f45c2 100644 (file)
@@ -43,7 +43,8 @@ final class LazyDataTreeModification<T extends DataObject> implements DataTreeMo
 
     @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());
@@ -51,23 +52,25 @@ final class LazyDataTreeModification<T extends DataObject> implements DataTreeMo
             : 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);
             }
@@ -81,7 +84,7 @@ final class LazyDataTreeModification<T extends DataObject> 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));
     }
index 24787cb0127d11780bea57f2d29e808c7b327dd8..dd5e420375e65383008907b07465bae1131f7f0c 100644 (file)
@@ -53,12 +53,6 @@ public final class OSGiDataBroker extends AbstractAdaptedService<DataBroker> imp
         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();
@@ -68,4 +62,17 @@ public final class OSGiDataBroker extends AbstractAdaptedService<DataBroker> imp
     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);
+    }
 }
index 1a00a5169315305b7a36292e22e52dd677b4b167..060150d70aa813b5150b6b8e06586fb54201e6e5 100644 (file)
@@ -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> 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<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) {
index 40332c6c217c17d10ad2bb994976e425529c0794..478aea5cc45e6f97eed491413002f8585bcd23d1 100644 (file)
@@ -41,12 +41,6 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D
         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();
@@ -56,4 +50,17 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D
     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);
+    }
 }
index 23f9c18697ffb617e5cd94b6c898fd19e98280bd..aa639380c64eb9b4081759f4f67af081c8cad485 100644 (file)
@@ -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.
  *
  * <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
 }
index 34f822c3faa991c893519df6909f8f97b82e2cbc..e21974d358a9d1688b716550258b77a6ccac5604 100644 (file)
@@ -129,6 +129,91 @@ public interface DOMDataBroker extends DOMService<DOMDataBroker, DOMDataBroker.E
          *         {@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);
     }
 }
index abcfd7a9656900624313e9cbe17531fef3f5150d..42f141960b05c9329d91e4e4621c7a8ab564f6f6 100644 (file)
@@ -153,6 +153,13 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype<String> 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<String> tx) {
         LOG.debug("Tx: {} is closed.", tx.getIdentifier());
index d1be9449ea21f527024923c2be5d6a6a78f5768b..ae82fcea6997f8dcc8711768119ab6fa7fa3c855 100644 (file)
@@ -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.<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)) {
index 08994b821c0a2afa154a836fe6944d75100e7f9f..0b38025b2a030d056bae3dba3037eb66b6811d3f 100644 (file)
@@ -139,6 +139,20 @@ public abstract class AbstractDOMStoreTreeChangePublisher
         }
     }
 
+    /**
+     * {@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) {
index 7ddfcf7ab9db432c9272c4b136c094acbdae57b1..bbf7c184e353d192966490051c79020fe9661d34 100644 (file)
@@ -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.
-     *
-     * <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
@@ -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);
 }
index b7b528b636de630f645554e18e5d6fb1a814a1c0..1a56d625e7c5af7a4bb6363279d445180f71dda8 100644 (file)
@@ -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 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;