Add InstanceNotification(Publish)ServiceAdapter 35/100435/10
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Apr 2022 14:58:07 +0000 (16:58 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 11 Apr 2022 09:46:51 +0000 (11:46 +0200)
Add the binding/dom adapter to route requests towards the DOM service.
This necessitates a bit of work on the codec side to allow generic
notification translation service.

JIRA: MDSAL-494
Change-Id: I6f98304aa475717ae4a0fb0e8fb2fb5b5a659336
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
22 files changed:
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationPublishService.java
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/InstanceNotificationService.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractInstanceNotificationListenerAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractLazySerializedEvent.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMAdapterLoader.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationPublishServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationListenerAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationPublishServiceAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationServiceAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/KeyedInstanceNotificationListenerAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotification.java [deleted file]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedInstanceNotification.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedNotification.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/PublisherAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotificationTest.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java
binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationProcessingTest.java

index 9b9b4ef26482465551ab7fa13f94c4072e76af5e..de0c8649b5cc61baf4cc5a0e319e6f62599d6bbb 100644 (file)
@@ -13,76 +13,101 @@ 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.InstanceIdentifier;
 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.
+ * {@link InstanceNotification}s for delivery.
  */
 @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.
+     * Create a new {@link Publisher} for a {@link InstanceNotificationSpec}. Returned interface may be freely reused
+     * and accessed concurrently from multiple threads.
      *
-     * @param notification the notification to publish.
-     * @throws InterruptedException if interrupted while waiting
-     * @throws NullPointerException if any argument is null
+     * @param <N> Generated InstanceNotification interface type
+     * @param <P> Notification parent type
+     * @param notificationSpec Specification of an {@link InstanceNotification}
+     * @return A {@link Publisher} handle
+     * @throws NullPointerException if {@code notificationSpec} is {@code null}
+     * @throws IllegalArgumentException when {@code notificationSpec} cannot be resolved
      */
-    <N extends InstanceNotification<N, P>, P extends DataObject> void putNotification(
-        @NonNull DataTreeIdentifier<P> path, @NonNull N notification) throws InterruptedException;
+    <N extends InstanceNotification<N, P>, P extends DataObject> @NonNull Publisher<N, P> newPublisher(
+        InstanceNotificationSpec<N, P> notificationSpec);
 
     /**
-     * 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.
+     * Interface for publishing {@link InstanceNotification} of a particular type bound to an instantiation. 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>
-     * Still guaranteed not to block. Returns Listenable Future which will complete once the delivery is completed.
+     * 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.
      *
-     * @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
+     * @param <N> Generated InstanceNotification interface type
+     * @param <P> Notification parent type
      */
-    <N extends InstanceNotification<N, P>, P extends DataObject>
-        @NonNull ListenableFuture<? extends Object> offerNotification(@NonNull DataTreeIdentifier<P> path,
+    @Beta
+    interface Publisher<N extends InstanceNotification<N, P>, P extends DataObject> {
+        /**
+         * 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 path parent path
+         * @param notification the notification to publish.
+         * @throws InterruptedException if interrupted while waiting
+         * @throws NullPointerException if any argument is null
+         */
+        void putNotification(@NonNull InstanceIdentifier<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 path parent path
+         * @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
+         */
+        @NonNull ListenableFuture<? extends Object> offerNotification(@NonNull InstanceIdentifier<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,
+        /**
+         * 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 path parent path
+         * @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.
+         */
+        @NonNull ListenableFuture<? extends Object> offerNotification(@NonNull InstanceIdentifier<P> path,
             @NonNull N notification, long timeout, @NonNull TimeUnit unit) throws InterruptedException;
+    }
 }
index b8fb57828f562a44bc4a1102d6b177b64beb1c88..7c3bc1383d2b7feed6600aa4d4c04110afbfbaf8 100644 (file)
@@ -16,7 +16,9 @@ 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.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedListNotification;
 
 /**
@@ -26,23 +28,22 @@ import org.opendaylight.yangtools.yang.binding.KeyedListNotification;
 @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);
+    <P extends DataObject, N extends InstanceNotification<N, P>> @NonNull Registration registerListener(
+        InstanceNotificationSpec<N, P> spec, InstanceIdentifier<P> path, Listener<P, N> 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) {
+    default <P extends DataObject, N extends InstanceNotification<N, P>> @NonNull Registration registerListener(
+            final InstanceNotificationSpec<N, P> spec, final InstanceIdentifier<P> path,
+            final Listener<P, N> 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);
+    <P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>, K extends Identifier<P>>
+        @NonNull Registration registerListener(InstanceNotificationSpec<N, P> spec, KeyedInstanceIdentifier<P, K> path,
+            KeyedListListener<P, N, K> 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) {
+    default <P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>, K extends Identifier<P>>
+            @NonNull Registration registerListener(final InstanceNotificationSpec<N, P> spec,
+                final KeyedInstanceIdentifier<P, K> path, final KeyedListListener<P, N, K> listener) {
         return registerListener(spec, path, listener, MoreExecutors.directExecutor());
     }
 
@@ -57,7 +58,7 @@ public interface InstanceNotificationService extends BindingService {
          * @param path Instance path
          * @param notification Notification body
          */
-        void onNotification(@NonNull DataTreeIdentifier<P> path, @NonNull N notification);
+        void onNotification(@NonNull InstanceIdentifier<P> path, @NonNull N notification);
     }
 
     /**
@@ -72,7 +73,6 @@ public interface InstanceNotificationService extends BindingService {
          * @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);
+        void onNotification(@NonNull KeyedInstanceIdentifier<P, K> path, @NonNull N notification);
     }
 }
index 4740420f3fa96ff58e21ba5e4f71105f8835fa50..eae5f768f0da95cdaeae41b2b5fc3240dbf5b70e 100644 (file)
@@ -8,6 +8,7 @@
  */
 package org.opendaylight.mdsal.binding.dom.adapter;
 
+import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
@@ -36,16 +37,18 @@ abstract class AbstractDOMNotificationListenerAdapter implements DOMNotification
     abstract Set<Absolute> getSupportedNotifications();
 
     private Notification<?> deserialize(final DOMNotification notification) {
-        if (notification instanceof LazySerializedDOMNotification) {
+        if (notification instanceof LazySerializedNotification) {
             // TODO: This is a routed-back notification, for which we may end up losing event time here, but that is
             //       okay, for now at least.
-            return ((LazySerializedDOMNotification) notification).getBindingData();
+            return ((LazySerializedNotification) notification).getBindingData();
         }
 
-        final CurrentAdapterSerializer serializer = adapterContext.currentSerializer();
-        return notification instanceof DOMEvent
+        final var serializer = adapterContext.currentSerializer();
+        final var result = notification instanceof DOMEvent
             ? serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody(),
                 ((DOMEvent) notification).getEventInstant())
                 : serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+        verify(result instanceof Notification, "Unexpected codec result %s", result);
+        return (Notification<?>) result;
     }
 }
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractInstanceNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractInstanceNotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..ddd7aae
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.concurrent.Executor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMEvent;
+import org.opendaylight.mdsal.dom.api.DOMInstanceNotificationListener;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractInstanceNotificationListenerAdapter<P extends DataObject, N extends InstanceNotification<N, P>,
+        L> implements DOMInstanceNotificationListener {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractInstanceNotificationListenerAdapter.class);
+
+    private final @NonNull AdapterContext adapterContext;
+    private final @NonNull Class<N> notificationClass;
+    private final @NonNull Executor executor;
+    private final @NonNull L delegate;
+
+    AbstractInstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class<N> nofiticationClass,
+            final L delegate, final Executor executor) {
+        this.adapterContext = requireNonNull(adapterContext);
+        this.notificationClass = requireNonNull(nofiticationClass);
+        this.delegate = requireNonNull(delegate);
+        this.executor = requireNonNull(executor);
+    }
+
+    @Override
+    public final void onNotification(final DOMDataTreeIdentifier path, final DOMNotification notification) {
+        final var serializer = adapterContext.currentSerializer();
+        final var bindingNotification = notification instanceof DOMEvent
+            ? serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody(),
+                ((DOMEvent) notification).getEventInstant())
+                : serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+
+        final N castNotification;
+        try {
+            castNotification = notificationClass.cast(bindingNotification);
+        } catch (ClassCastException e) {
+            LOG.warn("Mismatched notification type {}, not notifying listener", notification.getType(), e);
+            return;
+        }
+
+        final var bindingPath = serializer.fromYangInstanceIdentifier(path.getRootIdentifier());
+        executor.execute(() -> onNotification(delegate, bindingPath, castNotification));
+    }
+
+    abstract void onNotification(@NonNull L delegate, @NonNull InstanceIdentifier<?> path, @NonNull N notification);
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractLazySerializedEvent.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractLazySerializedEvent.java
new file mode 100644 (file)
index 0000000..b8fdf95
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.dom.adapter;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.time.Instant;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.dom.api.DOMEvent;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.yangtools.yang.binding.BaseNotification;
+import org.opendaylight.yangtools.yang.binding.EventInstantAware;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+
+/**
+ * Lazy serialized implementation of {@link DOMNotification} and {@link DOMEvent}.
+ *
+ * <p>
+ * This implementation performs serialization of data, only if receiver of notification actually accessed data from
+ * notification.
+ */
+abstract class AbstractLazySerializedEvent<T extends BaseNotification> implements DOMNotification, DOMEvent {
+    private final @NonNull BindingNormalizedNodeSerializer codec;
+    private final @NonNull T data;
+    private final @NonNull Absolute type;
+    private final @NonNull Instant eventInstant;
+
+    private volatile ContainerNode domBody;
+
+    AbstractLazySerializedEvent(final BindingNormalizedNodeSerializer codec, final T data, final Absolute type) {
+        this.codec = requireNonNull(codec);
+        this.data = requireNonNull(data);
+        this.type = requireNonNull(type);
+        this.eventInstant = data instanceof EventInstantAware ? ((EventInstantAware) data).eventInstant()
+            : Instant.now();
+    }
+
+    @Override
+    public final Absolute getType() {
+        return type;
+    }
+
+    @Override
+    public final ContainerNode getBody() {
+        ContainerNode local = domBody;
+        if (local == null) {
+            domBody = local = verifyNotNull(loadBody(codec));
+        }
+        return local;
+    }
+
+    abstract @NonNull ContainerNode loadBody(@NonNull BindingNormalizedNodeSerializer codec);
+
+    @Override
+    public final Instant getEventInstant() {
+        return eventInstant;
+    }
+
+    final @NonNull T getBindingData() {
+        return data;
+    }
+}
index c2adb18742c1941e3642dbcbc337eb8fe5f6f367..a763ab932c7995cdec0c89edc996f49c2a69389b 100644 (file)
@@ -16,6 +16,8 @@ import org.opendaylight.mdsal.binding.api.ActionProviderService;
 import org.opendaylight.mdsal.binding.api.ActionService;
 import org.opendaylight.mdsal.binding.api.BindingService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationPublishService;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationService;
 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
 import org.opendaylight.mdsal.binding.api.NotificationService;
 import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry;
