Implement create-notification-stream 47/107147/5
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 31 Jul 2023 10:02:24 +0000 (12:02 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 29 Oct 2023 13:53:56 +0000 (14:53 +0100)
Refactor YANG notification subscription to work with proper filters,
just as they did before NETCONF-342.

This leaves a chunk of commented-out code, which will be addressed in a
subsequent patch.

JIRA: NETCONF-1099
Change-Id: I36a370e67b440cba68f1a26430978273db61234e
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-common-models/src/main/yang/sal-remote.yang
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/monitoring/RestconfStateStreams.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtil.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/SubscribeToStreamUtil.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenersBroker.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/NotificationListenerAdapter.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/monitoring/RestconfStateStreamsTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/JsonNotificationListenerTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/XmlNotificationListenerTest.java

index 0caefe140648fb4572f337b213a3c63f71ffc88f..fd2ef92940e258f8e05fb1e87355aeee5fe2adc4 100644 (file)
@@ -76,7 +76,7 @@ module sal-remote {
                 description "Notification QNames";
                 min-elements 1;
             }
-         }
+        }
         output {
             leaf notification-stream-identifier {
                 type string;
index 5879535a3e689697b789a1430123f0eab692b537..2dc7e91ae896478d111a968009f88cdf9fd30767 100644 (file)
@@ -15,8 +15,9 @@ import java.time.Instant;
 import java.time.OffsetDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.RestconfState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.Streams;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.streams.Stream;
@@ -70,27 +71,24 @@ public final class RestconfStateStreams {
     /**
      * Map data of yang notification to normalized node according to ietf-restconf-monitoring.
      *
-     * @param context the {@link EffectiveModelContext}
-     * @param notifiQName qname of notification from listener
+     * @param streamName stream name
+     * @param qnames Notification QNames to listen on
      * @param start start-time query parameter of notification
      * @param outputType output type of notification
      * @param uri location of registered listener for sending data of notification
      * @return mapped data of notification - map entry node if parent exists,
      *         container streams with list and map entry node if not
      */
-    public static MapEntryNode notificationStreamEntry(final EffectiveModelContext context, final QName notifiQName,
+    public static MapEntryNode notificationStreamEntry(final String streamName, final Set<QName> qnames,
             final Instant start, final String outputType, final URI uri) {
-        final var notificationDefinition = context.findNotification(notifiQName)
-            .orElseThrow(() -> new RestconfDocumentedException(notifiQName + " not found"));
-
-        final String streamName = notifiQName.getLocalName();
         final var streamEntry = Builders.mapEntryBuilder()
             .withNodeIdentifier(NodeIdentifierWithPredicates.of(Stream.QNAME, NAME_QNAME, streamName))
-            .withChild(ImmutableNodes.leafNode(NAME_QNAME, streamName));
-
-        notificationDefinition.getDescription().ifPresent(
-            desc -> streamEntry.withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, desc)));
-        streamEntry.withChild(ImmutableNodes.leafNode(REPLAY_SUPPORT_QNAME, Boolean.TRUE));
+            .withChild(ImmutableNodes.leafNode(NAME_QNAME, streamName))
+            .withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, qnames.stream()
+                .map(QName::toString)
+                .collect(Collectors.joining(","))))
+            // FIXME: this is not quite accurate
+            .withChild(ImmutableNodes.leafNode(REPLAY_SUPPORT_QNAME, Boolean.TRUE));
         if (start != null) {
             streamEntry.withChild(ImmutableNodes.leafNode(REPLAY_LOG_CREATION_TIME,
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start,
index c61860dc4d0d09e9a7ace13cd73ad3a22ced9120..b829710993370421a16d1d9743e0f6c1fa843ec4 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.Nullable;
@@ -27,6 +28,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.device.notification.rev2211
 import org.opendaylight.yang.gen.v1.urn.opendaylight.device.notification.rev221106.SubscribeDeviceNotificationOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateNotificationStreamInput;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
@@ -40,6 +42,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@@ -70,6 +74,8 @@ final class CreateStreamUtil {
         NodeIdentifier.create(DEVICE_NOTIFICATION_PATH_QNAME);
     private static final NodeIdentifier SAL_REMOTE_OUTPUT_NODEID =
         NodeIdentifier.create(CreateDataChangeEventSubscriptionOutput.QNAME);
+    private static final NodeIdentifier NOTIFICATIONS =
+        NodeIdentifier.create(QName.create(CreateNotificationStreamInput.QNAME, "notifications").intern());
     private static final NodeIdentifier PATH_NODEID =
         NodeIdentifier.create(QName.create(CreateDataChangeEventSubscriptionInput.QNAME, "path").intern());
     private static final NodeIdentifier STREAM_NAME_NODEID =
@@ -131,6 +137,52 @@ final class CreateStreamUtil {
             .build();
     }
 
+    // FIXME: this really should be a normal RPC implementation
+    static ContainerNode createNotificationStream(final ListenersBroker listenersBroker, final ContainerNode input,
+            final EffectiveModelContext refSchemaCtx) {
+        final var qnames = ((LeafSetNode<String>) input.getChildByArg(NOTIFICATIONS)).body().stream()
+            .map(LeafSetEntryNode::body)
+            .map(QName::create)
+            .sorted()
+            .collect(ImmutableSet.toImmutableSet());
+
+        final var streamNameBuilder = new StringBuilder(RestconfStreamsConstants.NOTIFICATION_STREAM).append('/');
+        var haveFirst = false;
+        for (var qname : qnames) {
+            final var module = refSchemaCtx.findModuleStatement(qname.getModule())
+                .orElseThrow(() -> new RestconfDocumentedException(qname + " refers to an unknown module",
+                    ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
+            final var stmt = module.findSchemaTreeNode(qname)
+                .orElseThrow(() -> new RestconfDocumentedException(qname + " refers to an notification",
+                    ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
+            if (!(stmt instanceof NotificationEffectiveStatement)) {
+                throw new RestconfDocumentedException(qname + " refers to a non-notification",
+                    ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+            }
+
+            if (haveFirst) {
+                streamNameBuilder.append(',');
+            } else {
+                haveFirst = true;
+            }
+            streamNameBuilder.append(module.argument().getLocalName()).append(':').append(qname.getLocalName());
+        }
+        final var outputType = prepareOutputType(input);
+        if (outputType.equals(NotificationOutputType.JSON)) {
+            streamNameBuilder.append('/').append(outputType.getName());
+        }
+
+        final var streamName = streamNameBuilder.toString();
+
+        // registration of the listener
+        listenersBroker.registerNotificationListener(qnames, streamName, outputType);
+
+        return Builders.containerBuilder()
+            .withNodeIdentifier(SAL_REMOTE_OUTPUT_NODEID)
+            .withChild(ImmutableNodes.leafNode(STREAM_NAME_NODEID, streamName))
+            .build();
+    }
+
     /**
      * Create device notification stream.
      *
index 76252d5d3760fe655245745446c228da57af1a9c..945a76414179d82b83e51ea8daaacfac3a227008 100644 (file)
@@ -9,11 +9,9 @@ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.NOTIFICATION_STREAM;
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAMS_PATH;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_ACCESS_PATH_PART;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_LOCATION_PATH_PART;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH;
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.FutureCallback;
@@ -29,7 +27,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.Encoded;
@@ -55,7 +52,6 @@ import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -81,15 +77,11 @@ import org.opendaylight.restconf.nb.rfc8040.databind.XmlResourceBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
 import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.restconf.nb.rfc8040.monitoring.RestconfStateStreams;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.CreateOrReplaceResult;
-import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
-import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
-import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
@@ -99,13 +91,11 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.slf4j.Logger;
@@ -183,15 +173,7 @@ public final class RestconfDataServiceImpl {
             @Context final UriInfo uriInfo) {
         final var readParams = QueryParams.newReadDataParams(uriInfo);
         final var databind = databindProvider.currentContext();
-        final var schemaContextRef = databind.modelContext();
         final var reqPath = server.bindRequestPath(databind, identifier);
-        final var mountPoint = reqPath.getMountPoint();
-
-        // FIXME: this looks quite crazy, why do we even have it?
-        if (mountPoint == null && identifier != null && identifier.contains(STREAMS_PATH)
-            && !identifier.contains(STREAM_PATH_PART)) {
-            createAllYangNotificationStreams(schemaContextRef, uriInfo);
-        }
 
         final var nodeAndResponse = readData(reqPath, readParams);
 
@@ -241,58 +223,58 @@ public final class RestconfDataServiceImpl {
         });
     }
 
-    private void createAllYangNotificationStreams(final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
-        final var transaction = dataBroker.newWriteOnlyTransaction();
-
-        for (var module : schemaContext.getModuleStatements().values()) {
-            final var moduleName = module.argument().getLocalName();
-            // Note: this handles only RFC6020 notifications
-            module.streamEffectiveSubstatements(NotificationEffectiveStatement.class).forEach(notification -> {
-                final var notifName = notification.argument();
-
-                writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
-                    createYangNotifiStream(moduleName, notifName, NotificationOutputType.XML));
-                writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
-                    createYangNotifiStream(moduleName, notifName, NotificationOutputType.JSON));
-            });
-        }
-
-        try {
-            transaction.commit().get();
-        } catch (final InterruptedException | ExecutionException e) {
-            throw new RestconfDocumentedException("Problem while putting data to DS.", e);
-        }
-    }
-
-    private NotificationListenerAdapter createYangNotifiStream(final String moduleName, final QName notifName,
-            final NotificationOutputType outputType) {
-        final var streamName = createNotificationStreamName(moduleName, notifName.getLocalName(), outputType);
-
-        final var existing = listenersBroker.notificationListenerFor(streamName);
-        return existing != null ? existing
-            : listenersBroker.registerNotificationListener(Absolute.of(notifName), streamName, outputType);
-    }
-
-    private static String createNotificationStreamName(final String moduleName, final String notifName,
-            final NotificationOutputType outputType) {
-        final var sb = new StringBuilder()
-            .append(RestconfStreamsConstants.NOTIFICATION_STREAM)
-            .append('/').append(moduleName).append(':').append(notifName);
-        if (outputType != NotificationOutputType.XML) {
-            sb.append('/').append(outputType.getName());
-        }
-        return sb.toString();
-    }
-
-    private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
-            final UriInfo uriInfo, final DOMDataTreeWriteOperations tx, final NotificationListenerAdapter listener) {
-        final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
-        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(schemaContext,
-                listener.getSchemaPath().lastNodeIdentifier(), null, listener.getOutputType(), uri);
-
-        tx.merge(LogicalDatastoreType.OPERATIONAL,
-            RestconfStateStreams.restconfStateStreamPath(mapToStreams.name()), mapToStreams);
-    }
+//    private void createAllYangNotificationStreams(final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
+//        final var transaction = dataBroker.newWriteOnlyTransaction();
+//
+//        for (var module : schemaContext.getModuleStatements().values()) {
+//            final var moduleName = module.argument().getLocalName();
+//            // Note: this handles only RFC6020 notifications
+//            module.streamEffectiveSubstatements(NotificationEffectiveStatement.class).forEach(notification -> {
+//                final var notifName = notification.argument();
+//
+//                writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
+//                    createYangNotifiStream(moduleName, notifName, NotificationOutputType.XML));
+//                writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
+//                    createYangNotifiStream(moduleName, notifName, NotificationOutputType.JSON));
+//            });
+//        }
+//
+//        try {
+//            transaction.commit().get();
+//        } catch (final InterruptedException | ExecutionException e) {
+//            throw new RestconfDocumentedException("Problem while putting data to DS.", e);
+//        }
+//    }
+//
+//    private NotificationListenerAdapter createYangNotifiStream(final String moduleName, final QName notifName,
+//            final NotificationOutputType outputType) {
+//        final var streamName = createNotificationStreamName(moduleName, notifName.getLocalName(), outputType);
+//
+//        final var existing = listenersBroker.notificationListenerFor(streamName);
+//        return existing != null ? existing
+//            : listenersBroker.registerNotificationListener(ImmutableSet.of(notifName), streamName, outputType);
+//    }
+//
+//    private static String createNotificationStreamName(final String moduleName, final String notifName,
+//            final NotificationOutputType outputType) {
+//        final var sb = new StringBuilder()
+//            .append(RestconfStreamsConstants.NOTIFICATION_STREAM)
+//            .append('/').append(moduleName).append(':').append(notifName);
+//        if (outputType != NotificationOutputType.XML) {
+//            sb.append('/').append(outputType.getName());
+//        }
+//        return sb.toString();
+//    }
+//
+//    private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
+//            final UriInfo uriInfo, final DOMDataTreeWriteOperations tx, final NotificationListenerAdapter listener) {
+//        final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
+//        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(schemaContext,
+//                listener.getSchemaPath().lastNodeIdentifier(), null, listener.getOutputType(), uri);
+//
+//        tx.merge(LogicalDatastoreType.OPERATIONAL,
+//            RestconfStateStreams.restconfStateStreamPath(mapToStreams.name()), mapToStreams);
+//    }
 
     /**
      * Replace the data store.
@@ -797,7 +779,6 @@ public final class RestconfDataServiceImpl {
             reqPath.getMountPoint(), ar);
     }
 
-
     @VisibleForTesting
     void yangPatchData(final @NonNull EffectiveModelContext modelContext,
             final @NonNull PatchContext patch, final @Nullable DOMMountPoint mountPoint, final AsyncResponse ar) {
index 8589df772b7add9e5a629c18a8e626acf6007a81..d02b994f208d60a2acaba6acc91ef5ba7e565f5c 100644 (file)
@@ -39,6 +39,7 @@ import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.device.notification.rev221106.SubscribeDeviceNotification;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscription;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateNotificationStream;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -166,6 +167,9 @@ public final class RestconfInvokeOperationsServiceImpl {
             if (CreateDataChangeEventSubscription.QNAME.equals(type)) {
                 return RestconfFuture.of(Optional.of(CreateStreamUtil.createDataChangeNotifiStream(
                     streamUtils.listenersBroker(), input, localDatabind.modelContext())));
+            } else if (CreateNotificationStream.QNAME.equals(type)) {
+                return RestconfFuture.of(Optional.of(CreateStreamUtil.createNotificationStream(
+                    streamUtils.listenersBroker(), input, localDatabind.modelContext())));
             } else if (SubscribeDeviceNotification.QNAME.equals(type)) {
                 final var baseUrl = streamUtils.prepareUriByStreamName(uriInfo, "").toString();
                 return RestconfFuture.of(Optional.of(CreateStreamUtil.createDeviceNotificationListener(baseUrl, input,
index ab679b1baff7e22d80e5fda78eeab9feb200eb99..ba6be4e46a9b312dd0b02a83984bd4447997326f 100644 (file)
@@ -140,14 +140,13 @@ abstract class SubscribeToStreamUtil {
                 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
         }
 
-        final var schemaContext = handlersHolder.getDatabindProvider().currentContext().modelContext();
         final URI uri = prepareUriByStreamName(uriInfo, streamName);
         notificationListenerAdapter.setQueryParams(notificationQueryParams);
         notificationListenerAdapter.listen(handlersHolder.getNotificationServiceHandler());
         final DOMDataBroker dataBroker = handlersHolder.getDataBroker();
         notificationListenerAdapter.setCloseVars(dataBroker, handlersHolder.getDatabindProvider());
-        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(schemaContext,
-            notificationListenerAdapter.getSchemaPath().lastNodeIdentifier(), notificationListenerAdapter.getStart(),
+        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(streamName,
+            notificationListenerAdapter.qnames(), notificationListenerAdapter.getStart(),
             notificationListenerAdapter.getOutputType(), uri);
 
         // FIXME: how does this correlate with the transaction notificationListenerAdapter.close() will do?
index af6d5f7e6a2b011a4e93609c4fe9f26eb4b43a04..1a5d502de86487b3c882ae8e7f3547ee198df20f 100644 (file)
@@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableSet;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.locks.StampedLock;
@@ -21,10 +22,10 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.restconf.nb.rfc8040.URLConstants;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.slf4j.Logger;
@@ -157,21 +158,21 @@ public final class ListenersBroker {
      * Creates new {@link NotificationDefinition} listener using input stream name and schema path
      * if such listener haven't been created yet.
      *
-     * @param schemaPath Schema path of YANG notification structure.
+     * @param notifications {@link QName}s of accepted YANG notifications
      * @param streamName Stream name.
      * @param outputType Specific type of output for notifications - XML or JSON.
      * @return Created or existing notification listener adapter.
      */
-    public NotificationListenerAdapter registerNotificationListener(final Absolute schemaPath,
+    public NotificationListenerAdapter registerNotificationListener(final ImmutableSet<QName> notifications,
             final String streamName, final NotificationOutputType outputType) {
-        requireNonNull(schemaPath);
+        requireNonNull(notifications);
         requireNonNull(streamName);
         requireNonNull(outputType);
 
         final long stamp = notificationListenersLock.writeLock();
         try {
             return notificationListeners.computeIfAbsent(streamName,
-                stream -> new NotificationListenerAdapter(schemaPath, stream, outputType, this));
+                stream -> new NotificationListenerAdapter(notifications, stream, outputType, this));
         } finally {
             notificationListenersLock.unlockWrite(stamp);
         }
index 0bec0dfa7275b8de9b96ac91ccd66c818a3ed32a..556bc3524eb41fc54c56732d32927c625a5914a6 100644 (file)
@@ -10,8 +10,10 @@ package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
@@ -19,19 +21,20 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
  * {@link NotificationListenerAdapter} is responsible to track events on notifications.
  */
 public final class NotificationListenerAdapter extends AbstractNotificationListenerAdaptor {
-    private final Absolute path;
+    private final ImmutableSet<QName> paths;
 
     /**
      * Set path of listener and stream name.
      *
-     * @param path       Schema path of YANG notification.
+     * @param paths      Top-level  Schema path of YANG notification.
      * @param streamName Name of the stream.
      * @param outputType Type of output on notification (JSON or XML).
+     * @param listenersBroker Associated {@link ListenersBroker}
      */
-    NotificationListenerAdapter(final Absolute path, final String streamName, final NotificationOutputType outputType,
-            final ListenersBroker listenersBroker) {
+    NotificationListenerAdapter(final ImmutableSet<QName> paths, final String streamName,
+            final NotificationOutputType outputType, final ListenersBroker listenersBroker) {
         super(streamName, outputType, listenersBroker);
-        this.path = requireNonNull(path);
+        this.paths = requireNonNull(paths);
     }
 
     @Override
@@ -40,22 +43,23 @@ public final class NotificationListenerAdapter extends AbstractNotificationListe
     }
 
     /**
-     * Get schema path of notification.
+     * Return notification QNames.
      *
-     * @return The configured schema path that points to observing YANG notification schema node.
+     * @return The YANG notification {@link QName}s this listener is bound to
      */
-    public Absolute getSchemaPath() {
-        return path;
+    public ImmutableSet<QName> qnames() {
+        return paths;
     }
 
     public synchronized void listen(final DOMNotificationService notificationService) {
         if (!isListening()) {
-            setRegistration(notificationService.registerNotificationListener(this, getSchemaPath()));
+            setRegistration(notificationService.registerNotificationListener(this,
+                paths.stream().map(Absolute::of).toList()));
         }
     }
 
     @Override
     ToStringHelper addToStringAttributes(final ToStringHelper helper) {
-        return super.addToStringAttributes(helper.add("path", path));
+        return super.addToStringAttributes(helper.add("paths", paths));
     }
 }
index 751d1f8a903852ede72ff16d301b47917905a126..80065a33b25158d173466d27229207b9854cab3d 100644 (file)
@@ -18,6 +18,7 @@ import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
@@ -73,10 +74,10 @@ public class RestconfStateStreamsTest {
         final URI uri = new URI("uri");
 
         final Map<QName, Object> map = prepareMap("notifi", uri, start, outputType);
-        map.put(RestconfStateStreams.DESCRIPTION_QNAME, "Notifi");
+        map.put(RestconfStateStreams.DESCRIPTION_QNAME, "(urn:nested:module?revision=2014-06-03)notifi");
 
-        final MapEntryNode mappedData = RestconfStateStreams.notificationStreamEntry(schemaContextMonitoring,
-            QName.create("urn:nested:module", "2014-06-03", "notifi"), start, outputType, uri);
+        final MapEntryNode mappedData = RestconfStateStreams.notificationStreamEntry("notifi",
+            Set.of(QName.create("urn:nested:module", "2014-06-03", "notifi")), start, outputType, uri);
         assertMappedData(map, mappedData);
     }
 
index 8dbd7b7594901d9c339b0347dbf57735eec6433f..d452733d3bf37d71b6316b514f8bca3534bda767 100644 (file)
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import com.google.common.collect.ImmutableSet;
 import java.time.Instant;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -38,14 +39,14 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
 
     @Test
     public void notifi_leafTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-leaf"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-leaf");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
         final String result = prepareJson(notificationData, schemaPathNotifi);
@@ -60,15 +61,15 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
 
     @Test
     public void notifi_cont_leafTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-cont"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-cont");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
         final ContainerNode cont = mockCont(QName.create(MODULE, "cont"), leaf);
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), cont);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, cont);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
         final String result = prepareJson(notificationData, schemaPathNotifi);
@@ -82,18 +83,18 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
 
     @Test
     public void notifi_list_Test() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-list"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-list");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
         final MapEntryNode entry = mockMapEntry(QName.create(MODULE, "lst"), leaf);
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), Builders.mapBuilder()
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, Builders.mapBuilder()
             .withNodeIdentifier(NodeIdentifier.create(QName.create(MODULE, "lst")))
             .withChild(entry)
             .build());
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
         final String result = prepareJson(notificationData, schemaPathNotifi);
@@ -107,14 +108,14 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
 
     @Test
     public void notifi_grpTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-grp"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-grp");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
         final String result = prepareJson(notificationData, schemaPathNotifi);
@@ -126,14 +127,14 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
 
     @Test
     public void notifi_augmTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-augm"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-augm");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf-augm"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
         final String result = prepareJson(notificationData, schemaPathNotifi);
@@ -161,10 +162,10 @@ public class JsonNotificationListenerTest extends AbstractNotificationListenerTe
         return ImmutableNodes.leafNode(leafQName, "value");
     }
 
-    private String prepareJson(final DOMNotification notificationData, final Absolute schemaPathNotifi)
+    private String prepareJson(final DOMNotification notificationData, final QName schemaPathNotifi)
             throws Exception {
-        final var notifiAdapter = listenersBroker.registerNotificationListener(schemaPathNotifi, "json-stream",
-            NotificationOutputType.JSON);
+        final var notifiAdapter = listenersBroker.registerNotificationListener(ImmutableSet.of(schemaPathNotifi),
+            "json-stream", NotificationOutputType.JSON);
         return notifiAdapter.formatter().eventData(SCHEMA_CONTEXT, notificationData, Instant.now()).orElseThrow();
     }
 }
