import java.util.Set;
import java.util.concurrent.Executor;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
/**
- * Notification broker which allows clients to subscribe for and publish YANG-modeled notifications.
+ * Notification broker which allows clients to subscribe for and publish YANG 1.0 notifications, which is to say
+ * {@code notification} statements occurring directly within a {@code module} or a {@code submodule} statement.
*/
public interface NotificationService extends BindingService {
- /**
- * Registers a listener which implements a YANG-generated notification interface derived from
- * {@link NotificationListener}. The listener is registered for all notifications present in the implemented
- * interface.
- *
- * <p>
- * Each YANG module which defines notifications results in a generated interface <code>{ModuleName}Listener</code>
- * which handles all the notifications defined in the YANG model. Each notification type translates to a specific
- * method of the form <code>on{NotificationType}</code> on the generated interface. The generated interface also
- * extends the {@link org.opendaylight.yangtools.yang.binding.NotificationListener} interface and implementations
- * are registered using this method.
- *
- * <b>Dispatch Listener Example</b>
- *
- * <p>
- * Lets assume we have following YANG model:
- *
- * <pre>
- * module example {
- * ...
- *
- * notification start {
- * ...
- * }
- *
- * notification stop {
- * ...
- * }
- * }
- * </pre>
- *
- * <p>
- * The generated interface will be:
- *
- * <pre>
- * public interface ExampleListener extends NotificationListener {
- * void onStart(Start notification);
- *
- * void onStop(Stop notification);
- * }
- * </pre>
- *
- * <p>
- * The following defines an implementation of the generated interface:
- *
- * <pre>
- * public class MyExampleListener implements ExampleListener {
- * public void onStart(Start notification) {
- * // do something
- * }
- *
- * public void onStop(Stop notification) {
- * // do something
- * }
- * }
- * </pre>
- *
- * <p>
- * The implementation is registered as follows:
- *
- * <pre>
- * MyExampleListener listener = new MyExampleListener();
- * ListenerRegistration<NotificationListener> reg = service.registerNotificationListener(listener);
- * </pre>
- *
- * <p>
- * The {@code onStart} method will be invoked when someone publishes a {@code Start} notification and the
- * {@code onStop} method will be invoked when someone publishes a {@code Stop} notification.
- *
- * @param <T> NotificationListener type
- * @param listener the listener implementation that will receive notifications.
- * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
- * by invoking the {@link ListenerRegistration#close()} method when no longer needed.
- * @deprecated Prefer {@link #registerListener(Class, Listener)} or
- * {@link #registerListener(Class, Listener, Executor)} instead.
- */
- @Deprecated(since = "10.0.0", forRemoval = true)
- <T extends NotificationListener> @NonNull ListenerRegistration<T> registerNotificationListener(@NonNull T listener);
-
/**
* Registers a {@link Listener} to receive callbacks for {@link Notification}s of a particular type.
*
+++ /dev/null
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
- * Copyright (c) 2021 PANTHEON.tech, s.r.o.
- *
- * 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 static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
-
-import java.util.Set;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.dom.api.DOMEvent;
-import org.opendaylight.mdsal.dom.api.DOMNotification;
-import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-
-abstract class AbstractDOMNotificationListenerAdapter implements DOMNotificationListener {
- private final AdapterContext adapterContext;
-
- AbstractDOMNotificationListenerAdapter(final AdapterContext adapterContext) {
- this.adapterContext = requireNonNull(adapterContext);
- }
-
- @Override
- public final void onNotification(final DOMNotification notification) {
- onNotification(notification.getType(), verifyNotNull(deserialize(notification)));
- }
-
- abstract void onNotification(@NonNull Absolute domType, @NonNull Notification<?> notification);
-
- abstract Set<Absolute> getSupportedNotifications();
-
- private Notification<?> deserialize(final DOMNotification notification) {
- 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 ((LazySerializedNotification) notification).getBindingData();
- }
-
- 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;
- }
-}
/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2021 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,
*/
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;
-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 java.util.concurrent.Executor;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.adapter.invoke.NotificationListenerInvoker;
+import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener.Component;
+import org.opendaylight.mdsal.binding.api.NotificationService.Listener;
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.mdsal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-final class BindingDOMNotificationListenerAdapter extends AbstractDOMNotificationListenerAdapter {
- private final ImmutableMap<Absolute, NotificationListenerInvoker> invokers;
- private final @NonNull NotificationListener delegate;
+final class BindingDOMNotificationListenerAdapter<N extends Notification<N> & DataObject>
+ implements DOMNotificationListener {
+ private final AdapterContext adapterContext;
+ private final Listener<N> delegate;
+ private final Executor executor;
+ private final Class<N> type;
- BindingDOMNotificationListenerAdapter(final AdapterContext adapterContext, final NotificationListener delegate) {
- super(adapterContext);
+ BindingDOMNotificationListenerAdapter(final AdapterContext adapterContext, final Class<N> type,
+ final Listener<N> delegate, final Executor executor) {
+ this.adapterContext = requireNonNull(adapterContext);
+ this.type = requireNonNull(type);
this.delegate = requireNonNull(delegate);
- invokers = createInvokerMapFor(delegate.getClass());
+ this.executor = requireNonNull(executor);
}
- @Override
- void onNotification(final Absolute domType, final Notification<?> notification) {
- invokers.get(domType).invokeNotification(delegate, domType.lastNodeIdentifier(), notification);
+ BindingDOMNotificationListenerAdapter(final AdapterContext adapterContext, final Component<N> component,
+ final Executor executor) {
+ this(adapterContext, component.type(), component.listener(), executor);
}
- @Override
- Set<Absolute> getSupportedNotifications() {
- return invokers.keySet();
+ @NonNull Absolute schemaPath() {
+ return Absolute.of(BindingReflections.findQName(type));
}
- private static ImmutableMap<Absolute, NotificationListenerInvoker> createInvokerMapFor(
- final Class<? extends NotificationListener> implClz) {
- final Map<Absolute, NotificationListenerInvoker> builder = new HashMap<>();
- for (final TypeToken<?> ifaceToken : TypeToken.of(implClz).getTypes().interfaces()) {
- 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 Absolute path : getNotificationTypes(listenerType)) {
- builder.put(path, invoker);
- }
- }
- }
- return ImmutableMap.copyOf(builder);
+ @Override
+ public void onNotification(final DOMNotification notification) {
+ final var binding = type.cast(verifyNotNull(deserialize(notification)));
+ executor.execute(() -> delegate.onNotification(binding));
}
- private static Set<Absolute> getNotificationTypes(final Class<? extends NotificationListener> type) {
- // TODO: Investigate possibility and performance impact if we cache this or expose
- // it from NotificationListenerInvoker
- final Set<Absolute> ret = new HashSet<>();
- for (final Method method : type.getMethods()) {
- if (NotificationListenerInvoker.isNotificationCallback(method)) {
- final Class<?> notification = method.getParameterTypes()[0];
- ret.add(Absolute.of(BindingReflections.findQName(notification)));
- }
+ private Notification<?> deserialize(final DOMNotification notification) {
+ if (notification instanceof LazySerializedNotification lazy) {
+ // 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 lazy.getBindingData();
}
- return ret;
+
+ final var serializer = adapterContext.currentSerializer();
+ final var result = notification instanceof DOMEvent domEvent
+ ? serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody(),
+ domEvent.getEventInstant())
+ : serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+ verify(result instanceof Notification, "Unexpected codec result %s", result);
+ return (Notification<?>) result;
}
}
import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
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;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
@VisibleForTesting
this.domNotifService = domNotifService;
}
- @Override
- @Deprecated(since = "10.0.0", forRemoval = true)
- public <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener) {
- final var domListener = new BindingDOMNotificationListenerAdapter(adapterContext, listener);
- return new ListenerRegistrationImpl<>(listener,
- domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications()));
- }
-
@Override
public <N extends Notification<N> & DataObject> Registration registerListener(final Class<N> type,
final Listener<N> listener, final Executor executor) {
- final var domListener = new SingleBindingDOMNotificationAdapter<>(adapterContext, type, listener, executor);
- return domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications());
+ final var domListener = new BindingDOMNotificationListenerAdapter<>(adapterContext, type, listener, executor);
+ return domNotifService.registerNotificationListener(domListener, Set.of(domListener.schemaPath()));
}
@Override
final var exec = requireNonNull(executor);
final var listeners = new HashMap<Absolute, DOMNotificationListener>();
for (var constituent : listener.constituents()) {
- final var domListener = new SingleBindingDOMNotificationAdapter<>(adapterContext, constituent, exec);
- listeners.put(domListener.getSupportedNotifications().iterator().next(), domListener);
+ final var domListener = new BindingDOMNotificationListenerAdapter<>(adapterContext, constituent, exec);
+ listeners.put(domListener.schemaPath(), domListener);
}
return domNotifService.registerNotificationListeners(listeners);
}
- @Deprecated(since = "10.0.0", forRemoval = true)
- private static final 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> {
Builder(final AdapterContext adapterContext) {
super(adapterContext);
@Override
protected NotificationService createInstance(final ClassToInstanceMap<DOMService> delegates) {
- final DOMNotificationService domNotification = delegates.getInstance(DOMNotificationService.class);
- return new BindingDOMNotificationServiceAdapter(adapterContext(), domNotification);
+ return new BindingDOMNotificationServiceAdapter(adapterContext(),
+ delegates.getInstance(DOMNotificationService.class));
}
@Override
+++ /dev/null
-/*
- * Copyright (c) 2021 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 java.util.Set;
-import java.util.concurrent.Executor;
-import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener.Component;
-import org.opendaylight.mdsal.binding.api.NotificationService.Listener;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-
-final class SingleBindingDOMNotificationAdapter<N extends Notification<N> & DataObject>
- extends AbstractDOMNotificationListenerAdapter {
- private final Listener<N> delegate;
- private final Executor executor;
- private final Class<N> type;
-
- SingleBindingDOMNotificationAdapter(final AdapterContext adapterContext, final Class<N> type,
- final Listener<N> delegate, final Executor executor) {
- super(adapterContext);
- this.type = requireNonNull(type);
- this.delegate = requireNonNull(delegate);
- this.executor = requireNonNull(executor);
- }
-
- SingleBindingDOMNotificationAdapter(final AdapterContext adapterContext, final Component<N> component,
- final Executor executor) {
- this(adapterContext, component.type(), component.listener(), executor);
- }
-
- @Override
- void onNotification(final Absolute domType, final Notification<?> notification) {
- executor.execute(() -> delegate.onNotification(type.cast(notification)));
- }
-
- @Override
- Set<Absolute> getSupportedNotifications() {
- return Set.of(Absolute.of(BindingReflections.findQName(type)));
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.invoke;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-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.MethodType;
-import java.lang.reflect.Method;
-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.DataContainer;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-import org.opendaylight.yangtools.yang.common.QName;
-
-/**
- * Provides single method invocation of notificatoin callbacks on supplied instance.
- *
- * <p>
- * Notification Listener invoker provides common invocation interface for any subtype of {@link NotificationListener}.
- * via {@link #invokeNotification(NotificationListener, QName, DataContainer)} method.
- */
-public final class NotificationListenerInvoker {
- private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
- CacheBuilder.newBuilder().weakKeys()
- .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
- @Override
- public NotificationListenerInvoker load(final Class<? extends NotificationListener> key) {
- return new NotificationListenerInvoker(createInvokerMap(key));
- }
-
- private ImmutableMap<QName, MethodHandle> createInvokerMap(
- final Class<? extends NotificationListener> key) {
- final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
- for (final Method method : key.getMethods()) {
- if (isNotificationCallback(method)) {
- final Class<?> notification = method.getParameterTypes()[0];
- final QName name = BindingReflections.findQName(notification);
- MethodHandle handle;
- try {
- handle = MethodHandles.publicLookup().unreflect(method).asType(
- MethodType.methodType(void.class, NotificationListener.class, DataContainer.class));
- ret.put(name, handle);
- } catch (final IllegalAccessException e) {
- throw new IllegalStateException("Can not access public method.", e);
- }
- }
-
- }
- return ret.build();
- }
- });
-
- private final ImmutableMap<QName, MethodHandle> methodInvokers;
-
- NotificationListenerInvoker(final ImmutableMap<QName, MethodHandle> map) {
- methodInvokers = map;
- }
-
- /**
- * Creates RPCServiceInvoker for specified RpcService type.
- *
- * @param type
- * RpcService interface, which was generated from model.
- * @return Cached instance of {@link NotificationListenerInvoker} for
- * supplied RPC type.
- */
- public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
- checkArgument(type.isInterface());
- checkArgument(BindingReflections.isBindingClass(type));
- return INVOKERS.getUnchecked(type);
- }
-
- /**
- * Invokes supplied RPC on provided implementation of RPC Service.
- *
- * @param impl Implementation on which notification callback should be invoked.
- * @param rpcName Name of RPC to be invoked.
- * @param input Input data for RPC.
- */
- @SuppressWarnings("checkstyle:illegalCatch")
- public void invokeNotification(final @NonNull NotificationListener impl, final @NonNull QName rpcName,
- final @Nullable DataContainer input) {
- requireNonNull(impl, "implemetation must be supplied");
- final MethodHandle invoker = methodInvokers.get(rpcName);
- checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
- try {
- invoker.invokeExact(impl, input);
- } catch (final Throwable e) {
- Throwables.throwIfUnchecked(e);
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Checks if supplied method is callback for notifications.
- *
- * @param method method to check
- * @return true if method is notification callback.
- */
- public static boolean isNotificationCallback(final Method method) {
- if (method.getName().startsWith("on") && method.getParameterCount() == 1) {
- Class<?> potentialNotification = method.getParameterTypes()[0];
- if (Notification.class.isAssignableFrom(potentialNotification)
- && method.getName().equals("on" + potentialNotification.getSimpleName())) {
- return true;
- }
- }
- return false;
- }
-}
import java.util.Map;
import java.util.concurrent.Executor;
import org.opendaylight.mdsal.binding.api.NotificationService;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
super(NotificationService.class);
}
- @Override
- @Deprecated(since = "10.0.0", forRemoval = true)
- public <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener) {
- return delegate().registerNotificationListener(listener);
- }
-
@Override
public <N extends Notification<N> & DataObject> Registration registerListener(final Class<N> type,
final NotificationService.Listener<N> listener, final Executor executor) {
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNull;
import org.junit.Test;
import org.opendaylight.mdsal.binding.api.NotificationPublishService;
import org.opendaylight.mdsal.binding.api.NotificationService.Listener;
import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractNotificationBrokerTest;
-import org.opendaylight.mdsal.binding.runtime.spi.BindingRuntimeHelpers;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.OpendaylightMdsalBindingTestListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.$YangModuleInfoImpl;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TwoLevelListChanged;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TwoLevelListChangedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
public class ForwardedNotificationAdapterTest extends AbstractNotificationBrokerTest {
@Override
protected Set<YangModuleInfo> getModuleInfos() throws Exception {
- return Set.of(BindingRuntimeHelpers.getYangModuleInfo(TwoLevelListChanged.class));
+ return Set.of($YangModuleInfoImpl.getInstance());
}
@Test
- public void testPutSubscription() throws InterruptedException {
- final var listener = new TestNotifListener(1);
- try (var reg = getNotificationService().registerNotificationListener(listener)) {
- assertPutSubscription(listener);
- }
- }
-
- @Test
- public void testPutSubscriptionSimple() throws InterruptedException {
- final var listener = new SimpleNotifListener(1);
+ public void testPutSubscription() throws Exception {
+ final var listener = new NotifListener(1);
try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) {
- assertPutSubscription(listener);
- }
- }
-
- private void assertPutSubscription(final AbstractNotifListener listener) throws InterruptedException {
- final var testData = createTestData();
- getNotificationPublishService().putNotification(testData);
-
- final var received = listener.awaitNotifications();
- assertEquals(1, received.size());
- assertSame(testData, received.get(0));
- }
+ final var testData = createTestData();
+ getNotificationPublishService().putNotification(testData);
- @Test
- public void testOfferSubscription() throws InterruptedException {
- final var listener = new TestNotifListener(1);
- try (var reg = getNotificationService().registerNotificationListener(listener)) {
- assertOfferNotification(listener);
+ final var received = listener.awaitNotifications();
+ assertEquals(1, received.size());
+ assertSame(testData, received.get(0));
}
}
@Test
- public void testOfferSubscriptionSimple() throws InterruptedException {
- final var listener = new SimpleNotifListener(1);
+ public void testOfferSubscription() throws Exception {
+ final var listener = new NotifListener(1);
try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) {
- assertOfferNotification(listener);
- }
- }
+ final var testData = createTestData();
- private void assertOfferNotification(final AbstractNotifListener listener) throws InterruptedException {
- final var testData = createTestData();
-
- try {
getNotificationPublishService().offerNotification(testData).get(1, TimeUnit.SECONDS);
- } catch (ExecutionException | TimeoutException e) {
- throw new AssertionError("Notification should be delivered", e);
- }
-
- final var received = listener.awaitNotifications();
- assertEquals(1, received.size());
- assertSame(testData, received.get(0));
- }
- @Test
- public void testOfferTimedNotification() throws InterruptedException {
- final var listener = new TestNotifListener(1);
- try (var reg = getNotificationService().registerNotificationListener(listener)) {
- assertOfferTimedNotification(listener);
+ final var received = listener.awaitNotifications();
+ assertEquals(1, received.size());
+ assertSame(testData, received.get(0));
}
}
@Test
- public void testOfferTimedNotificationSimple() throws InterruptedException {
- final var listener = new SimpleNotifListener(1);
+ public void testOfferTimedNotification() throws Exception {
+ final var listener = new NotifListener(1);
try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) {
- assertOfferTimedNotification(listener);
- }
- }
-
- private void assertOfferTimedNotification(final AbstractNotifListener listener) throws InterruptedException {
- final var testData = createTestData();
+ final var testData = createTestData();
- assertNotSame(NotificationPublishService.REJECTED,
- getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS));
+ assertNotSame(NotificationPublishService.REJECTED,
+ getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS));
- final var received = listener.awaitNotifications();
- assertEquals(1, received.size());
- assertSame(testData, received.get(0));
+ final var received = listener.awaitNotifications();
+ assertEquals(1, received.size());
+ assertSame(testData, received.get(0));
+ }
}
-
private static @NonNull TwoLevelListChanged createTestData() {
return new TwoLevelListChangedBuilder()
.setTopLevelList(BindingMap.of(new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build()))
.build();
}
- private abstract static class AbstractNotifListener {
+ private static final class NotifListener implements Listener<TwoLevelListChanged> {
private final List<TwoLevelListChanged> receivedNotifications = new ArrayList<>();
private final CountDownLatch latch;
- AbstractNotifListener(final int expectedCount) {
+ NotifListener(final int expectedCount) {
latch = new CountDownLatch(expectedCount);
}
- final void receiveNotification(final TwoLevelListChanged notification) {
+ void receiveNotification(final TwoLevelListChanged notification) {
receivedNotifications.add(notification);
latch.countDown();
}
- final List<TwoLevelListChanged> awaitNotifications() throws InterruptedException {
+ List<TwoLevelListChanged> awaitNotifications() throws InterruptedException {
latch.await();
return receivedNotifications;
}
- }
-
- private static class SimpleNotifListener extends AbstractNotifListener implements Listener<TwoLevelListChanged> {
- SimpleNotifListener(final int expectedCount) {
- super(expectedCount);
- }
@Override
public void onNotification(final TwoLevelListChanged notification) {
receiveNotification(notification);
}
}
-
- private static class TestNotifListener extends AbstractNotifListener
- implements OpendaylightMdsalBindingTestListener {
- TestNotifListener(final int expectedCount) {
- super(expectedCount);
- }
-
- @Override
- public void onTwoLevelListChanged(final TwoLevelListChanged notification) {
- receiveNotification(notification);
- }
- }
}
+++ /dev/null
-/*
- * Copyright (c) 2016 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.invoke;
-
-import static org.hamcrest.CoreMatchers.endsWith;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.mock;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.UncheckedExecutionException;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.WrongMethodTypeException;
-import org.junit.Test;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.notification.rev150205.OpendaylightTestNotificationListener;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-import org.opendaylight.yangtools.yang.common.QName;
-
-public class NotificationListenerInvokerTest {
- @Test
- public void fromTest() throws Exception {
- assertNotNull(NotificationListenerInvoker.from(OpendaylightTestNotificationListener.class));
- }
-
- @Test
- public void fromWithExceptionTest() {
- final var cause = assertThrows(UncheckedExecutionException.class,
- () -> NotificationListenerInvoker.from(TestPrivateInterface.class))
- .getCause();
- assertThat(cause, instanceOf(IllegalStateException.class));
- assertThat(cause.getCause(), instanceOf(IllegalAccessException.class));
- }
-
- @Test
- public void invokeNotification() throws Exception {
- final var notificationListener = mock(NotificationListener.class);
- final var methodHandle = MethodHandles.publicLookup().unreflect(String.class.getDeclaredMethod("toString"));
-
- final var notificationListenerInvoker = new NotificationListenerInvoker(
- ImmutableMap.of(QName.create("test", "test"), methodHandle));
-
- final var ex = assertThrows(WrongMethodTypeException.class,
- () -> notificationListenerInvoker.invokeNotification(notificationListener, QName.create("test", "test"),
- null));
- assertThat(ex.getMessage(), endsWith(" (String)String but found (NotificationListener,DataContainer)void"));
- }
-
- private interface TestPrivateInterface extends NotificationListener, Augmentation {
- QName QNAME = QName.create("test", "test");
-
- void onTestNotificationInterface(TestNotificationInterface notif);
- }
-
- public interface TestNotificationInterface extends DataObject, Notification<TestNotificationInterface> {
- QName QNAME = QName.create("test", "test");
- }
-}
\ No newline at end of file
// substatements to establish this order.
tmpAug.sort(AbstractAugmentGenerator.COMPARATOR);
tmp.addAll(tmpAug);
-
- // Compatibility FooService and FooListener interfaces, only generated for modules.
- if (this instanceof ModuleGenerator moduleGen) {
- final var notifs = tmp.stream()
- .filter(NotificationGenerator.class::isInstance)
- .map(NotificationGenerator.class::cast)
- .collect(Collectors.toUnmodifiableList());
- if (!notifs.isEmpty()) {
- tmp.add(new NotificationServiceGenerator(moduleGen, notifs));
- }
- }
-
return List.copyOf(tmp);
}
/**
* An explicit {@link Generator}, associated with a particular {@link EffectiveStatement}.
*/
+// FIXME: unify this with Generator
public abstract class AbstractExplicitGenerator<S extends EffectiveStatement<?, ?>, R extends RuntimeType>
extends Generator implements CopyableNode, StatementRepresentation<S> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractExplicitGenerator.class);
+++ /dev/null
-/*
- * Copyright (c) 2021 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.generator.impl.reactor;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
-
-/**
- * An implicit {@link Generator}, not associated with any particular statement. We use these only for aggregate services
- * harking back to the original specification for Java 7. As such we want to eventually get rid of it.
- */
-// FIXME: eventually remove this subclass and unify Generator and AbstractExplicitGenerator
-abstract class AbstractImplicitGenerator extends Generator {
- AbstractImplicitGenerator(final ModuleGenerator parent) {
- super(parent);
- }
-
- @Override
- final void pushToInference(final SchemaInferenceStack inferenceStack) {
- // No-op
- }
-
- @Override
- final ClassPlacement classPlacement() {
- return ClassPlacement.TOP_LEVEL;
- }
-
- @Override
- final Member createMember(final CollisionDomain domain) {
- return domain.addSecondary(this, ((ModuleGenerator) getParent()).getPrefixMember(), classSuffix());
- }
-
- abstract @NonNull String classSuffix();
-}
+++ /dev/null
-/*
- * Copyright (c) 2021 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.generator.impl.reactor;
-
-import static com.google.common.base.Verify.verify;
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-import org.opendaylight.mdsal.binding.model.api.AccessModifier;
-import org.opendaylight.mdsal.binding.model.api.GeneratedType;
-import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
-import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
-import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
-import org.opendaylight.mdsal.binding.model.ri.Types;
-import org.opendaylight.yangtools.yang.binding.contract.Naming;
-import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
-import org.opendaylight.yangtools.yang.model.api.Status;
-import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
-
-/**
- * Aggregate service for top-level {@code notification} statements for a particular module. It does not handle nested
- * (YANG 1.1) notifications.
- */
-// FIXME: MDSAL-497: remove this generator
-final class NotificationServiceGenerator extends AbstractImplicitGenerator {
- private final List<NotificationGenerator> notifs;
-
- NotificationServiceGenerator(final ModuleGenerator parent, final List<NotificationGenerator> notifs) {
- super(parent);
- this.notifs = requireNonNull(notifs);
- }
-
- @Override
- StatementNamespace namespace() {
- return StatementNamespace.NOTIFICATION_LISTENER;
- }
-
- @Override
- String classSuffix() {
- return Naming.NOTIFICATION_LISTENER_SUFFIX;
- }
-
- @Override
- GeneratedType createTypeImpl(final TypeBuilderFactory builderFactory) {
- final GeneratedTypeBuilder builder = builderFactory.newGeneratedTypeBuilder(typeName());
- builder.addImplementsType(BindingTypes.NOTIFICATION_LISTENER)
- .addAnnotation(DEPRECATED_ANNOTATION);
-
- for (NotificationGenerator gen : notifs) {
- final MethodSignatureBuilder notificationMethod = builder.addMethod("on" + gen.assignedName())
- .setAccessModifier(AccessModifier.PUBLIC)
- .addParameter(gen.getGeneratedType(builderFactory), "notification")
- .setReturnType(Types.primitiveVoidType());
-
- final NotificationEffectiveStatement stmt = gen.statement();
- verify(stmt instanceof WithStatus, "Unexpected statement %s", stmt);
- final WithStatus withStatus = (WithStatus) stmt;
-
- annotateDeprecatedIfNecessary(withStatus, notificationMethod);
- if (withStatus.getStatus() == Status.OBSOLETE) {
- notificationMethod.setDefault(true);
- }
-
- // FIXME: finish this up
- // addComment(notificationMethod, notification);
- }
-
- return builder.build();
- }
-}
import static java.util.Objects.requireNonNull;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
/**
* <a href="https://www.rfc-editor.org/rfc/rfc6020#section-6.2.1">YANG statement namespaces</a> which we process.
* </code>
* In this case the module-derived FooData gets shifted to {@code $D}.
*/
- DATA_ROOT("$D", true),
- /**
- * The namespace for combined {@link NotificationListener} interface. This typically does not conflict, but could in
- * case of
- * <code>
- * <pre>
- * module foo {
- * container foo-listener; // results in FooListener
- * notification bar; // Triggers FooListener generation for module
- * }
- * </pre>
- * </code>
- * In this case the module-derived FooListener gets shifted to {@code $LL}.
- *
- * @deprecated This will be removed once {@link NotificationServiceGenerator} is gone.
- */
- // FIXME: MDSAL-497: remove this value
- @Deprecated
- NOTIFICATION_LISTENER("$LL", true);
+ DATA_ROOT("$D", true);
private final @NonNull String suffix;
private final boolean resistant;
package org.opendaylight.mdsal.binding.generator.impl;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Test;
-import org.opendaylight.mdsal.binding.model.api.AnnotationType;
-import org.opendaylight.mdsal.binding.model.api.AnnotationType.Parameter;
-import org.opendaylight.mdsal.binding.model.api.GeneratedType;
import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
-import org.opendaylight.mdsal.binding.model.api.MethodSignature;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
public class Mdsal554Test {
@Test
public void builderTemplateGenerateListenerMethodsTest() {
- final List<GeneratedType> genTypes = DefaultBindingGenerator.generateFor(
+ final var genTypes = DefaultBindingGenerator.generateFor(
YangParserTestUtils.parseYangResource("/mdsal554.yang"));
- assertEquals(5, genTypes.size());
-
- final List<MethodSignature> methods = genTypes.get(4).getMethodDefinitions();
- assertEquals(3, methods.size());
+ assertEquals(4, genTypes.size());
// status deprecated
- final MethodSignature deprecated = methods.get(0);
- assertEquals("onDeprecatedNotification", deprecated.getName());
- assertFalse(deprecated.isDefault());
-
- final List<AnnotationType> deprecatedAnnotations = deprecated.getAnnotations();
+ final var deprecated = genTypes.get(1);
+ assertEquals("DeprecatedNotification", deprecated.getName());
+ final var deprecatedAnnotations = deprecated.getAnnotations();
assertEquals(1, deprecatedAnnotations.size());
- AnnotationType annotation = deprecatedAnnotations.get(0);
+ var annotation = deprecatedAnnotations.get(0);
assertEquals(JavaTypeName.create(Deprecated.class), annotation.getIdentifier());
assertEquals(List.of(), annotation.getParameters());
// status obsolete
- final MethodSignature obsolete = methods.get(1);
- assertEquals("onObsoleteNotification", obsolete.getName());
- assertTrue(obsolete.isDefault());
+ final var obsolete = genTypes.get(2);
+ assertEquals("ObsoleteNotification", obsolete.getName());
- final List<AnnotationType> obsoleteAnnotations = obsolete.getAnnotations();
+ final var obsoleteAnnotations = obsolete.getAnnotations();
assertEquals(1, obsoleteAnnotations.size());
annotation = obsoleteAnnotations.get(0);
assertEquals(JavaTypeName.create(Deprecated.class), annotation.getIdentifier());
- final List<Parameter> annotationParameters = annotation.getParameters();
+ final var annotationParameters = annotation.getParameters();
assertEquals(1, annotationParameters.size());
assertEquals("forRemoval", annotationParameters.get(0).getName());
assertEquals("true", annotationParameters.get(0).getValue());
// status current
- final MethodSignature current = methods.get(2);
- assertEquals("onTestNotification", current.getName());
- assertFalse(current.isDefault());
+ final var current = genTypes.get(3);
+ assertEquals("TestNotification", current.getName());
assertEquals(List.of(), current.getAnnotations());
}
}
public void testListenerConflict() {
assertGeneratedNames("listener-conflict.yang",
"org.opendaylight.yang.gen.v1.listener.conflict.norev.ListenerConflictData",
- "org.opendaylight.yang.gen.v1.listener.conflict.norev.ListenerConflictListener$CO",
- "org.opendaylight.yang.gen.v1.listener.conflict.norev.Bar",
- "org.opendaylight.yang.gen.v1.listener.conflict.norev.ListenerConflictListener");
+ "org.opendaylight.yang.gen.v1.listener.conflict.norev.ListenerConflictListener",
+ "org.opendaylight.yang.gen.v1.listener.conflict.norev.Bar");
}
@Test
"org.opendaylight.yang.gen.v1.schema.conflict.norev.FooBar$NO",
"org.opendaylight.yang.gen.v1.schema.conflict.norev.FooBar$RP",
"org.opendaylight.yang.gen.v1.schema.conflict.norev.FooBar$RPInput",
- "org.opendaylight.yang.gen.v1.schema.conflict.norev.FooBar$RPOutput",
- "org.opendaylight.yang.gen.v1.schema.conflict.norev.SchemaConflictListener");
+ "org.opendaylight.yang.gen.v1.schema.conflict.norev.FooBar$RPOutput");
}
private static void assertGeneratedNames(final String yangFile, final String... fqcns) {
// Test if all sources were generated from 'module foo'
File parent = new File(sourcesOutputDir, CompilationTestUtils.NS_FOO);
assertTrue(new File(parent, "FooData.java").exists());
- assertTrue(new File(parent, "FooListener.java").exists());
assertTrue(new File(parent, "PathAttributes.java").exists());
assertTrue(new File(parent, "Update.java").exists());
assertTrue(new File(parent, "UpdateBuilder.java").exists());
- CompilationTestUtils.assertFilesCount(parent, 8);
+ CompilationTestUtils.assertFilesCount(parent, 7);
parent = new File(sourcesOutputDir, CompilationTestUtils.NS_FOO + CompilationTestUtils.FS + "path");
CompilationTestUtils.assertFilesCount(parent, 1);
import org.opendaylight.yangtools.yang.binding.KeyedListAction;
import org.opendaylight.yangtools.yang.binding.KeyedListNotification;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.opendaylight.yangtools.yang.binding.OpaqueObject;
import org.opendaylight.yangtools.yang.binding.Rpc;
import org.opendaylight.yangtools.yang.binding.RpcInput;
public static final ConcreteType DATA_CONTAINER = typeForClass(DataContainer.class);
public static final ConcreteType DATA_OBJECT = typeForClass(DataObject.class);
public static final ConcreteType DATA_ROOT = typeForClass(DataRoot.class);
- @Deprecated(since = "10.0.0", forRemoval = true)
- public static final ConcreteType NOTIFICATION_LISTENER = typeForClass(NotificationListener.class);
public static final ConcreteType QNAME = typeForClass(QName.class);
public static final ConcreteType RPC_INPUT = typeForClass(RpcInput.class);
public static final ConcreteType RPC_OUTPUT = typeForClass(RpcOutput.class);
import org.opendaylight.yangtools.yang.binding.Key;
import org.opendaylight.yangtools.yang.binding.KeyAware;
import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
public class BindingTypesTest {
@Test
assertEquals("KEY_AWARE", typeForClass(KeyAware.class), BindingTypes.KEY_AWARE);
assertEquals("KEY", typeForClass(Key.class), BindingTypes.KEY);
assertEquals("INSTANCE_IDENTIFIER", typeForClass(InstanceIdentifier.class), BindingTypes.INSTANCE_IDENTIFIER);
- assertEquals("NOTIFICATION_LISTENER", typeForClass(NotificationListener.class),
- BindingTypes.NOTIFICATION_LISTENER);
}
@Test
+++ /dev/null
-/*
- * Copyright (c) 2013 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.yangtools.yang.binding;
-
-import java.util.EventListener;
-
-/**
- * Marker interface for generated notification listener interfaces. This interface exists solely as support for
- * generated code. Users should never implement this interface directly, but rather implement one of the sub-interfaces
- * generated from a YANG model.
- *
- * <p>
- * The subclasses of this interface have callbacks for events, which are derived from {@link Notification} class in form
- * <pre>
- * void on{NotificationType}(NotificationType notification)
- * </pre>
- *
- * <p>
- * E.g. if we have notification SessionUp the callback will have signature:
- * {@code void onSessionUp(SessionUp notification)}
- */
-@Deprecated(since = "10.0.0", forRemoval = true)
-public interface NotificationListener extends EventListener {
-
-}