@@ -31,6 +33,9 @@ public abstract class BindingDOMAdapterLoader extends AdapterLoader<BindingServi
             .put(RpcConsumerRegistry.class, BindingDOMRpcServiceAdapter.BUILDER_FACTORY)
             .put(ActionService.class, ActionServiceAdapter.BUILDER_FACTORY)
             .put(ActionProviderService.class, ActionProviderServiceAdapter.BUILDER_FACTORY)
+            .put(InstanceNotificationService.class, InstanceNotificationServiceAdapter.BUILDER_FACTORY)
+            .put(InstanceNotificationPublishService.class,
+                InstanceNotificationPublishServiceAdapter.BUILDER_FACTORY)
             .build();
 
     private final AdapterContext codec;
index a6f1fa8d5f86a2db38ccbb17d8ac4148bce3e8da..9354986195183085f31a7bd3688e162fd4bd0864 100644 (file)
@@ -11,7 +11,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.time.Instant;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.eclipse.jdt.annotation.NonNull;
@@ -20,7 +19,6 @@ import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Facto
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
 import org.opendaylight.mdsal.dom.api.DOMService;
-import org.opendaylight.yangtools.yang.binding.EventInstantAware;
 import org.opendaylight.yangtools.yang.binding.Notification;
 
 @VisibleForTesting
