package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
import java.net.URI;
import java.time.Instant;
import javax.ws.rs.Path;
import javax.ws.rs.core.UriInfo;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMNotificationService;
import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.FilterParameter;
+import org.opendaylight.restconf.nb.rfc8040.StartTimeParameter;
+import org.opendaylight.restconf.nb.rfc8040.StopTimeParameter;
import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
.appendOffset("+HH:MM", "Z").toFormatter();
- private final Instant start;
- private final Instant stop;
+ private final @NonNull Instant startTime;
+ private final Instant stopTime;
private final String filter;
private final boolean skipNotificationData;
- private NotificationQueryParams(final Instant start, final Instant stop, final String filter,
+ private NotificationQueryParams(final Instant startTime, final Instant stopTime, final String filter,
final boolean skipNotificationData) {
- this.start = start == null ? Instant.now() : start;
- this.stop = stop;
+ this.startTime = requireNonNull(startTime);
+ this.stopTime = stopTime;
this.filter = filter;
this.skipNotificationData = skipNotificationData;
}
static NotificationQueryParams fromUriInfo(final UriInfo uriInfo) {
- Instant start = null;
- boolean startTimeUsed = false;
- Instant stop = null;
- boolean stopTimeUsed = false;
+ Instant startTime = null;
+ Instant stopTime = null;
String filter = null;
- boolean filterUsed = false;
- boolean skipNotificationDataUsed = false;
boolean skipNotificationData = false;
for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
- switch (entry.getKey()) {
- case "start-time":
- if (!startTimeUsed) {
- startTimeUsed = true;
- start = parseDateFromQueryParam(entry);
- } else {
+ final String paramName = entry.getKey();
+ final List<String> paramValues = entry.getValue();
+ if (paramName.equals(StartTimeParameter.uriName())) {
+ switch (paramValues.size()) {
+ case 0:
+ break;
+ case 1:
+ startTime = parseDateFromQueryParam(paramValues.get(0));
+ break;
+ default:
throw new RestconfDocumentedException("Start-time parameter can be used only once.");
- }
- break;
- case "stop-time":
- if (!stopTimeUsed) {
- stopTimeUsed = true;
- stop = parseDateFromQueryParam(entry);
- } else {
+ }
+ } else if (paramName.equals(StopTimeParameter.uriName())) {
+ switch (paramValues.size()) {
+ case 0:
+ break;
+ case 1:
+ stopTime = parseDateFromQueryParam(paramValues.get(0));
+ break;
+ default:
throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
- }
- break;
- case "filter":
- if (!filterUsed) {
- filterUsed = true;
- filter = entry.getValue().iterator().next();
- }
- break;
- case "odl-skip-notification-data":
- if (!skipNotificationDataUsed) {
- skipNotificationDataUsed = true;
- skipNotificationData = Boolean.parseBoolean(entry.getValue().iterator().next());
- } else {
+ }
+ } else if (paramName.equals(FilterParameter.uriName())) {
+ if (!paramValues.isEmpty()) {
+ // FIXME: use FilterParameter
+ filter = paramValues.get(0);
+ }
+ } else if (paramName.equals("odl-skip-notification-data")) {
+ switch (paramValues.size()) {
+ case 0:
+ break;
+ case 1:
+ skipNotificationData = Boolean.parseBoolean(paramValues.get(0));
+ break;
+ default:
throw new RestconfDocumentedException(
- "Odl-skip-notification-data parameter can be used only once.");
- }
- break;
- default:
- throw new RestconfDocumentedException(
- "Bad parameter used with notifications: " + entry.getKey());
+ "Odl-skip-notification-data parameter can be used only once.");
+ }
+ } else {
+ throw new RestconfDocumentedException("Bad parameter used with notifications: " + paramName);
}
}
- if (!startTimeUsed && stopTimeUsed) {
+ if (startTime == null && stopTime != null) {
throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
}
- return new NotificationQueryParams(start, stop, filter, skipNotificationData);
+ return new NotificationQueryParams(startTime == null ? Instant.now() : startTime, stopTime, filter,
+ skipNotificationData);
}
* Parse input of query parameters - start-time or stop-time - from {@link DateAndTime} format
* to {@link Instant} format.
*
- * @param entry Start-time or stop-time as string in {@link DateAndTime} format.
+ * @param uriValue Start-time or stop-time as string in {@link DateAndTime} format.
* @return Parsed {@link Instant} by entry.
*/
- private static Instant parseDateFromQueryParam(final Entry<String, List<String>> entry) {
- final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
- final String value = event.getValue();
+ private static @NonNull Instant parseDateFromQueryParam(final String uriValue) {
final TemporalAccessor accessor;
try {
- accessor = FORMATTER.parse(value);
- } catch (final DateTimeParseException e) {
- throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e);
+ accessor = FORMATTER.parse(new DateAndTime(uriValue).getValue());
+ } catch (final DateTimeParseException | IllegalArgumentException e) {
+ throw new RestconfDocumentedException("Cannot parse of value in date: " + uriValue, e);
}
return Instant.from(accessor);
}
*
* @return start-time
*/
- public @NonNull Instant getStart() {
- return start;
+ public @NonNull Instant startTime() {
+ return startTime;
}
/**
*
* @return stop-time
*/
- public Optional<Instant> getStop() {
- return Optional.ofNullable(stop);
+ public @Nullable Instant stopTime() {
+ return stopTime;
}
/**
*
* @return filter
*/
- public Optional<String> getFilter() {
- return Optional.ofNullable(filter);
+ public @Nullable String filter() {
+ return filter;
}
/**
final URI uri = prepareUriByStreamName(uriInfo, streamName);
notificationListenerAdapter.listen(handlersHolder.getNotificationServiceHandler());
notificationListenerAdapter.setQueryParams(
- notificationQueryParams.getStart(),
- notificationQueryParams.getStop().orElse(null),
- notificationQueryParams.getFilter().orElse(null),
+ notificationQueryParams.startTime(),
+ notificationQueryParams.stopTime(),
+ notificationQueryParams.filter(),
false, notificationQueryParams.isSkipNotificationData());
final DOMDataBroker dataBroker = handlersHolder.getDataBroker();
notificationListenerAdapter.setCloseVars(dataBroker, handlersHolder.getSchemaHandler());
final MapEntryNode mapToStreams = RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
notificationListenerAdapter.getSchemaPath().lastNodeIdentifier(),
- schemaContext.getNotifications(), notificationQueryParams.getStart(),
+ schemaContext.getNotifications(), notificationQueryParams.startTime(),
notificationListenerAdapter.getOutputType(), uri);
// FIXME: how does this correlate with the transaction notificationListenerAdapter.close() will do?
ErrorType.APPLICATION, ErrorTag.DATA_MISSING));
listener.setQueryParams(
- notificationQueryParams.getStart(),
- notificationQueryParams.getStop().orElse(null),
- notificationQueryParams.getFilter().orElse(null),
+ notificationQueryParams.startTime(),
+ notificationQueryParams.stopTime(),
+ notificationQueryParams.filter(),
false, notificationQueryParams.isSkipNotificationData());
final DOMDataBroker dataBroker = handlersHolder.getDataBroker();
final MapEntryNode mapToStreams =
RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(listener.getPath(),
- notificationQueryParams.getStart(), listener.getOutputType(), uri, schemaContext, serializedPath);
+ notificationQueryParams.startTime(), listener.getOutputType(), uri, schemaContext, serializedPath);
final DOMDataTreeWriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
writeDataToDS(writeTransaction, mapToStreams);
submitData(writeTransaction);
}
// FIXME: these should be final
- private Instant start = null;
- private Instant stop = null;
+ private Instant startTime = null;
+ private Instant stopTime = null;
private String filter = null;
private boolean leafNodesOnly = false;
private boolean skipNotificationData = false;
@VisibleForTesting
public final Instant getStart() {
- return start;
+ return startTime;
}
/**
* Set query parameters for listener.
*
- * @param start Start-time of getting notification.
- * @param stop Stop-time of getting notification.
+ * @param startTime Start-time of getting notification.
+ * @param stopTime Stop-time of getting notification.
* @param filter Indicates which subset of all possible events are of interest.
* @param leafNodesOnly If TRUE, notifications will contain changes of leaf nodes only.
*/
@SuppressWarnings("checkstyle:hiddenField")
- public void setQueryParams(final Instant start, final Instant stop, final String filter,
+ public void setQueryParams(final Instant startTime, final Instant stopTime, final String filter,
final boolean leafNodesOnly, final boolean skipNotificationData) {
- this.start = requireNonNull(start);
- this.stop = stop;
+ this.startTime = requireNonNull(startTime);
+ this.stopTime = stopTime;
this.filter = filter;
this.leafNodesOnly = leafNodesOnly;
this.skipNotificationData = skipNotificationData;
@SuppressWarnings("checkstyle:IllegalCatch")
<T extends BaseListenerInterface> boolean checkStartStop(final Instant now, final T listener) {
- if (this.stop != null) {
- if (this.start.compareTo(now) < 0 && this.stop.compareTo(now) > 0) {
+ if (stopTime != null) {
+ if (startTime.compareTo(now) < 0 && stopTime.compareTo(now) > 0) {
return true;
}
- if (this.stop.compareTo(now) < 0) {
+ if (stopTime.compareTo(now) < 0) {
try {
listener.close();
} catch (final Exception e) {
throw new RestconfDocumentedException("Problem with unregister listener." + e);
}
}
- } else if (this.start != null) {
- if (this.start.compareTo(now) < 0) {
- this.start = null;
+ } else if (startTime != null) {
+ if (startTime.compareTo(now) < 0) {
+ startTime = null;
return true;
}
} else {
* @param xml XML data of notification.
*/
@SuppressWarnings("checkstyle:IllegalCatch")
+ // FIXME: this method is never called, have we lost functionality compared to bierman02?
boolean checkFilter(final String xml) {
- if (this.filter == null) {
+ if (filter == null) {
return true;
}
try {
final Document docOfXml = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
final XPath xPath = XPathFactory.newInstance().newXPath();
// FIXME: BUG-7956: xPath.setNamespaceContext(nsContext);
- return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+ return (boolean) xPath.compile(filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
}
}