Add support for composite notification listeners 84/97984/11
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 19 Oct 2021 15:30:51 +0000 (17:30 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 26 Jun 2022 19:34:20 +0000 (21:34 +0200)
This is a replacement for FooListener notifications, allowing
an atomic subscription.

JIRA: MDSAL-702
Change-Id: I8ebf57f1a479d178aecb8d465d85d690124a587c
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java

index 8b904e05c9e843d1e743444466625c84a535cc84..a3b41ea2f13359a96716a87aa145a36d8347aecc 100644 (file)
@@ -7,8 +7,13 @@
  */
 package org.opendaylight.mdsal.binding.api;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.EventListener;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -131,6 +136,33 @@ public interface NotificationService extends BindingService {
         return registerListener(type, listener, MoreExecutors.directExecutor());
     }
 
+    /**
+     * Registers a {@link Listener} to receive callbacks for {@link Notification}s of a particular type.
+     *
+     * @param listener Composite listener containing listener implementations that will receive notifications
+     * @param executor Executor to use for invoking the listener's methods
+     * @return a {@link Registration} instance that should be used to unregister the listener by invoking the
+     *        {@link Registration#close()} method when no longer needed
+     */
+    @Beta
+    @NonNull Registration registerCompositeListener(CompositeListener listener, Executor executor);
+
+    /**
+     * Registers a {@link Listener} to receive callbacks for {@link Notification}s of a particular type.
+     *
+     * @implSpec
+     *     This method is equivalent to {@code registerCompositeListener(listener, MoreExecutors.directExecutor())},
+     *     i.e. listeners will be invoked on some implementation-specific thread.
+     *
+     * @param listener Composite listener containing listener implementations that will receive notifications
+     * @return a {@link Registration} instance that should be used to unregister the listener by invoking the
+     *        {@link Registration#close()} method when no longer needed
+     */
+    @Beta
+    default @NonNull Registration registerCompositeListener(final CompositeListener listener) {
+        return registerCompositeListener(listener, MoreExecutors.directExecutor());
+    }
+
     /**
      * Interface for listeners on global (YANG 1.0) notifications. Such notifications are identified by their generated
      * interface which extends {@link Notification}. Each listener instance can listen to only a single notification
@@ -147,4 +179,26 @@ public interface NotificationService extends BindingService {
          */
         void onNotification(@NonNull N notification);
     }
+
+    /**
+     * A composite listener. This class allows registering multiple {@link Listener}s in a single operation. Constituent
+     * listeners are available through {@link #constituents()}.
+     */
+    @Beta
+    record CompositeListener(@NonNull Set<CompositeListener.Component<?>> constituents) {
+        @Beta
+        public record Component<T extends Notification<T> & DataObject>(@NonNull Class<T> type, Listener<T> listener) {
+            public Component {
+                requireNonNull(type);
+                requireNonNull(listener);
+                checkArgument(DataObject.class.isAssignableFrom(type), "%s is not a DataObject", type);
+                checkArgument(Notification.class.isAssignableFrom(type), "%s is not a Notification", type);
+            }
+        }
+
+        public CompositeListener {
+            requireNonNull(constituents);
+            checkArgument(!constituents.isEmpty(), "Composite listener requires at least one constituent listener");
+        }
+    }
 }
index 683eb959bbfde040ad610f8a356c5542d9473897..00709df52093d3a5bf42851b0be7910342e75048 100644 (file)
@@ -12,10 +12,12 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import org.opendaylight.mdsal.binding.api.NotificationService;
 import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMService;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
@@ -24,6 +26,7 @@ import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
 @VisibleForTesting
 // FIXME: 10.0.0: make this class final
@@ -54,6 +57,18 @@ public class BindingDOMNotificationServiceAdapter implements NotificationService
         return domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications());
     }
 
+    @Override
+    public Registration registerCompositeListener(final CompositeListener listener, final Executor executor) {
+        final var exec = requireNonNull(executor);
+        final var listeners = new HashMap<Absolute, DOMNotificationListener>();
+        for (var constituent : listener.constituents()) {
+            final var domListener = new SingleBindingDOMNotificationAdapter<>(adapterContext, constituent, exec);
+            listeners.put(domListener.getSupportedNotifications().iterator().next(), domListener);
+        }
+
+        return domNotifService.registerNotificationListeners(listeners);
+    }
+
     @Deprecated(since = "10.0.0", forRemoval = true)
     private static final class ListenerRegistrationImpl<T extends NotificationListener>
             extends AbstractListenerRegistration<T> {
index a4bb85f1e164039552998872bef34b6608c49a3e..a392fd5a11b83de54caaf5868495706d6d6cd823 100644 (file)
@@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 import java.util.Set;
 import java.util.concurrent.Executor;
+import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener.Component;
 import org.opendaylight.mdsal.binding.api.NotificationService.Listener;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -31,6 +32,11 @@ final class SingleBindingDOMNotificationAdapter<N extends Notification<N> & Data
         this.executor = requireNonNull(executor);
     }
 
+    SingleBindingDOMNotificationAdapter(final AdapterContext adapterContext, final Component<N> component,
+            final Executor executor) {
+        this(adapterContext, component.type(), component.listener(), executor);
+    }
+
     @Override
     void onNotification(final Absolute domType, final Notification<?> notification) {
         executor.execute(() -> delegate.onNotification(type.cast(notification)));
index 36da2c924a99850c93bc77ec292ca852afe06eaf..404129dd14284be973f6467b676095f9d7ff818b 100644 (file)
@@ -11,6 +11,7 @@ import com.google.common.annotations.Beta;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import org.opendaylight.mdsal.binding.api.NotificationService;
+import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener;
 import org.opendaylight.mdsal.binding.api.NotificationService.Listener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.concepts.Registration;
@@ -44,6 +45,11 @@ public final class OSGiNotificationService extends AbstractAdaptedService<Notifi
         return delegate().registerListener(type, listener, executor);
     }
 
+    @Override
+    public Registration registerCompositeListener(final CompositeListener listener, final Executor executor) {
+        return delegate().registerCompositeListener(listener, executor);
+    }
+
     @Activate
     void activate(final Map<String, ?> properties) {
         start(properties);