@@ -57,9 +55,7 @@ public class BindingDOMNotificationPublishServiceAdapter extends AbstractBinding
     }
 
     private @NonNull DOMNotification toDomNotification(final Notification<?> notification) {
-        final Instant instant = notification instanceof EventInstantAware
-                ? ((EventInstantAware) notification).eventInstant() : Instant.now();
-        return LazySerializedDOMNotification.create(currentSerializer(), notification, instant);
+        return new LazySerializedNotification(currentSerializer(), notification);
     }
 
     private static @NonNull ListenableFuture<? extends Object> toBindingResult(
index cce6e894a2628753e1849b30ae9c2d48228ba980..d6ff0c336f25aae9e3949264578391d23a6f803c 100644 (file)
@@ -19,10 +19,13 @@ import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableBiMap;
 import java.lang.reflect.Method;
 import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.ActionSpec;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationSpec;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
 import org.opendaylight.mdsal.binding.dom.codec.spi.ForwardingBindingDOMCodecServices;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
@@ -41,6 +44,7 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 import org.slf4j.Logger;
@@ -87,9 +91,26 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer
     }
 
     @NonNull Absolute getActionPath(final @NonNull ActionSpec<?, ?> spec) {
+        final var entry = resolvePath(spec.path());
+        final var stack = entry.getKey();
+        final var stmt = stack.enterSchemaTree(BindingReflections.findQName(spec.type()).bindTo(entry.getValue()));
+        verify(stmt instanceof ActionEffectiveStatement, "Action %s resolved to unexpected statement %s", spec, stmt);
+        return stack.toSchemaNodeIdentifier();
+    }
+
+    @NonNull Absolute getNotificationPath(final @NonNull InstanceNotificationSpec<?, ?> spec) {
+        final var entry = resolvePath(spec.path());
+        final var stack = entry.getKey();
+        final var stmt = stack.enterSchemaTree(BindingReflections.findQName(spec.type()).bindTo(entry.getValue()));
+        verify(stmt instanceof NotificationEffectiveStatement, "Notification %s resolved to unexpected statement %s",
+            spec, stmt);
+        return stack.toSchemaNodeIdentifier();
+    }
+
+    private @NonNull Entry<SchemaInferenceStack, QNameModule> resolvePath(final @NonNull InstanceIdentifier<?> path) {
         final var stack = SchemaInferenceStack.of(getRuntimeContext().getEffectiveModelContext());
-        final var it = toYangInstanceIdentifier(spec.path()).getPathArguments().iterator();
-        verify(it.hasNext(), "Unexpected empty instance identifier for %s", spec);
+        final var it = toYangInstanceIdentifier(path).getPathArguments().iterator();
+        verify(it.hasNext(), "Unexpected empty instance identifier for %s", path);
 
         QNameModule lastNamespace;
         do {
@@ -106,18 +127,16 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer
             lastNamespace = qname.getModule();
             if (stmt instanceof ListEffectiveStatement) {
                 // Lists have two steps
-                verify(it.hasNext(), "Unexpected list termination at %s in %s", stmt, spec);
+                verify(it.hasNext(), "Unexpected list termination at %s in %s", stmt, path);
                 // Verify just to make sure we are doing the right thing
                 final var skipped = it.next();
-                verify(skipped instanceof NodeIdentifier, "Unexpected skipped list entry item %s in %s", skipped, spec);
+                verify(skipped instanceof NodeIdentifier, "Unexpected skipped list entry item %s in %s", skipped, path);
                 verify(stmt.argument().equals(skipped.getNodeType()), "Mismatched list entry item %s in %s", skipped,
-                    spec);
+                    path);
             }
         } while (it.hasNext());
 
