From: Robert Varga Date: Tue, 10 Dec 2019 10:38:57 +0000 (+0100) Subject: Add InstanceNotificationService X-Git-Tag: v9.0.2~23 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=0200653e5890d9854f015bc9ae5da5c4e8704d46 Add InstanceNotificationService InstanceNotificationService provides access to YANG 1.1 nested notifications, i.e. those defined in a container or a list. JIRA: MDSAL-494 Change-Id: If308adb3a26964cea90456f8fb020b7364c937db Signed-off-by: Robert Varga --- diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationPublishService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationPublishService.java new file mode 100644 index 0000000000..9b9b4ef264 --- /dev/null +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationPublishService.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.api; + +import com.google.common.annotations.Beta; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.util.concurrent.FluentFutures; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceNotification; + +/** + * A {@link BindingService} which allows its users to submit YANG-modeled top-level (YANG 1.1) + * {@link InstanceNotification}s for delivery. There are three methods of submission, following the patters from + * {@link java.util.concurrent.BlockingQueue}: + * + * + *

+ * The actual delivery to listeners is asynchronous and implementation-specific. Users of this interface should not make + * any assumptions as to whether the notification has or has not been seen. + */ +@Beta +public interface InstanceNotificationPublishService extends BindingService { + /** + * Well-known value indicating that the binding-aware implementation is currently not able to accept a notification. + */ + @NonNull ListenableFuture REJECTED = FluentFutures.immediateFailedFluentFuture( + new NotificationRejectedException("Rejected due to resource constraints.")); + + /** + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. + * + * Note: This call will block when the notification queue is full. + * + * @param notification the notification to publish. + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if any argument is null + */ + , P extends DataObject> void putNotification( + @NonNull DataTreeIdentifier

path, @NonNull N notification) throws InterruptedException; + + /** + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. + * + *

+ * Still guaranteed not to block. Returns Listenable Future which will complete once the delivery is completed. + * + * @param notification the notification to publish. + * @return A listenable future which will report completion when the service has finished propagating the + * notification to its immediate registrants, or {@link #REJECTED} if resource constraints prevent + * @throws NullPointerException if any argument is null + */ + , P extends DataObject> + @NonNull ListenableFuture offerNotification(@NonNull DataTreeIdentifier

path, + @NonNull N notification); + + /** + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. This method + * is guaranteed not to block more than the specified timeout. + * + * @param notification the notification to publish. + * @param timeout how long to wait before giving up, in units of unit + * @param unit a TimeUnit determining how to interpret the timeout parameter + * @return A listenable future which will report completion when the service has finished propagating the + * notification to its immediate registrants, or {@link #REJECTED} if resource constraints prevent + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if timeout is negative. + */ + , P extends DataObject> + @NonNull ListenableFuture offerNotification(@NonNull DataTreeIdentifier

path, + @NonNull N notification, long timeout, @NonNull TimeUnit unit) throws InterruptedException; +} diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationService.java new file mode 100644 index 0000000000..b8fb57828f --- /dev/null +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationService.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.api; + +import com.google.common.annotations.Beta; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.EventListener; +import java.util.concurrent.Executor; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.concepts.Registration; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceNotification; +import org.opendaylight.yangtools.yang.binding.KeyedListNotification; + +/** + * A {@link BindingService} which allows clients to subscribe to (YANG 1.1) {@link InstanceNotification}s and + * {@link KeyedListNotification}s. + */ +@Beta +public interface InstanceNotificationService extends BindingService { + +

, T extends Listener> + @NonNull Registration registerListener(InstanceNotificationSpec spec, DataTreeIdentifier

path, + T listener, Executor executor); + + default

, T extends Listener> + @NonNull Registration registerListener(final InstanceNotificationSpec spec, + final DataTreeIdentifier

path, final T listener) { + return registerListener(spec, path, listener, MoreExecutors.directExecutor()); + } + +

, N extends KeyedListNotification, K extends Identifier

, + T extends KeyedListListener> @NonNull Registration registerListener( + InstanceNotificationSpec spec, DataTreeIdentifier

path, T listener, Executor executor); + + default

, N extends KeyedListNotification, K extends Identifier

, + T extends KeyedListListener> @NonNull Registration registerListener( + final InstanceNotificationSpec spec, final DataTreeIdentifier

path, final T listener) { + return registerListener(spec, path, listener, MoreExecutors.directExecutor()); + } + + /* + * Interface for listeners on instance (YANG 1.1) notifications. + */ + @FunctionalInterface + interface Listener

> extends EventListener { + /** + * Process an instance notification. + * + * @param path Instance path + * @param notification Notification body + */ + void onNotification(@NonNull DataTreeIdentifier

path, @NonNull N notification); + } + + /** + * Interface for listeners on instance (YANG 1.1) notifications defined in a {@code list} with a {@code key}. + */ + @FunctionalInterface + interface KeyedListListener

, N extends KeyedListNotification, + K extends Identifier

> extends EventListener { + /** + * Process an instance notification. + * + * @param path Instance path + * @param notification Notification body + */ + // FIXME: DataTreeIdentifier does not have a Keyed flavor + void onNotification(@NonNull DataTreeIdentifier

path, @NonNull N notification); + } +} diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationSpec.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationSpec.java new file mode 100644 index 0000000000..6a3107a889 --- /dev/null +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationSpec.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.api; + +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects; +import java.util.Objects; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.concepts.Mutable; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.ChildOf; +import org.opendaylight.yangtools.yang.binding.ChoiceIn; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.DataRoot; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceNotification; + +/** + * A combination of an {@link InstanceNotification} class and its corresponding instantiation wildcard, expressed as + * an {@link InstanceIdentifier}. This glue is required because instance notification interfaces are generated at the + * place of their definition, most importantly in {@code grouping} and we actually need to bind to a particular + * instantiation (e.g. a place where {@code uses} references that grouping). + * + * @param Generated InstanceNotification interface type + * @param

Instance notification parent type + */ +@Beta +public final class InstanceNotificationSpec, P extends DataObject> + implements Immutable { + private final @NonNull InstanceIdentifier

path; + private final @NonNull Class type; + + private InstanceNotificationSpec(final Class type, final InstanceIdentifier

path) { + this.type = requireNonNull(type); + this.path = requireNonNull(path); + } + + public static

> @NonNull Builder

builder(final Class

container) { + return new Builder<>(InstanceIdentifier.builder(container)); + } + + public static & DataObject, P extends ChildOf> + @NonNull Builder

builder(final Class caze, final Class

container) { + return new Builder<>(InstanceIdentifier.builder(caze, container)); + } + + public @NonNull InstanceIdentifier

path() { + return path; + } + + public @NonNull Class type() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(type, path); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof InstanceNotificationSpec)) { + return false; + } + final var other = (InstanceNotificationSpec) obj; + return type.equals(other.type) && path.equals(other.path); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("action", type).add("path", path).toString(); + } + + @Beta + public static final class Builder