index 15afe969bfcd90395fb6ce721b9f5bee080d1d6a..b8907bd254ac425c533ee78c505791229ac382d9 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import com.google.common.collect.ImmutableSet;
 import java.time.Instant;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,112 +35,97 @@ public class XmlNotificationListenerTest extends AbstractNotificationListenerTes
 
     @Test
     public void notifi_leafTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-leaf"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-leaf");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
-        final String result = prepareXmlResult(notificationData, schemaPathNotifi);
-
-        final String control = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
-                + "<eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-leaf xmlns=\"notifi:mod\">"
-                + "<lf>value</lf></notifi-leaf></notification>";
-
-        assertXmlMatches(result, control);
+        assertXmlMatches(prepareXmlResult(notificationData, schemaPathNotifi), """
+            <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">\
+            <eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-leaf xmlns="notifi:mod">\
+            <lf>value</lf></notifi-leaf></notification>""");
     }
 
     @Test
     public void notifi_cont_leafTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-cont"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-cont");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
         final ContainerNode cont = mockCont(QName.create(MODULE, "cont"), leaf);
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), cont);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, cont);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
-        final String result = prepareXmlResult(notificationData, schemaPathNotifi);
-
-        final String control = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
-                + "<eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-cont xmlns=\"notifi:mod\">"
-                + "<cont><lf>value</lf></cont></notifi-cont></notification>";
-
-        assertXmlMatches(result, control);
+        assertXmlMatches(prepareXmlResult(notificationData, schemaPathNotifi), """
+            <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">\
+            <eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-cont xmlns="notifi:mod">\
+            <cont><lf>value</lf></cont></notifi-cont></notification>""");
     }
 
     @Test
     public void notifi_list_Test() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-list"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-list");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
         final MapEntryNode entry = mockMapEntry(QName.create(MODULE, "lst"), leaf);
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), Builders.mapBuilder()
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, Builders.mapBuilder()
             .withNodeIdentifier(NodeIdentifier.create(QName.create(MODULE, "lst")))
             .withChild(entry)
             .build());
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
-        final String result = prepareXmlResult(notificationData, schemaPathNotifi);
-
-        final String control = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
-                + "<eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-list xmlns=\"notifi:mod\">"
-                + "<lst><lf>value</lf></lst></notifi-list></notification>";
-
-        assertXmlMatches(result, control);
+        assertXmlMatches(prepareXmlResult(notificationData, schemaPathNotifi), """
+            <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">\
+            <eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-list xmlns="notifi:mod">\
+            <lst><lf>value</lf></lst></notifi-list></notification>""");
     }
 
     @Test
     public void notifi_grpTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-grp"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-grp");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
