import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.TimeUnit;
-import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
-import org.opendaylight.mdsal.binding.javav2.spec.base.KeyedInstanceIdentifier;
import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
-import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
/**
* A {@link NotificationService} which also allows its users to submit YANG-modeled notifications for delivery.
* - {@link #offerNotification(org.opendaylight.mdsal.binding.javav2.spec.base.Notification, int, TimeUnit)}, which may
* block for specified time if resources are thin.
*
- * <p>
- * Every method has two alternatives for YANG 1.1 notifications tied to container or list node.
- *
*<p>
* The actual delivery to listeners is asynchronous and implementation-specific.
* Users of this interface should not make any assumptions as to whether the
*/
@Beta
-public interface NotificationPublishService {
+public interface NotificationPublishService extends BindingService {
ListenableFuture<Object> REJECTED = Futures.immediateFailedFuture(
new NotificationRejectedException("Rejected due to resource constraints."));
- void putNotification(Notification notification) throws InterruptedException;
-
- ListenableFuture<?> offerNotification(Notification notification);
-
- ListenableFuture<?> offerNotification(Notification notification, int timeout, TimeUnit unit)
- throws InterruptedException;
+ void putNotification(Notification<?> notification) throws InterruptedException;
+ ListenableFuture<?> offerNotification(Notification<?> notification);
- <T extends TreeNode> void putContainerNotification(Notification notification, InstanceIdentifier<T> ii)
+ ListenableFuture<?> offerNotification(Notification<?> notification, int timeout, TimeUnit unit)
throws InterruptedException;
- <T extends TreeNode> ListenableFuture<?> offerContainerNotification(Notification notification,
- InstanceIdentifier<T> ii);
-
- <T extends TreeNode> ListenableFuture<?> offerContainerNotification(Notification notification, int timeout,
- TimeUnit unit, InstanceIdentifier<T> ii) throws InterruptedException;
-
-
- <T extends TreeNode, K> void putListNotification(Notification notification, KeyedInstanceIdentifier<T, K> kii)
- throws InterruptedException;
-
- <T extends TreeNode, K> ListenableFuture<?> offerListNotification(Notification notification,
- KeyedInstanceIdentifier<T, K> kii);
-
- <T extends TreeNode, K> ListenableFuture<?> offerListNotification(Notification notification, int timeout,
- TimeUnit unit, KeyedInstanceIdentifier<T, K> kii) throws InterruptedException;
}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.adapter.impl.notification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.serializer.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedDOMNotification;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.NotificationListener;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Notification listener adapter.
+ */
+@Beta
+public class BindingDOMNotificationListenerAdapter implements DOMNotificationListener {
+
+ private final BindingNormalizedNodeSerializer codec;
+ private final NotificationListener delegate;
+ private final Map<SchemaPath, NotificationListenerInvoker> invokers;
+
+ public BindingDOMNotificationListenerAdapter(final BindingNormalizedNodeSerializer codec,
+ final NotificationListener delegate) {
+ this.codec = codec;
+ this.delegate = delegate;
+ this.invokers = createInvokerMapFor(delegate.getClass());
+ }
+
+ @Override
+ public void onNotification(@Nonnull final DOMNotification notification) {
+ final Notification<?> baNotification = deserialize(notification);
+ final QName notificationQName = notification.getType().getLastComponent();
+ getInvoker(notification.getType()).invokeNotification(delegate, notificationQName,
+ (Instantiable<?>) baNotification);
+ }
+
+ private Notification<?> deserialize(final DOMNotification notification) {
+ if (notification instanceof LazySerializedDOMNotification) {
+ return ((LazySerializedDOMNotification) notification).getBindingData();
+ }
+ return codec.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+ }
+
+ private NotificationListenerInvoker getInvoker(final SchemaPath type) {
+ return invokers.get(type);
+ }
+
+ /**
+ * Get supported notifications.
+ *
+ * @return supported notifications
+ */
+ public Set<SchemaPath> getSupportedNotifications() {
+ return invokers.keySet();
+ }
+
+ /**
+ * Create map of all notification listeners.
+ *
+ * @param implClz
+ * - class type of {@link NotificationListener}
+ * @return map of notification listeners
+ */
+ private static Map<SchemaPath, NotificationListenerInvoker>
+ createInvokerMapFor(final Class<? extends NotificationListener> implClz) {
+ final Map<SchemaPath, NotificationListenerInvoker> builder = new HashMap<>();
+ for (final TypeToken<?> ifaceToken : TypeToken.of(implClz).getTypes().interfaces()) {
+ final Class<?> iface = ifaceToken.getRawType();
+ if (NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends NotificationListener> listenerType =
+ (Class<? extends NotificationListener>) iface;
+ final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+ for (final SchemaPath path : getNotificationTypes(listenerType)) {
+ builder.put(path, invoker);
+ }
+ }
+ }
+ return ImmutableMap.copyOf(builder);
+ }
+
+ private static Set<SchemaPath> getNotificationTypes(final Class<? extends NotificationListener> type) {
+ // TODO: Investigate possibility and performance impact if we cache this or expose
+ // it from NotificationListenerInvoker
+ final Set<SchemaPath> ret = new HashSet<>();
+ for (final Method method : type.getMethods()) {
+ if (BindingReflections.isNotificationCallback(method)) {
+ final Class<?> notification = method.getParameterTypes()[0];
+ final QName name = BindingReflections.findQName(notification);
+ ret.add(SchemaPath.create(true, name));
+ }
+ }
+ return ret;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.adapter.impl.notification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.mdsal.binding.javav2.api.NotificationPublishService;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedDOMNotification;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.mdsal.dom.api.DOMService;
+
+/**
+ * Adapter for notification publisher service.
+ */
+@Beta
+public class BindingDOMNotificationPublishServiceAdapter implements NotificationPublishService, AutoCloseable {
+
+ public static final Factory<NotificationPublishService> BUILDER_FACTORY = Builder::new;
+
+ private final BindingToNormalizedNodeCodec codecRegistry;
+ private final DOMNotificationPublishService domPublishService;
+
+ public BindingDOMNotificationPublishServiceAdapter(final BindingToNormalizedNodeCodec codec,
+ final DOMNotificationPublishService domPublishService) {
+ this.codecRegistry = codec;
+ this.domPublishService = domPublishService;
+ }
+
+ public BindingToNormalizedNodeCodec getCodecRegistry() {
+ return codecRegistry;
+ }
+
+ public DOMNotificationPublishService getDomPublishService() {
+ return domPublishService;
+ }
+
+ @Override
+ public void putNotification(final Notification<?> notification) throws InterruptedException {
+ domPublishService.putNotification(toDomNotification(notification));
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final Notification<?> notification) {
+ final ListenableFuture<?> offerResult = domPublishService.offerNotification(toDomNotification(notification));
+ return DOMNotificationPublishService.REJECTED.equals(offerResult) ? NotificationPublishService.REJECTED
+ : offerResult;
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final Notification<?> notification, final int timeout,
+ final TimeUnit unit) throws InterruptedException {
+ final ListenableFuture<?> offerResult =
+ domPublishService.offerNotification(toDomNotification(notification), timeout, unit);
+ return DOMNotificationPublishService.REJECTED.equals(offerResult) ? NotificationPublishService.REJECTED
+ : offerResult;
+ }
+
+ private DOMNotification toDomNotification(final Notification<?> notification) {
+ return LazySerializedDOMNotification.create(codecRegistry, notification);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+
+ private static class Builder extends BindingDOMAdapterBuilder<NotificationPublishService> {
+
+ @Override
+ public Set<Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMNotificationPublishService.class);
+ }
+
+ @Override
+ protected NotificationPublishService createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMNotificationPublishService domPublish = delegates.getInstance(DOMNotificationPublishService.class);
+ return new BindingDOMNotificationPublishServiceAdapter(codec, domPublish);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.adapter.impl.notification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.mdsal.binding.javav2.api.NotificationService;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.serializer.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.NotificationListener;
+import org.opendaylight.mdsal.dom.api.DOMNotificationService;
+import org.opendaylight.mdsal.dom.api.DOMService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Notification service adapter.
+ */
+@Beta
+public class BindingDOMNotificationServiceAdapter implements NotificationService, AutoCloseable {
+
+ public static final Factory<NotificationService> BUILDER_FACTORY = Builder::new;
+
+ private final BindingNormalizedNodeSerializer codec;
+ private final DOMNotificationService domNotifService;
+
+ public BindingDOMNotificationServiceAdapter(final BindingNormalizedNodeSerializer codec,
+ final DOMNotificationService domNotifService) {
+ this.codec = codec;
+ this.domNotifService = domNotifService;
+ }
+
+ @Override
+ public <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener) {
+ final BindingDOMNotificationListenerAdapter domListener =
+ new BindingDOMNotificationListenerAdapter(codec, listener);
+ final ListenerRegistration<BindingDOMNotificationListenerAdapter> domRegistration =
+ domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications());
+ return new ListenerRegistrationImpl<>(listener, domRegistration);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+
+ private static class ListenerRegistrationImpl<T extends NotificationListener>
+ extends AbstractListenerRegistration<T> {
+ private final ListenerRegistration<?> listenerRegistration;
+
+ ListenerRegistrationImpl(final T listener, final ListenerRegistration<?> listenerRegistration) {
+ super(listener);
+ this.listenerRegistration = listenerRegistration;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ listenerRegistration.close();
+ }
+ }
+
+ private static class Builder extends BindingDOMAdapterBuilder<NotificationService> {
+
+ @Override
+ protected NotificationService createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMNotificationService domNotification = delegates.getInstance(DOMNotificationService.class);
+ return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), domNotification);
+ }
+
+ @Override
+ public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMNotificationService.class);
+ }
+ }
+
+ /**
+ * Get DOM notification service.
+ *
+ * @return DOM notification service
+ */
+ public DOMNotificationService getDomService() {
+ return domNotifService;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.adapter.impl.notification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.NotificationListener;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Provides single method invocation of notification callbacks on supplied instance.
+ *
+ * <p>
+ * Notification Listener invoker provides common invocation interface for any subtype of
+ * {@link NotificationListener}. via {@link #invokeNotification(NotificationListener, QName, Instantiable)}
+ * method.
+ */
+@Beta
+public final class NotificationListenerInvoker {
+
+ private static final Lookup LOOKUP = MethodHandles.publicLookup();
+
+ private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
+ CacheBuilder.newBuilder().weakKeys()
+ .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
+
+ private NotificationListenerInvoker
+ createInvoker(final Class<? extends NotificationListener> key) {
+ return new NotificationListenerInvoker(createInvokerMap(key));
+ }
+
+ private Map<QName, MethodHandle>
+ createInvokerMap(final Class<? extends NotificationListener> key) {
+ final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
+ for (final Method method : key.getMethods()) {
+ if (BindingReflections.isNotificationCallback(method)) {
+
+ final Class<?> notification = method.getParameterTypes()[0];
+ final QName name = BindingReflections.findQName(notification);
+ MethodHandle handle;
+ try {
+ handle = LOOKUP.unreflect(method).asType(MethodType.methodType(void.class,
+ NotificationListener.class, Instantiable.class));
+ ret.put(name, handle);
+ } catch (final IllegalAccessException e) {
+ throw new IllegalStateException("Can not access public method.", e);
+ }
+ }
+
+ }
+ return ret.build();
+ }
+
+ @Override
+ public NotificationListenerInvoker load(final Class<? extends NotificationListener> key)
+ throws Exception {
+ return createInvoker(key);
+ }
+
+ });
+
+ private final Map<QName, MethodHandle> methodInvokers;
+
+ public NotificationListenerInvoker(final Map<QName, MethodHandle> map) {
+ this.methodInvokers = map;
+ }
+
+ /**
+ * Creates Notification service invoker for specified type.
+ *
+ * @param type
+ * - NotificationListener interface, which was generated from model.
+ * @return Cached instance of {@link NotificationListenerInvoker} for supplied notification type.
+ *
+ */
+ public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
+ Preconditions.checkArgument(type.isInterface());
+ Preconditions.checkArgument(BindingReflections.isBindingClass(type));
+ return INVOKERS.getUnchecked(type);
+ }
+
+ /**
+ * Invokes supplied Notification on provided implementation of NotificationListener.
+ *
+ * @param impl
+ * - implementation on which notification callback should be invoked.
+ * @param notificationName
+ * - name of notification to be invoked.
+ * @param input
+ * - input data for notification.
+ *
+ */
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ public void invokeNotification(@Nonnull final NotificationListener impl, @Nonnull final QName notificationName,
+ @Nullable final Instantiable<?> input) {
+ Preconditions.checkNotNull(impl, "implementation must be supplied");
+ final MethodHandle invoker = methodInvokers.get(notificationName);
+ Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
+ try {
+ invoker.invokeExact(impl, input);
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
+
import javax.annotation.Nonnull;
import org.opendaylight.mdsal.binding.javav2.api.BindingService;
import org.opendaylight.mdsal.binding.javav2.api.DataBroker;
+import org.opendaylight.mdsal.binding.javav2.api.NotificationPublishService;
+import org.opendaylight.mdsal.binding.javav2.api.NotificationService;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.data.BindingDOMDataBrokerAdapter;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.notification.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.notification.BindingDOMNotificationServiceAdapter;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.AdapterBuilder;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
private static final Map<Class<?>, BindingDOMAdapterBuilder.Factory<?>> FACTORIES =
ImmutableMap.<Class<?>, BindingDOMAdapterBuilder.Factory<?>> builder()
.put(DataBroker.class, BindingDOMDataBrokerAdapter.BUILDER_FACTORY)
+ .put(NotificationPublishService.class, BindingDOMNotificationPublishServiceAdapter.BUILDER_FACTORY)
+ .put(NotificationService.class, BindingDOMNotificationServiceAdapter.BUILDER_FACTORY)
.build();
private final BindingToNormalizedNodeCodec codec;