Add InstanceNotificationService 23/86323/21
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 10 Dec 2019 10:38:57 +0000 (11:38 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Apr 2022 09:22:56 +0000 (11:22 +0200)
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 <robert.varga@pantheon.tech>
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationPublishService.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationService.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationSpec.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationPublishService.java

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 (file)
index 0000000..9b9b4ef
--- /dev/null
@@ -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}:
+ * <ul>
+ *   <li>{@link #putNotification(InstanceNotification)}, which may block indefinitely if the implementation cannot
+ *       allocate resources to accept the notification,</li>
+ *   <li>{@link #offerNotification(InstanceNotification)}, which does not block if face of resource starvation,</li>
+ *   <li>{@link #offerNotification(InstanceNotification, int, TimeUnit)}, which may block for specified time if
+ *       resources are thin.</li>
+ * </ul>
+ *
+ * <p>
+ * 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<Object> 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.
+     *
+     * <b>Note:</b> 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
+     */
+    <N extends InstanceNotification<N, P>, P extends DataObject> void putNotification(
+        @NonNull DataTreeIdentifier<P> 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.
+     *
+     * <p>
+     * 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
+     */
+    <N extends InstanceNotification<N, P>, P extends DataObject>
+        @NonNull ListenableFuture<? extends Object> offerNotification(@NonNull DataTreeIdentifier<P> 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.
+     */
+    <N extends InstanceNotification<N, P>, P extends DataObject>
+        @NonNull ListenableFuture<? extends Object> offerNotification(@NonNull DataTreeIdentifier<P> 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 (file)
index 0000000..b8fb578
--- /dev/null
@@ -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 {
+
+    <P extends DataObject, N extends InstanceNotification<N, P>, T extends Listener<P, N>>
+        @NonNull Registration registerListener(InstanceNotificationSpec<N, P> spec, DataTreeIdentifier<P> path,
+            T listener, Executor executor);
+
+    default <P extends DataObject, N extends InstanceNotification<N, P>, T extends Listener<P, N>>
+        @NonNull Registration registerListener(final InstanceNotificationSpec<N, P> spec,
+            final DataTreeIdentifier<P> path, final T listener) {
+        return registerListener(spec, path, listener, MoreExecutors.directExecutor());
+    }
+
+    <P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>, K extends Identifier<P>,
+        T extends KeyedListListener<P, N, K>> @NonNull Registration registerListener(
+            InstanceNotificationSpec<N, P> spec, DataTreeIdentifier<P> path, T listener, Executor executor);
+
+    default <P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>, K extends Identifier<P>,
+        T extends KeyedListListener<P, N, K>> @NonNull Registration registerListener(
+            final InstanceNotificationSpec<N, P> spec, final DataTreeIdentifier<P> path, final T listener) {
+        return registerListener(spec, path, listener, MoreExecutors.directExecutor());
+    }
+
+    /*
+     * Interface for listeners on instance (YANG 1.1) notifications.
+     */
+    @FunctionalInterface
+    interface Listener<P extends DataObject, N extends InstanceNotification<N, P>> extends EventListener {
+        /**
+         * Process an instance notification.
+         *
+         * @param path Instance path
+         * @param notification Notification body
+         */
+        void onNotification(@NonNull DataTreeIdentifier<P> 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<P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>,
+            K extends Identifier<P>> 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<P> 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 (file)
index 0000000..6a3107a
--- /dev/null
@@ -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 <N> Generated InstanceNotification interface type
+ * @param <P> Instance notification parent type
+ */
+@Beta
+public final class InstanceNotificationSpec<N extends InstanceNotification<N, P>, P extends DataObject>
+        implements Immutable {
+    private final @NonNull InstanceIdentifier<P> path;
+    private final @NonNull Class<N> type;
+
+    private InstanceNotificationSpec(final Class<N> type, final InstanceIdentifier<P> path) {
+        this.type = requireNonNull(type);
+        this.path = requireNonNull(path);
+    }
+
+    public static <P extends ChildOf<? extends DataRoot>> @NonNull Builder<P> builder(final Class<P> container) {
+        return new Builder<>(InstanceIdentifier.builder(container));
+    }
+
+    public static <C extends ChoiceIn<? extends DataRoot> & DataObject, P extends ChildOf<? super C>>
+            @NonNull Builder<P> builder(final Class<C> caze, final Class<P> container) {
+        return new Builder<>(InstanceIdentifier.builder(caze, container));
+    }
+
+    public @NonNull InstanceIdentifier<P> path() {
+        return path;
+    }
+
+    public @NonNull Class<N> 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<P extends DataObject> implements Mutable {
+        private final InstanceIdentifierBuilder<P> pathBuilder;
+
+        Builder(final InstanceIdentifierBuilder<P> pathBuilder) {
+            this.pathBuilder = requireNonNull(pathBuilder);
+        }
+
+        public <N extends ChildOf<? super P>> @NonNull Builder<N> withPathChild(final Class<N> container) {
+            pathBuilder.child(container);
+            return castThis();
+        }
+
+        public <C extends ChoiceIn<? super P> & DataObject, N extends ChildOf<? super C>>
+                @NonNull Builder<N> withPathChild(final Class<C> caze, final Class<N> container) {
+            pathBuilder.child(caze, container);
+            return castThis();
+        }
+
+        public <N extends DataObject & Augmentation<? super P>> @NonNull Builder<N> withPathAugmentation(
+                final Class<N> container) {
+            pathBuilder.augmentation(container);
+            return castThis();
+        }
+
+        public <N extends InstanceNotification<N, P>> @NonNull InstanceNotificationSpec<N, P> build(
+                final Class<N> type) {
+            return new InstanceNotificationSpec<>(type, pathBuilder.build());
+        }
+
+        @SuppressWarnings("unchecked")
+        private <N extends DataObject> @NonNull Builder<N> castThis() {
+            return (Builder<N>) this;
+        }
+    }
+}
index 69bf606014cb8b20348c007d28999412d27bd9d8..0df5ec99a9301bc120055742a8739e20f15ce299 100644 (file)
@@ -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}:
+ * <ul>
+ *   <li>{@link #putNotification(Notification)}, which may block indefinitely if the implementation cannot allocate
+ *       resources to accept the notification,</li>
+ *   <li>{@link #offerNotification(Notification)}, which does not block if face of resource starvation,</li>
+ *   <li>{@link #offerNotification(Notification, int, TimeUnit)}, which may block for specified time if resources are
+ *       thin.</li>
+ * </ul>
  *
  * <p>
- * 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<Object> 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.
      *
      * <b>Note:</b> 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.
      *
      * <p>
      * 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<? extends Object> 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.