From 7ecf19a3c98c17b9f2d82b32fa9932f594e5a7ac Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 19 Oct 2021 14:08:56 +0200 Subject: [PATCH] Add NotificationService.registerListener() We want to get rid of FooListener-based notifications, so that we can use simple lambdas and proper composition. This takes the first step towards that by allowing users to register a simple @FunctionalInterface-based listener. JIRA: MDSAL-700 Change-Id: Ica1ac23515c813366d5d66dc91c3183695f84ce7 Signed-off-by: Robert Varga --- .../binding/api/NotificationService.java | 55 ++++++- ...bstractDOMNotificationListenerAdapter.java | 51 ++++++ ...BindingDOMNotificationListenerAdapter.java | 40 +---- .../BindingDOMNotificationServiceAdapter.java | 18 ++- .../SingleBindingDOMNotificationAdapter.java | 41 +++++ .../adapter/osgi/OSGiNotificationService.java | 10 ++ .../ForwardedNotificationAdapterTest.java | 149 ++++++++++++------ 7 files changed, 275 insertions(+), 89 deletions(-) create mode 100644 binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java create mode 100644 binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java index 8d49fb5e5d..6ff74ca341 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/NotificationService.java @@ -7,8 +7,13 @@ */ package org.opendaylight.mdsal.binding.api; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.EventListener; +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.Notification; import org.opendaylight.yangtools.yang.binding.NotificationListener; /** @@ -88,10 +93,58 @@ public interface NotificationService extends BindingService { * {@link NotificationListener}. The listener is registered for all notifications present in * the implemented interface. * - *

+ * @param 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. */ @NonNull ListenerRegistration registerNotificationListener(@NonNull T listener); + + /** + * Registers a {@link Listener} to receive callbacks for {@link Notification}s of a particular type. + * + * @param Notification type + * @param type Notification type class + * @param listener The listener implementation that will receive notifications + * @param executor Executor to use for invoking the listener's methods + * @return a {@link Registration} instance that should be used to unregister the listener by invoking the + * {@link Registration#close()} method when no longer needed + */ + @NonNull Registration registerListener(Class type, Listener listener, + Executor executor); + + /** + * Registers a {@link Listener} to receive callbacks for {@link Notification}s of a particular type. + * + * @implSpec + * This method is equivalent to {@code registerListener(type, listener, MoreExecutors.directExecutor())}, i.e. + * the listener will be invoked on some implementation-specific thread. + * + * @param Notification type + * @param type Notification type class + * @param listener The listener implementation that will receive notifications + * @return a {@link Registration} instance that should be used to unregister the listener by invoking the + * {@link Registration#close()} method when no longer needed + */ + default @NonNull Registration registerListener(final Class type, + final Listener listener) { + return registerListener(type, listener, MoreExecutors.directExecutor()); + } + + /** + * Interface for listeners on global (YANG 1.0) notifications. Such notifications are identified by their generated + * interface which extends {@link Notification}. Each listener instance can listen to only a single notification + * type. + * + * @param N Notification type + */ + @FunctionalInterface + interface Listener extends EventListener { + /** + * Process a global notification. + * + * @param notification Notification body + */ + void onNotification(@NonNull N notification); + } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java new file mode 100644 index 0000000000..f85f2ab777 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDOMNotificationListenerAdapter.java @@ -0,0 +1,51 @@ +/* + * 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.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 getSupportedNotifications(); + + private Notification deserialize(final DOMNotification notification) { + if (notification instanceof LazySerializedDOMNotification) { + // 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(); + } + + final CurrentAdapterSerializer serializer = adapterContext.currentSerializer(); + return notification instanceof DOMEvent + ? serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody(), + ((DOMEvent) notification).getEventInstant()) + : serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody()); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationListenerAdapter.java index 2785500fa2..cf09f350ce 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationListenerAdapter.java @@ -18,51 +18,27 @@ import java.util.Map; import java.util.Set; import org.opendaylight.mdsal.binding.dom.adapter.invoke.NotificationListenerInvoker; 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.Notification; import org.opendaylight.yangtools.yang.binding.NotificationListener; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; -class BindingDOMNotificationListenerAdapter implements DOMNotificationListener { - - private final AdapterContext adapterContext; - private final NotificationListener delegate; +final class BindingDOMNotificationListenerAdapter extends AbstractDOMNotificationListenerAdapter { private final ImmutableMap invokers; + private final NotificationListener delegate; BindingDOMNotificationListenerAdapter(final AdapterContext adapterContext, final NotificationListener delegate) { - this.adapterContext = requireNonNull(adapterContext); + super(adapterContext); this.delegate = requireNonNull(delegate); - this.invokers = createInvokerMapFor(delegate.getClass()); + invokers = createInvokerMapFor(delegate.getClass()); } @Override - public void onNotification(final DOMNotification notification) { - final Notification baNotification = deserialize(notification); - final QName notificationQName = notification.getType().lastNodeIdentifier(); - getInvoker(notification.getType()).invokeNotification(delegate, notificationQName, baNotification); - } - - private Notification deserialize(final DOMNotification notification) { - if (notification instanceof LazySerializedDOMNotification) { - // 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(); - } - - final CurrentAdapterSerializer serializer = adapterContext.currentSerializer(); - return notification instanceof DOMEvent ? serializer.fromNormalizedNodeNotification(notification.getType(), - notification.getBody(), ((DOMEvent) notification).getEventInstant()) - : serializer.fromNormalizedNodeNotification(notification.getType(), notification.getBody()); + void onNotification(final Absolute domType, final Notification notification) { + invokers.get(domType).invokeNotification(delegate, domType.lastNodeIdentifier(), notification); } - private NotificationListenerInvoker getInvoker(final Absolute type) { - return invokers.get(type); - } - - protected Set getSupportedNotifications() { + @Override + Set getSupportedNotifications() { return invokers.keySet(); } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java index 7f935e6aa1..ce26cbca98 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMNotificationServiceAdapter.java @@ -13,12 +13,15 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableSet; import java.util.Set; +import java.util.concurrent.Executor; import org.opendaylight.mdsal.binding.api.NotificationService; import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Factory; 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.Notification; import org.opendaylight.yangtools.yang.binding.NotificationListener; @VisibleForTesting @@ -43,6 +46,17 @@ public class BindingDOMNotificationServiceAdapter implements NotificationService return new ListenerRegistrationImpl<>(listener, domRegistration); } + @Override + public Registration registerListener(final Class type, final Listener listener, + final Executor executor) { + final var domListener = new SingleBindingDOMNotificationAdapter<>(adapterContext, type, listener, executor); + return domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications()); + } + + public DOMNotificationService getDomService() { + return domNotifService; + } + private static class ListenerRegistrationImpl extends AbstractListenerRegistration { private final ListenerRegistration listenerRegistration; @@ -74,8 +88,4 @@ public class BindingDOMNotificationServiceAdapter implements NotificationService return ImmutableSet.of(DOMNotificationService.class); } } - - public DOMNotificationService getDomService() { - return domNotifService; - } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java new file mode 100644 index 0000000000..0a7ead1061 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/SingleBindingDOMNotificationAdapter.java @@ -0,0 +1,41 @@ +/* + * 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.Listener; +import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; + +final class SingleBindingDOMNotificationAdapter extends AbstractDOMNotificationListenerAdapter { + private final Listener delegate; + private final Executor executor; + private final Class type; + + SingleBindingDOMNotificationAdapter(final AdapterContext adapterContext, final Class type, + final Listener delegate, final Executor executor) { + super(adapterContext); + this.type = requireNonNull(type); + this.delegate = requireNonNull(delegate); + this.executor = requireNonNull(executor); + } + + @Override + void onNotification(final Absolute domType, final Notification notification) { + executor.execute(() -> delegate.onNotification(type.cast(notification))); + } + + @Override + Set getSupportedNotifications() { + return Set.of(Absolute.of(BindingReflections.findQName(type))); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java index 85b0de45d8..0513f2efb2 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiNotificationService.java @@ -9,8 +9,12 @@ package org.opendaylight.mdsal.binding.dom.adapter.osgi; import com.google.common.annotations.Beta; import java.util.Map; +import java.util.concurrent.Executor; import org.opendaylight.mdsal.binding.api.NotificationService; +import org.opendaylight.mdsal.binding.api.NotificationService.Listener; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.Registration; +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; @@ -32,6 +36,12 @@ public final class OSGiNotificationService extends AbstractAdaptedService Registration registerListener(final Class type, final Listener listener, + final Executor executor) { + return delegate().registerListener(type, listener, executor); + } + @Activate void activate(final Map properties) { start(properties); diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ForwardedNotificationAdapterTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ForwardedNotificationAdapterTest.java index 90e3ed96b3..d998bcbea3 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ForwardedNotificationAdapterTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ForwardedNotificationAdapterTest.java @@ -9,10 +9,8 @@ package org.opendaylight.mdsal.binding.dom.adapter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertSame; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -20,9 +18,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.junit.Assert; +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.spec.reflect.BindingReflections; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.OpendaylightMdsalBindingTestListener; @@ -30,101 +29,147 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TwoLevelListChangedBuilder; 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.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; +import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ForwardedNotificationAdapterTest extends AbstractNotificationBrokerTest { - private static final Logger LOG = LoggerFactory.getLogger(ForwardedNotificationAdapterTest.class); @Override protected Set getModuleInfos() throws Exception { - return ImmutableSet.of(BindingReflections.getModuleInfo(TwoLevelListChanged.class)); - + return Set.of(BindingReflections.getModuleInfo(TwoLevelListChanged.class)); } - private static TwoLevelListChanged createTestData() { - final TopLevelListKey key = new TopLevelListKey("test"); - return new TwoLevelListChangedBuilder() - .setTopLevelList(ImmutableMap.of(key, new TopLevelListBuilder().withKey(key).build())) - .build(); + @Test + public void testPutSubscription() throws InterruptedException { + final var listener = new TestNotifListener(1); + try (var reg = getNotificationService().registerNotificationListener(listener)) { + assertPutSubscription(listener); + } } @Test - public void testNotifSubscription() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final TwoLevelListChanged testData = createTestData(); + public void testPutSubscriptionSimple() throws InterruptedException { + final var listener = new SimpleNotifListener(1); + try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) { + assertPutSubscription(listener); + } + } - final TestNotifListener testNotifListener = new TestNotifListener(latch); - final ListenerRegistration listenerRegistration = getNotificationService() - .registerNotificationListener(testNotifListener); + private void assertPutSubscription(final AbstractNotifListener listener) throws InterruptedException { + final var testData = createTestData(); getNotificationPublishService().putNotification(testData); - latch.await(); - assertTrue(testNotifListener.getReceivedNotifications().size() == 1); - assertEquals(testData, testNotifListener.getReceivedNotifications().get(0)); + final var received = listener.awaitNotifications(); + assertEquals(1, received.size()); + assertSame(testData, received.get(0)); + } - listenerRegistration.close(); + @Test + public void testOfferSubscription() throws InterruptedException { + final var listener = new TestNotifListener(1); + try (var reg = getNotificationService().registerNotificationListener(listener)) { + assertOfferNotification(listener); + } } @Test - public void testNotifSubscription2() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final TwoLevelListChanged testData = createTestData(); + public void testOfferSubscriptionSimple() throws InterruptedException { + final var listener = new SimpleNotifListener(1); + try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) { + assertOfferNotification(listener); + } + } + + private void assertOfferNotification(final AbstractNotifListener listener) throws InterruptedException { + final var testData = createTestData(); - final TestNotifListener testNotifListener = new TestNotifListener(latch); - final ListenerRegistration listenerRegistration = getNotificationService() - .registerNotificationListener(testNotifListener); try { getNotificationPublishService().offerNotification(testData).get(1, TimeUnit.SECONDS); } catch (ExecutionException | TimeoutException e) { - LOG.error("Notification delivery failed", e); - Assert.fail("notification should be delivered"); + throw new AssertionError("Notification should be delivered", e); } - latch.await(); - assertTrue(testNotifListener.getReceivedNotifications().size() == 1); - assertEquals(testData, testNotifListener.getReceivedNotifications().get(0)); + final var received = listener.awaitNotifications(); + assertEquals(1, received.size()); + assertSame(testData, received.get(0)); + } - listenerRegistration.close(); + @Test + public void testOfferTimedNotification() throws InterruptedException { + final var listener = new TestNotifListener(1); + try (var reg = getNotificationService().registerNotificationListener(listener)) { + assertOfferTimedNotification(listener); + } } @Test - public void testNotifSubscription3() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final TwoLevelListChanged testData = createTestData(); + public void testOfferTimedNotificationSimple() throws InterruptedException { + final var listener = new SimpleNotifListener(1); + try (var reg = getNotificationService().registerListener(TwoLevelListChanged.class, listener)) { + assertOfferTimedNotification(listener); + } + } + + private void assertOfferTimedNotification(final AbstractNotifListener listener) throws InterruptedException { + final var testData = createTestData(); - final TestNotifListener testNotifListener = new TestNotifListener(latch); - final ListenerRegistration listenerRegistration = getNotificationService() - .registerNotificationListener(testNotifListener); assertNotSame(NotificationPublishService.REJECTED, - getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS)); + getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS)); + + final var received = listener.awaitNotifications(); + assertEquals(1, received.size()); + assertSame(testData, received.get(0)); + } - latch.await(); - assertTrue(testNotifListener.getReceivedNotifications().size() == 1); - assertEquals(testData, testNotifListener.getReceivedNotifications().get(0)); - listenerRegistration.close(); + private static @NonNull TwoLevelListChanged createTestData() { + return new TwoLevelListChangedBuilder() + .setTopLevelList(BindingMap.of(new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build())) + .build(); } - private static class TestNotifListener implements OpendaylightMdsalBindingTestListener { + private abstract static class AbstractNotifListener { private final List receivedNotifications = new ArrayList<>(); private final CountDownLatch latch; - TestNotifListener(final CountDownLatch latch) { - this.latch = latch; + AbstractNotifListener(final int expectedCount) { + latch = new CountDownLatch(expectedCount); } - @Override - public void onTwoLevelListChanged(final TwoLevelListChanged notification) { + final void receiveNotification(final TwoLevelListChanged notification) { receivedNotifications.add(notification); latch.countDown(); } - public List getReceivedNotifications() { + final List awaitNotifications() throws InterruptedException { + latch.await(); return receivedNotifications; } } + + private static class SimpleNotifListener extends AbstractNotifListener implements Listener { + 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); + } + } } -- 2.36.6