-        final var stmt = stack.enterSchemaTree(BindingReflections.findQName(spec.type()).bindTo(lastNamespace));
-        verify(stmt instanceof ActionEffectiveStatement, "Action %s resolved to unexpected statement %s", spec, stmt);
-        return stack.toSchemaNodeIdentifier();
+        return Map.entry(stack, lastNamespace);
     }
 
     // FIXME: This should be probably part of Binding Runtime context
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..d916355
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
+
+import java.util.concurrent.Executor;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationService.Listener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+
+final class InstanceNotificationListenerAdapter<P extends DataObject, N extends InstanceNotification<N, P>>
+        extends AbstractInstanceNotificationListenerAdapter<P, N, Listener<P, N>> {
+    InstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class<N> notificationClass,
+            final Listener<P, N> delegate, final Executor executor) {
+        super(adapterContext, notificationClass, delegate, executor);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    void onNotification(final Listener<P, N> delegate, final InstanceIdentifier<?> path, final N notification) {
+        delegate.onNotification((InstanceIdentifier<P>) path, notification);
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationPublishServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationPublishServiceAdapter.java
new file mode 100644 (file)
index 0000000..d1f11c9
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.dom.adapter;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationPublishService;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationSpec;
+import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.dom.api.DOMInstanceNotificationPublishService;
+import org.opendaylight.mdsal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+
+final class InstanceNotificationPublishServiceAdapter
+        extends AbstractBindingLoadingAdapter<DOMInstanceNotificationPublishService, InstanceNotificationSpec<?, ?>,
+            PublisherAdapter<?, ?>>
+        implements InstanceNotificationPublishService {
+    private static final class Builder extends BindingDOMAdapterBuilder<InstanceNotificationPublishService> {
+        Builder(final AdapterContext adapterContext) {
+            super(adapterContext);
+        }
+
+        @Override
+        public Set<Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.of(DOMInstanceNotificationPublishService.class);
+        }
+
+        @Override
+        protected InstanceNotificationPublishService createInstance(final ClassToInstanceMap<DOMService> delegates) {
+            return new InstanceNotificationPublishServiceAdapter(adapterContext(),
+                delegates.getInstance(DOMInstanceNotificationPublishService.class));
+        }
+    }
+
+    static final Factory<InstanceNotificationPublishService> BUILDER_FACTORY = Builder::new;
+
+    private InstanceNotificationPublishServiceAdapter(final AdapterContext adapterContext,
+            final DOMInstanceNotificationPublishService domPublishService) {
+        super(adapterContext, domPublishService);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <N extends InstanceNotification<N, P>, P extends DataObject> Publisher<N, P> newPublisher(
+            final InstanceNotificationSpec<N, P> notificationSpec) {
+        return (Publisher<N, P>) getAdapter(notificationSpec);
+    }
+
+    @Override
+    PublisherAdapter<?, ?> loadAdapter(final InstanceNotificationSpec<?, ?> key) {
+        final var type = key.type();
+        checkArgument(BindingReflections.isBindingClass(type));
+        checkArgument(type.isInterface(), "Supplied Notification type must be an interface.");
+        checkArgument(InstanceNotification.class.isAssignableFrom(type), "Illegal instance notification class %s",
+            type);
+        return new PublisherAdapter<>(adapterContext(), getDelegate(), key);
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationServiceAdapter.java
new file mode 100644 (file)
index 0000000..b746e36
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.dom.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationService;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationSpec;
+import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMInstanceNotificationListener;
+import org.opendaylight.mdsal.dom.api.DOMInstanceNotificationService;
+import org.opendaylight.mdsal.dom.api.DOMService;
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedListNotification;
+
+final class InstanceNotificationServiceAdapter implements InstanceNotificationService {
+    private static final class Builder extends BindingDOMAdapterBuilder<InstanceNotificationService> {
+        Builder(final AdapterContext adapterContext) {
+            super(adapterContext);
+        }
+
+        @Override
+        protected InstanceNotificationService createInstance(final ClassToInstanceMap<DOMService> delegates) {
+            return new InstanceNotificationServiceAdapter(adapterContext(),
+                delegates.getInstance(DOMInstanceNotificationService.class));
+        }
+
+        @Override
+        public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.of(DOMInstanceNotificationService.class);
+        }
+    }
+
+    static final Factory<InstanceNotificationService> BUILDER_FACTORY = Builder::new;
+
+    private final AdapterContext adapterContext;
+    private final DOMInstanceNotificationService domNotifService;
+
+    private InstanceNotificationServiceAdapter(final AdapterContext adapterContext,
+            final DOMInstanceNotificationService domNotifService) {
+        this.adapterContext = requireNonNull(adapterContext);
+        this.domNotifService = requireNonNull(domNotifService);
+    }
+
+    @Override
+    public <P extends DataObject, N extends InstanceNotification<N, P>> Registration registerListener(
+            final InstanceNotificationSpec<N, P> spec, final InstanceIdentifier<P> path, final Listener<P, N> listener,
+            final Executor executor) {
+        return registerListener(spec, path,
+            new InstanceNotificationListenerAdapter<>(adapterContext, spec.type(), listener, executor));
+    }
+
+    @Override
+    public <P extends DataObject & Identifiable<K>, N extends KeyedListNotification<N, P, K>, K extends Identifier<P>>
+            Registration registerListener(final InstanceNotificationSpec<N, P> spec,
+                final KeyedInstanceIdentifier<P, K> path, final KeyedListListener<P, N, K> listener,
+                final Executor executor) {
+        return registerListener(spec, path,
+            new KeyedInstanceNotificationListenerAdapter<>(adapterContext, spec.type(), listener, executor));
+    }
+
+    private @NonNull Registration registerListener(final @NonNull InstanceNotificationSpec<?, ?> spec,
+            final @NonNull InstanceIdentifier<?> path, final @NonNull DOMInstanceNotificationListener listener) {
+        final var serializer = adapterContext.currentSerializer();
+        return domNotifService.registerNotificationListener(
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, serializer.toYangInstanceIdentifier(path)),
+            serializer.getNotificationPath(spec).lastNodeIdentifier(), listener);
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/KeyedInstanceNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/KeyedInstanceNotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..a1d9a6d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
+
+import static com.google.common.base.Verify.verify;
+
+import java.util.concurrent.Executor;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationService.KeyedListListener;
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedListNotification;
+
+final class KeyedInstanceNotificationListenerAdapter<P extends DataObject & Identifiable<K>, K extends Identifier<P>,
+            N extends KeyedListNotification<N, P, K>>
+        extends AbstractInstanceNotificationListenerAdapter<P, N, KeyedListListener<P, N, K>> {
+    KeyedInstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class<N> notificationClass,
+            final KeyedListListener<P, N, K> delegate, final Executor executor) {
+        super(adapterContext, notificationClass, delegate, executor);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    void onNotification(final KeyedListListener<P, N, K> delegate, final InstanceIdentifier<?> path,
+            final N notification) {
+        verify(path instanceof KeyedInstanceIdentifier, "Unexpected path %s", path);
+        delegate.onNotification((KeyedInstanceIdentifier<P, K>) path, notification);
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotification.java
deleted file mode 100644 (file)
index 6f56169..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import java.time.Instant;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.mdsal.dom.api.DOMEvent;
-import org.opendaylight.mdsal.dom.api.DOMNotification;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-
-/**
- * Lazy serialized implementation of DOM Notification.
- *
- * <p>
- * This implementation performs serialization of data, only if receiver of notification actually accessed data from
- * notification.
- */
-final class LazySerializedDOMNotification implements DOMNotification, DOMEvent {
-    private static final LoadingCache<Class<?>, Absolute> PATHS = CacheBuilder.newBuilder().weakKeys()
-        .build(new CacheLoader<Class<?>, Absolute>() {
-            @Override
-            public Absolute load(final Class<?> key) {
-                // TODO: for nested (YANG 1.1) notifications we will need the SchemaNodeIdentifier where the
-                //       notification is being invoked and use that instead of ROOT. How Binding users will refer to
-                //       it is TBD (but probably InstanceIdentifier, which means we will need to do some lifting to
-                //       find the SchemaNodeIdentifier), as we will need to deal with the same multiplicies we need
-                //       for data due to grouping-based reuse.
-                return Absolute.of(BindingReflections.findQName(key)).intern();
-            }
-        });
-
-    private final @NonNull BindingNormalizedNodeSerializer codec;
-    private final @NonNull Notification<?> data;
-    private final @NonNull Absolute type;
-    private final @NonNull Instant eventInstant;
-
-    private volatile ContainerNode domBody;
-
-    LazySerializedDOMNotification(final BindingNormalizedNodeSerializer codec, final Notification<?> data,
-            final Absolute type, final Instant eventInstant) {
-        this.codec = requireNonNull(codec);
-        this.data = requireNonNull(data);
-        this.type = requireNonNull(type);
-        this.eventInstant = requireNonNull(eventInstant);
-    }
-
-    static @NonNull DOMNotification create(final BindingNormalizedNodeSerializer codec, final Notification<?> data,
-            final Instant eventInstant) {
-        final Absolute type = PATHS.getUnchecked(data.implementedInterface());
-        return new LazySerializedDOMNotification(codec, data, type, eventInstant);
-    }
-
-    @Override
-    public Absolute getType() {
-        return type;
-    }
-
-    @Override
-    public ContainerNode getBody() {
-        ContainerNode local = domBody;
-        if (local == null) {
-            domBody = local = codec.toNormalizedNodeNotification(data);
-        }
-        return local;
-    }
-
-    @Override
-    public Instant getEventInstant() {
-        return eventInstant;
-    }
-
-    @NonNull Notification<?> getBindingData() {
-        return data;
-    }
-}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedInstanceNotification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedInstanceNotification.java
new file mode 100644 (file)
index 0000000..c60cf24
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
+
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+
+/**
+ * {@link AbstractLazySerializedEvent} specialized to RFC7950 instance notifications.
+ */
+final class LazySerializedInstanceNotification extends AbstractLazySerializedEvent<InstanceNotification<?, ?>> {
+    LazySerializedInstanceNotification(final BindingNormalizedNodeSerializer codec, final Absolute path,
+            final InstanceNotification<?, ?> data) {
+        super(codec, data, path);
+    }
+
+    @Override
+    ContainerNode loadBody(final BindingNormalizedNodeSerializer codec) {
+        return codec.toNormalizedNodeNotification(getType(), getBindingData());
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedNotification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedNotification.java
new file mode 100644 (file)
index 0000000..ba9b54e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.dom.adapter;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+
+/**
+ * {@link AbstractLazySerializedEvent} specialized to RFC6020 global notifications.
+ */
+final class LazySerializedNotification extends AbstractLazySerializedEvent<Notification<?>> {
+    private static final LoadingCache<Class<?>, Absolute> PATHS = CacheBuilder.newBuilder().weakKeys()
+        .build(new CacheLoader<>() {
+            @Override
+            public Absolute load(final Class<?> key) {
+                // FIXME: do not use reflection here, look the QName up from BindingRuntimeType
+                return Absolute.of(BindingReflections.findQName(key)).intern();
+            }
+        });
+
+    LazySerializedNotification(final BindingNormalizedNodeSerializer codec, final Notification<?> data) {
+        super(codec, data, PATHS.getUnchecked(data.implementedInterface()));
+    }
+
+    @Override
+    ContainerNode loadBody(final BindingNormalizedNodeSerializer codec) {
+        return codec.toNormalizedNodeNotification(getBindingData());
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/PublisherAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/PublisherAdapter.java
new file mode 100644 (file)
index 0000000..dd862f6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.dom.adapter;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationPublishService.Publisher;
+import org.opendaylight.mdsal.binding.api.InstanceNotificationSpec;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMInstanceNotificationPublishService;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceNotification;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+
+/**
+ * An implementation of {@link Publisher} backed by a {@link DOMInstanceNotificationPublishService}.
+ */
+final class PublisherAdapter<N extends InstanceNotification<N, P>, P extends DataObject>
+        extends AbstractBindingAdapter<DOMInstanceNotificationPublishService> implements Publisher<N, P> {
+    private final @NonNull Absolute notificationPath;
+
+    PublisherAdapter(final AdapterContext adapterContext, final DOMInstanceNotificationPublishService domPublishService,
+            final InstanceNotificationSpec<?, ?> spec) {
+        super(adapterContext, domPublishService);
+        notificationPath = currentSerializer().getNotificationPath(spec);
+    }
+
+    @Override
+    public void putNotification(final InstanceIdentifier<P> path, final N notification) throws InterruptedException {
+        final var serializer = currentSerializer();
+        getDelegate().putNotification(toDomPath(serializer, path), toDomNotification(serializer, notification));
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final InstanceIdentifier<P> path,
+            final N notification) {
+        final var serializer = currentSerializer();
+        return toBindingResult(getDelegate().offerNotification(toDomPath(serializer, path),
+            toDomNotification(serializer, notification)));
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final InstanceIdentifier<P> path, final N notification,
+            final long timeout,  final TimeUnit unit) throws InterruptedException {
+        final var serializer = currentSerializer();
+        return toBindingResult(getDelegate().offerNotification(toDomPath(serializer, path),
+            toDomNotification(serializer, notification), timeout, unit));
+    }
+
+    private static @NonNull ListenableFuture<? extends Object> toBindingResult(
+            final @NonNull ListenableFuture<? extends Object> domResult) {
+        return DOMInstanceNotificationPublishService.REJECTED.equals(domResult) ? REJECTED : domResult;
+    }
+
+    private static @NonNull DOMDataTreeIdentifier toDomPath(final CurrentAdapterSerializer serializer,
+            final InstanceIdentifier<?> path) {
+        return new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, serializer.toYangInstanceIdentifier(path));
+    }
+
+    private @NonNull DOMNotification toDomNotification(final CurrentAdapterSerializer serializer,
+            final InstanceNotification<?, ?> notification) {
+        return new LazySerializedInstanceNotification(serializer, notificationPath, notification);
+    }
+}
index 951f286dd75ff6a72c6d0d92f85dac919249a2d5..8d7f779c34ba6d3ab74297468365583a966bb785 100644 (file)
@@ -12,7 +12,6 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
-import java.time.Instant;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
@@ -25,7 +24,7 @@ public class LazySerializedDOMNotificationTest {
     public void basicTest() throws Exception {
         BindingNormalizedNodeSerializer codec = mock(BindingNormalizedNodeSerializer.class);
         final DOMNotification lazySerializedDOMNotification =
-                LazySerializedDOMNotification.create(codec, new TwoLevelListChangedBuilder().build(), Instant.now());
+                new LazySerializedNotification(codec, new TwoLevelListChangedBuilder().build());
         ContainerNode containerNode = mock(ContainerNode.class);
         doReturn(containerNode).when(codec).toNormalizedNodeNotification(any());
         assertEquals(containerNode, lazySerializedDOMNotification.getBody());
index 2d3a0a458d5e1bb1700abe373da08ab2d8557235..773b1778717cf414193ecfa28b369517fa0fa453 100644 (file)
@@ -14,6 +14,7 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -80,7 +81,7 @@ public interface BindingNormalizedNodeSerializer {
      * @param data NormalizedNode representing data
      * @return Binding representation of Notification
      */
-    @Nullable Notification<?> fromNormalizedNodeNotification(@NonNull Absolute path, @NonNull ContainerNode data);
+    @NonNull BaseNotification fromNormalizedNodeNotification(@NonNull Absolute path, @NonNull ContainerNode data);
 
     /**
      * Translates supplied NormalizedNode Notification into Binding data, optionally taking an instant
@@ -92,7 +93,7 @@ public interface BindingNormalizedNodeSerializer {
      * @return Binding representation of Notification
      */
     @Beta
-    @Nullable Notification<?> fromNormalizedNodeNotification(@NonNull Absolute path, @NonNull ContainerNode data,
+    @NonNull BaseNotification fromNormalizedNodeNotification(@NonNull Absolute path, @NonNull ContainerNode data,
             @Nullable Instant eventInstant);
 
     /**
@@ -131,11 +132,20 @@ public interface BindingNormalizedNodeSerializer {
     /**
      * Translates supplied Binding Notification or output into NormalizedNode notification.
      *
-     * @param data NormalizedNode representing notification data
+     * @param data {@link Notification} representing notification data
      * @return NormalizedNode representation of notification
      */
     @NonNull ContainerNode toNormalizedNodeNotification(@NonNull Notification<?> data);
 
+    /**
+     * Translates supplied Binding Notification or output into NormalizedNode notification.
+     *
+     * @param path schema node identifier of the notification
+     * @param data {@link BaseNotification} representing notification data
+     * @return NormalizedNode representation of notification
+     */
+    @NonNull ContainerNode toNormalizedNodeNotification(@NonNull Absolute path, @NonNull BaseNotification data);
+
     /**
      * Translates supplied Binding RPC input or output into NormalizedNode data.
      *
index cc28ef3b80ee3110547c5b629417a41b55ecba40..39f8d6ede0c10e32c01f99a0b38aa4e152887afe 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.mdsal.binding.dom.codec.api.BindingLazyContainerNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -71,6 +72,11 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
         return delegate().toNormalizedNodeNotification(data);
     }
 
+    @Override
+    public ContainerNode toNormalizedNodeNotification(final Absolute path, final BaseNotification data) {
+        return delegate().toNormalizedNodeNotification(path, data);
+    }
+
     @Override
     public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
         return delegate().toNormalizedNodeRpcData(data);
@@ -95,12 +101,12 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     }
 
     @Override
-    public Notification<?> fromNormalizedNodeNotification(final Absolute path, final ContainerNode data) {
+    public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data) {
         return delegate().fromNormalizedNodeNotification(path, data);
     }
 
     @Override
-    public Notification<?> fromNormalizedNodeNotification(final Absolute path, final ContainerNode data,
+    public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data,
             final Instant eventInstant) {
         return delegate().fromNormalizedNodeNotification(path, data, eventInstant);
     }
index fa7dcf656c5caa2865224781ac43768a3248af7d..5c6b327413ca05a136b0e868115f8ae2e34aca83 100644 (file)
@@ -59,6 +59,7 @@ import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
@@ -560,16 +561,15 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     @Override
-    public Notification<?> fromNormalizedNodeNotification(final Absolute path, final ContainerNode data) {
+    public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data) {
         return getNotificationContext(path).deserialize(data);
     }
 
     @Override
-    public Notification<?> fromNormalizedNodeNotification(final Absolute path, final ContainerNode data,
+    public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data,
             final Instant eventInstant) {
         return eventInstant == null ? fromNormalizedNodeNotification(path, data)
                 : getNotificationContext(path).deserialize(data, eventInstant);
-
     }
 
     @Override
@@ -598,6 +598,17 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
                 (Class<? extends Notification<?>>) iface.asSubclass(Notification.class), domWriter));
     }
 
+    @Override
+    public ContainerNode toNormalizedNodeNotification(final Absolute path, final BaseNotification data) {
+        checkArgument(data instanceof DataObject, "Unexpected data %s", data);
+        @SuppressWarnings("rawtypes")
+        final NotificationCodecContext notifContext = getNotificationContext(path);
+        @SuppressWarnings("unchecked")
+        final var result = notifContext.serialize((DataObject) data);
+        verify(result instanceof ContainerNode, "Unexpected result %s from %s", result, data);
+        return (ContainerNode) result;
+    }
+
     @Override
     @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
     public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
index 8f7d92f949ad594969e1571d74bd40485c9a6bf4..87da154d3393a6437a1e1d0bf20cda371cccc05c 100644 (file)
@@ -37,14 +37,14 @@ import net.bytebuddy.matcher.ElementMatchers;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
+import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
-import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
-final class NotificationCodecContext<D extends DataObject & Notification<D>>
+final class NotificationCodecContext<D extends DataObject & BaseNotification>
         extends DataObjectCodecContext<D, NotificationRuntimeType> {
     private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class);
 
@@ -63,7 +63,7 @@ final class NotificationCodecContext<D extends DataObject & Notification<D>>
 
     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
         DistinctNodeContainer.class, Instant.class);
-    private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(Notification.class,
+    private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(BaseNotification.class,
         NotificationCodecContext.class, ContainerNode.class, Instant.class);
     private static final String INSTANT_FIELD = "instant";
 
@@ -107,13 +107,15 @@ final class NotificationCodecContext<D extends DataObject & Notification<D>>
     }
 
     @SuppressWarnings("checkstyle:illegalCatch")
-    Notification<?> deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
+    @NonNull BaseNotification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
+        final BaseNotification ret;
         try {
-            return (Notification<?>) eventProxy.invokeExact(this, data, eventInstant);
+            ret = (BaseNotification) eventProxy.invokeExact(this, data, eventInstant);
         } catch (final Throwable e) {
             Throwables.throwIfUnchecked(e);
-            throw new IllegalStateException(e);
+            throw new LinkageError("Failed to instantiate notification", e);
         }
+        return verifyNotNull(ret);
     }
 
     @Override
index a9debb048f2a887ea6772d1bfa07c85a7c884536..906f0d671c372f2645b8c00f2774fe7a8477bb27 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.ImmutableMap;
@@ -19,7 +20,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
-import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -58,8 +58,8 @@ public class NotificationProcessingTest extends AbstractBindingCodecTest {
 
     @Test
     public void testNormalizedToNotification() {
-        final Notification<?> bindingDeserialized = codecContext.fromNormalizedNodeNotification(Absolute.of(
-            TwoLevelListChanged.QNAME), createTestDomData());
+        final var bindingDeserialized = codecContext.fromNormalizedNodeNotification(
+            Absolute.of(TwoLevelListChanged.QNAME), createTestDomData());
         assertTrue(bindingDeserialized instanceof TwoLevelListChanged);
         assertEquals(createTestBindingData(), bindingDeserialized);
     }
@@ -67,18 +67,18 @@ public class NotificationProcessingTest extends AbstractBindingCodecTest {
     @Test
     public void testNormalizedToNotificationWithInstant() {
         final Instant instant = Instant.now();
-        final Notification<?> bindingDeserialized = codecContext.fromNormalizedNodeNotification(Absolute.of(
-            TwoLevelListChanged.QNAME), createTestDomData(), instant);
+        final var bindingDeserialized = codecContext.fromNormalizedNodeNotification(
+            Absolute.of(TwoLevelListChanged.QNAME), createTestDomData(), instant);
         assertTrue(bindingDeserialized instanceof TwoLevelListChanged);
         assertEquals(createTestBindingData(), bindingDeserialized);
         assertTrue(bindingDeserialized instanceof EventInstantAware);
-        assertEquals(instant, ((EventInstantAware) bindingDeserialized).eventInstant());
+        assertSame(instant, ((EventInstantAware) bindingDeserialized).eventInstant());
     }
 
     @Test
     public void testNormalizedToNotificationWithNull() {
-        final Notification<?> bindingDeserialized = codecContext.fromNormalizedNodeNotification(Absolute.of(
-            TwoLevelListChanged.QNAME), createTestDomData(), null);
+        final var bindingDeserialized = codecContext.fromNormalizedNodeNotification(
+            Absolute.of(TwoLevelListChanged.QNAME), createTestDomData(), null);
         assertTrue(bindingDeserialized instanceof TwoLevelListChanged);
         assertEquals(createTestBindingData(), bindingDeserialized);
     }