From: Robert Varga Date: Wed, 6 Apr 2022 14:58:07 +0000 (+0200) Subject: Add InstanceNotification(Publish)ServiceAdapter X-Git-Tag: v9.0.2~13 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=cb0f3a767fa856c220edd674fda4d55dde408c64 Add InstanceNotification(Publish)ServiceAdapter 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 --- 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 index 9b9b4ef264..de0c8649b5 100644 --- 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 @@ -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}: - * - * - *

- * 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 REJECTED = FluentFutures.immediateFailedFluentFuture( - new NotificationRejectedException("Rejected due to resource constraints.")); - - /** - * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but - * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. - * - * Note: This call will block when the notification queue is full. + * 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 Generated InstanceNotification interface type + * @param

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 */ - , P extends DataObject> void putNotification( - @NonNull DataTreeIdentifier

path, @NonNull N notification) throws InterruptedException; + , P extends DataObject> @NonNull Publisher newPublisher( + InstanceNotificationSpec 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}: + *

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

- * 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 Generated InstanceNotification interface type + * @param

Notification parent type */ - , P extends DataObject> - @NonNull ListenableFuture offerNotification(@NonNull DataTreeIdentifier

path, + @Beta + interface Publisher, P extends DataObject> { + /** + * Well-known value indicating that the binding-aware implementation is currently not able to accept a + * notification. + */ + @NonNull ListenableFuture REJECTED = FluentFutures.immediateFailedFluentFuture( + new NotificationRejectedException("Rejected due to resource constraints.")); + + /** + * Publishes a notification to subscribed listeners. This initiates the process of sending the notification, but + * delivery to the listeners can happen asynchronously, potentially after a call to this method returns. + * + * Note: This call will block when the notification queue is full. + * + * @param 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

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

+ * Still guaranteed not to block. Returns Listenable Future which will complete once the delivery is completed. + * + * @param 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 offerNotification(@NonNull InstanceIdentifier

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

path, + /** + * 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 offerNotification(@NonNull InstanceIdentifier

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 index b8fb57828f..7c3bc1383d 100644 --- 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 @@ -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 { -

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

path, - T listener, Executor executor); +

> @NonNull Registration registerListener( + InstanceNotificationSpec spec, InstanceIdentifier

path, Listener listener, Executor executor); - default

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

path, final T listener) { + default

> @NonNull Registration registerListener( + final InstanceNotificationSpec spec, final InstanceIdentifier

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

, N extends KeyedListNotification, K extends Identifier

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

path, T listener, Executor executor); +

, N extends KeyedListNotification, K extends Identifier

> + @NonNull Registration registerListener(InstanceNotificationSpec spec, KeyedInstanceIdentifier path, + KeyedListListener listener, Executor executor); - default

, N extends KeyedListNotification, K extends Identifier

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

path, final T listener) { + default

, N extends KeyedListNotification, K extends Identifier

> + @NonNull Registration registerListener(final InstanceNotificationSpec spec, + final KeyedInstanceIdentifier path, final KeyedListListener 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

path, @NonNull N notification); + void onNotification(@NonNull InstanceIdentifier

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

path, @NonNull N notification); + void onNotification(@NonNull KeyedInstanceIdentifier path, @NonNull N notification); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java index 4740420f3f..eae5f768f0 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java @@ -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 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 index 0000000000..ddd7aae29e --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractInstanceNotificationListenerAdapter.java @@ -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

, + L> implements DOMInstanceNotificationListener { + private static final Logger LOG = LoggerFactory.getLogger(AbstractInstanceNotificationListenerAdapter.class); + + private final @NonNull AdapterContext adapterContext; + private final @NonNull Class notificationClass; + private final @NonNull Executor executor; + private final @NonNull L delegate; + + AbstractInstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class 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 index 0000000000..b8fdf95b2d --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractLazySerializedEvent.java @@ -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}. + * + *

+ * This implementation performs serialization of data, only if receiver of notification actually accessed data from + * notification. + */ +abstract class AbstractLazySerializedEvent 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; + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMAdapterLoader.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMAdapterLoader.java index c2adb18742..a763ab932c 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMAdapterLoader.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMAdapterLoader.java @@ -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 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 toBindingResult( diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java index cce6e894a2..d6ff0c336f 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java @@ -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 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 index 0000000000..d916355363 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationListenerAdapter.java @@ -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

> + extends AbstractInstanceNotificationListenerAdapter> { + InstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class notificationClass, + final Listener delegate, final Executor executor) { + super(adapterContext, notificationClass, delegate, executor); + } + + @Override + @SuppressWarnings("unchecked") + void onNotification(final Listener delegate, final InstanceIdentifier path, final N notification) { + delegate.onNotification((InstanceIdentifier

) 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 index 0000000000..d1f11c9b4a --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationPublishServiceAdapter.java @@ -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, + PublisherAdapter> + implements InstanceNotificationPublishService { + private static final class Builder extends BindingDOMAdapterBuilder { + Builder(final AdapterContext adapterContext) { + super(adapterContext); + } + + @Override + public Set> getRequiredDelegates() { + return ImmutableSet.of(DOMInstanceNotificationPublishService.class); + } + + @Override + protected InstanceNotificationPublishService createInstance(final ClassToInstanceMap delegates) { + return new InstanceNotificationPublishServiceAdapter(adapterContext(), + delegates.getInstance(DOMInstanceNotificationPublishService.class)); + } + } + + static final Factory BUILDER_FACTORY = Builder::new; + + private InstanceNotificationPublishServiceAdapter(final AdapterContext adapterContext, + final DOMInstanceNotificationPublishService domPublishService) { + super(adapterContext, domPublishService); + } + + @Override + @SuppressWarnings("unchecked") + public , P extends DataObject> Publisher newPublisher( + final InstanceNotificationSpec notificationSpec) { + return (Publisher) 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 index 0000000000..b746e368d1 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/InstanceNotificationServiceAdapter.java @@ -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 { + Builder(final AdapterContext adapterContext) { + super(adapterContext); + } + + @Override + protected InstanceNotificationService createInstance(final ClassToInstanceMap delegates) { + return new InstanceNotificationServiceAdapter(adapterContext(), + delegates.getInstance(DOMInstanceNotificationService.class)); + } + + @Override + public Set> getRequiredDelegates() { + return ImmutableSet.of(DOMInstanceNotificationService.class); + } + } + + static final Factory 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

> Registration registerListener( + final InstanceNotificationSpec spec, final InstanceIdentifier

path, final Listener listener, + final Executor executor) { + return registerListener(spec, path, + new InstanceNotificationListenerAdapter<>(adapterContext, spec.type(), listener, executor)); + } + + @Override + public

, N extends KeyedListNotification, K extends Identifier

> + Registration registerListener(final InstanceNotificationSpec spec, + final KeyedInstanceIdentifier path, final KeyedListListener 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 index 0000000000..a1d9a6d0a4 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/KeyedInstanceNotificationListenerAdapter.java @@ -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

, K extends Identifier

, + N extends KeyedListNotification> + extends AbstractInstanceNotificationListenerAdapter> { + KeyedInstanceNotificationListenerAdapter(final AdapterContext adapterContext, final Class notificationClass, + final KeyedListListener delegate, final Executor executor) { + super(adapterContext, notificationClass, delegate, executor); + } + + @Override + @SuppressWarnings("unchecked") + void onNotification(final KeyedListListener delegate, final InstanceIdentifier path, + final N notification) { + verify(path instanceof KeyedInstanceIdentifier, "Unexpected path %s", path); + delegate.onNotification((KeyedInstanceIdentifier) 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 index 6f56169aba..0000000000 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotification.java +++ /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. - * - *

- * 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, Absolute> PATHS = CacheBuilder.newBuilder().weakKeys() - .build(new CacheLoader, 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 index 0000000000..c60cf242b8 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedInstanceNotification.java @@ -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> { + 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 index 0000000000..ba9b54e66a --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedNotification.java @@ -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> { + private static final LoadingCache, 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 index 0000000000..dd862f63d1 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/PublisherAdapter.java @@ -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, P extends DataObject> + extends AbstractBindingAdapter implements Publisher { + 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

path, final N notification) throws InterruptedException { + final var serializer = currentSerializer(); + getDelegate().putNotification(toDomPath(serializer, path), toDomNotification(serializer, notification)); + } + + @Override + public ListenableFuture offerNotification(final InstanceIdentifier

path, + final N notification) { + final var serializer = currentSerializer(); + return toBindingResult(getDelegate().offerNotification(toDomPath(serializer, path), + toDomNotification(serializer, notification))); + } + + @Override + public ListenableFuture offerNotification(final InstanceIdentifier

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 toBindingResult( + final @NonNull ListenableFuture 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); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotificationTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotificationTest.java index 951f286dd7..8d7f779c34 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotificationTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazySerializedDOMNotificationTest.java @@ -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()); diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java index 2d3a0a458d..773b177871 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java @@ -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. * diff --git a/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java b/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java index cc28ef3b80..39f8d6ede0 100644 --- a/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java +++ b/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java @@ -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); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java index fa7dcf656c..5c6b327413 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java @@ -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>) 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) { diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java index 8f7d92f949..87da154d33 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java @@ -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> +final class NotificationCodecContext extends DataObjectCodecContext { private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class); @@ -63,7 +63,7 @@ final class NotificationCodecContext> 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> } @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 diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationProcessingTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationProcessingTest.java index a9debb048f..906f0d671c 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationProcessingTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationProcessingTest.java @@ -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); }