--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
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.