description "Notification QNames";
min-elements 1;
}
- }
+ }
output {
leaf notification-stream-identifier {
type string;
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;
/**
* 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,
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;
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;
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;
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 =
.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.
*
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;
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;
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;
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;
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;
@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);
});
}
- 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.
reqPath.getMountPoint(), ar);
}
-
@VisibleForTesting
void yangPatchData(final @NonNull EffectiveModelContext modelContext,
final @NonNull PatchContext patch, final @Nullable DOMMountPoint mountPoint, final AsyncResponse ar) {
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;
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,
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?
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;
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;
* 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);
}
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;
* {@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
}
/**
- * 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));
}
}
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;
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);
}
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;
@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);
@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);
@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);
@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);
@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);
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();
}
}
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;
@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) {
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();
}
}