From 05e8a84eedb4030d0396fce17c8b5456bdf2b7bb Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 19 Oct 2021 17:30:51 +0200 Subject: [PATCH] Add support for composite notification listeners This is a replacement for FooListener notifications, allowing an atomic subscription. JIRA: MDSAL-702 Change-Id: I8ebf57f1a479d178aecb8d465d85d690124a587c Signed-off-by: Robert Varga --- .../binding/api/NotificationService.java | 54 +++++++++++++++++++ .../BindingDOMNotificationServiceAdapter.java | 15 ++++++ .../SingleBindingDOMNotificationAdapter.java | 6 +++ .../adapter/osgi/OSGiNotificationService.java | 6 +++ 4 files changed, 81 insertions(+) diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java index 8b904e05c9..a3b41ea2f1 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java @@ -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> constituents) { + @Beta + public record Component & DataObject>(@NonNull Class type, Listener 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"); + } + } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java index 683eb959bb..00709df520 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java @@ -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(); + 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 extends AbstractListenerRegistration { diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java index a4bb85f1e1..a392fd5a11 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java @@ -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 & Data this.executor = requireNonNull(executor); } + SingleBindingDOMNotificationAdapter(final AdapterContext adapterContext, final Component 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))); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java index 36da2c924a..404129dd14 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java @@ -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 properties) { start(properties); -- 2.36.6