-        final String result = prepareXmlResult(notificationData, schemaPathNotifi);
-
-        final String control = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
-                + "<eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-grp xmlns=\"notifi:mod\">"
-                + "<lf>value</lf></notifi-grp></notification>";
-
-        assertXmlMatches(result, control);
+        assertXmlMatches(prepareXmlResult(notificationData, schemaPathNotifi), """
+            <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">\
+            <eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-grp xmlns="notifi:mod">\
+            <lf>value</lf></notifi-grp></notification>""");
     }
 
     @Test
     public void notifi_augmTest() throws Exception {
-        final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-augm"));
+        final QName schemaPathNotifi = QName.create(MODULE, "notifi-augm");
 
         final DOMNotification notificationData = mock(DOMNotification.class);
 
         final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf-augm"));
-        final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi, leaf);
 
-        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getType()).thenReturn(Absolute.of(schemaPathNotifi));
         when(notificationData.getBody()).thenReturn(notifiBody);
 
-        final String result = prepareXmlResult(notificationData, schemaPathNotifi);
-
-        final String control = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">"
-                + "<eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-augm xmlns=\"notifi:mod\">"
-                + "<lf-augm>value</lf-augm></notifi-augm></notification>";
-
-        assertXmlMatches(result, control);
+        assertXmlMatches(prepareXmlResult(notificationData, schemaPathNotifi), """
+            <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">\
+            <eventTime>2020-06-29T14:23:46.086855+02:00</eventTime><notifi-augm xmlns="notifi:mod">\
+            <lf-augm>value</lf-augm></notifi-augm></notification>""");
     }
 
     private static void assertXmlMatches(final String result, final String control) {
@@ -168,10 +154,10 @@ public class XmlNotificationListenerTest extends AbstractNotificationListenerTes
         return ImmutableNodes.leafNode(leafQName, "value");
     }
 
-    private String prepareXmlResult(final DOMNotification notificationData, final Absolute schemaPathNotifi)
+    private String prepareXmlResult(final DOMNotification notificationData, final QName schemaPathNotifi)
             throws Exception {
-        final var notifiAdapter = listenersBroker.registerNotificationListener(schemaPathNotifi, "xml-stream",
-            NotificationOutputType.XML);
+        final var notifiAdapter = listenersBroker.registerNotificationListener(ImmutableSet.of(schemaPathNotifi),
+            "xml-stream", NotificationOutputType.XML);
         return notifiAdapter.formatter().eventData(SCHEMA_CONTEXT, notificationData, Instant.now()).orElseThrow();
     }
 }