implements Mutable { + private final InstanceIdentifierBuilder

pathBuilder; + + Builder(final InstanceIdentifierBuilder

pathBuilder) { + this.pathBuilder = requireNonNull(pathBuilder); + } + + public > @NonNull Builder withPathChild(final Class container) { + pathBuilder.child(container); + return castThis(); + } + + public & DataObject, N extends ChildOf> + @NonNull Builder withPathChild(final Class caze, final Class container) { + pathBuilder.child(caze, container); + return castThis(); + } + + public > @NonNull Builder withPathAugmentation( + final Class container) { + pathBuilder.augmentation(container); + return castThis(); + } + + public > @NonNull InstanceNotificationSpec build( + final Class type) { + return new InstanceNotificationSpec<>(type, pathBuilder.build()); + } + + @SuppressWarnings("unchecked") + private @NonNull Builder castThis() { + return (Builder) this; + } + } +} diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationPublishService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationPublishService.java index 69bf606014..0df5ec99a9 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationPublishService.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationPublishService.java @@ -14,72 +14,64 @@ import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.Notification; /** - * A {@link NotificationService} which also allows its users to - * submit YANG-modeled notifications for delivery. There are three - * methods of submission, following the patters from {@link java.util.concurrent.BlockingQueue}: - * - {@link #putNotification(Notification)}, which may block indefinitely - * if the implementation cannot allocate resources to accept the notification, - * - {@link #offerNotification(Notification)}, which does not block if face - * of resource starvation, - * - {@link #offerNotification(Notification, int, TimeUnit)}, which may block - * for specified time if resources are thin. + * A {@link BindingService} which allows its users to submit YANG-modeled top-level (YANG 1) {@link Notification}s for + * delivery. There are three methods of submission, following the patters from + * {@link java.util.concurrent.BlockingQueue}: + *

    + *
  • {@link #putNotification(Notification)}, which may block indefinitely if the implementation cannot allocate + * resources to accept the notification,
  • + *
  • {@link #offerNotification(Notification)}, which does not block if face of resource starvation,
  • + *
  • {@link #offerNotification(Notification, int, TimeUnit)}, which may block for specified time if resources are + * thin.
  • + *
* *

- * The actual delivery to listeners is asynchronous and implementation-specific. - * Users of this interface should not make any assumptions as to whether the - * notification has or has not been seen. + * The actual delivery to listeners is asynchronous and implementation-specific. Users of this interface should not make + * any assumptions as to whether the notification has or has not been seen. */ public interface NotificationPublishService extends BindingService { /** - * Well-known value indicating that the binding-aware implementation is currently not - * able to accept a notification. + * Well-known value indicating that the binding-aware implementation is currently not able to accept a notification. */ @NonNull ListenableFuture REJECTED = FluentFutures.immediateFailedFluentFuture( new NotificationRejectedException("Rejected due to resource constraints.")); /** - * Publishes a notification to subscribed listeners. This initiates - * the process of sending the notification, but delivery to the - * listeners can happen asynchronously, potentially after a call to - * this method returns. + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. * * Note: This call will block when the notification queue is full. * - * @param notification - * the notification to publish. + * @param notification the notification to publish. * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if the notification is null */ void putNotification(@NonNull Notification notification) throws InterruptedException; /** - * Publishes a notification to subscribed listeners. This initiates the process of sending the - * notification, but delivery to the listeners can happen asynchronously, potentially after a - * call to this method returns. + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. * *

* Still guaranteed not to block. Returns Listenable Future which will complete once. * * @param notification the notification to publish. - * @return A listenable future which will report completion when the service has finished - * propagating the notification to its immediate registrants, or {@link #REJECTED} if - * resource constraints prevent + * @return A listenable future which will report completion when the service has finished propagating the + * notification to its immediate registrants, or {@link #REJECTED} if resource constraints prevent * @throws NullPointerException if the notification is null */ @NonNull ListenableFuture offerNotification(@NonNull Notification notification); /** - * Publishes a notification to subscribed listeners. This initiates the process of sending the - * notification, but delivery to the listeners can happen asynchronously, potentially after a - * call to this method returns. This method is guaranteed not to block more than the specified - * timeout. + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. This method + * is guaranteed not to block more than the specified timeout. * * @param notification the notification to publish. * @param timeout how long to wait before giving up, in units of unit * @param unit a TimeUnit determining how to interpret the timeout parameter - * @return A listenable future which will report completion when the service has finished - * propagating the notification to its immediate registrants, or {@link #REJECTED} if - * resource constraints prevent + * @return A listenable future which will report completion when the service has finished propagating the + * notification to its immediate registrants, or {@link #REJECTED} if resource constraints prevent * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if the notification or unit is null * @throws IllegalArgumentException if timeout is negative.