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>
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;
+ }
}
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;
/**
@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());
}
* @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);
}
/**
* @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);
}
}
*/
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;
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;
}
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.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;
+ }
+}
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;
.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;
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;
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
}
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(
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;
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;
}
@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 {
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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.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);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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;
- }
-}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.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);
+ }
+}
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;
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());
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;
* @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
* @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);
/**
/**
* 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.
*
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;
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);
}
@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);
}
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;
}
@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
(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) {
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);
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";
}
@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
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;
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;
@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);
}
@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);
}