From 3ff1ec3dd60d1314297c3e37626bb25e21918e7b Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sun, 12 Mar 2017 01:15:44 +0100 Subject: [PATCH] Switch time keeping to java.time interfaces Date-based interfaces are not thread-safe and ambiguous, Java 8 gives us java.time, which has proper domain model. Change-Id: I6b0fa37c008a96ed6a47afc68a809570fd1848a0 Signed-off-by: Robert Varga --- .../sal/restconf/impl/RestconfImpl.java | 108 ++++++++---------- .../listeners/AbstractQueryParams.java | 24 ++-- .../impl/RestconfDataServiceImpl.java | 40 +++---- ...estconfStreamsSubscriptionServiceImpl.java | 48 ++++---- .../utils/RestconfStreamsConstants.java | 36 +++--- .../restful/utils/SubscribeToStreamUtil.java | 53 ++++----- .../mapping/RestconfMappingNodeUtil.java | 17 +-- .../impl/test/ExpressionParserTest.java | 4 +- ...stconfImplNotificationSubscribingTest.java | 12 +- .../providers/AbstractBodyReaderTest.java | 14 +-- .../mapping/RestconfMappingNodeUtilTest.java | 20 ++-- 11 files changed, 179 insertions(+), 197 deletions(-) diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java index 7484a377fb..d28fcea107 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -19,18 +19,19 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import java.net.URI; -import java.net.URISyntaxException; -import java.text.DateFormat; import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -70,6 +71,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; @@ -121,10 +123,6 @@ public class RestconfImpl implements RestconfService { private static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; - private BrokerFacade broker; - - private ControllerContext controllerContext; - private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class); private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE; @@ -143,11 +141,23 @@ public class RestconfImpl implements RestconfService { private static final String NETCONF_BASE_PAYLOAD_NAME = "data"; - private static final QName NETCONF_BASE_QNAME; + private static final QName NETCONF_BASE_QNAME = QName.create(QNameModule.create(URI.create(NETCONF_BASE), null), + NETCONF_BASE_PAYLOAD_NAME).intern(); private static final QNameModule SAL_REMOTE_AUGMENT; + static { + try { + SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08")); + } catch (final ParseException e) { + throw new ExceptionInInitializerError(e); + } + } - private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER; + private static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = + new AugmentationIdentifier(ImmutableSet.of( + QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), + QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); public static final CharSequence DATA_SUBSCR = "data-change-event-subscription"; private static final CharSequence CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR; @@ -155,24 +165,19 @@ public class RestconfImpl implements RestconfService { public static final CharSequence NOTIFICATION_STREAM = "notification-stream"; private static final CharSequence CREATE_NOTIFICATION_STREAM = "create-" + NOTIFICATION_STREAM; - static { - try { - final Date eventSubscriptionAugRevision = SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08"); - NETCONF_BASE_QNAME = - QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME); - SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision); - SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet( - QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), - QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); - } catch (final ParseException e) { - final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date"; - LOG.debug(errMsg); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } catch (final URISyntaxException e) { - final String errMsg = "It wasn't possible to create instance of URI class with " + NETCONF_BASE + " URI"; - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } - } + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4).appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2).appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2).appendLiteral('T') + .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .appendOffset("+HH:MM", "Z").toFormatter(); + + private BrokerFacade broker; + + private ControllerContext controllerContext; public void setBroker(final BrokerFacade broker) { this.broker = broker; @@ -417,8 +422,7 @@ public class RestconfImpl implements RestconfService { try { final String moduleName = pathArgs.get(0); final String revision = pathArgs.get(1); - final Date moduleRevision = SimpleDateFormatUtil.getRevisionFormat().parse(revision); - return QName.create(null, moduleRevision, moduleName); + return QName.create(null, SimpleDateFormatUtil.getRevisionFormat().parse(revision), moduleName); } catch (final ParseException e) { LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier); throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'", @@ -1084,8 +1088,8 @@ public class RestconfImpl implements RestconfService { boolean startTime_used = false; boolean stopTime_used = false; boolean filter_used = false; - Date start = null; - Date stop = null; + Instant start = Instant.now(); + Instant stop = null; String filter = null; for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { @@ -1148,30 +1152,16 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException(msg); } - private static Date parseDateFromQueryParam(final Entry> entry) { + private static Instant parseDateFromQueryParam(final Entry> entry) { final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); - String numOf_ms = ""; final String value = event.getValue(); - if (value.contains(".")) { - numOf_ms = numOf_ms + "."; - final int lastChar = value.contains("Z") ? value.indexOf("Z") : value.contains("+") ? value.indexOf("+") - : value.contains("-") ? value.indexOf("-") : value.length(); - for (int i = 0; i < lastChar - value.indexOf(".") - 1; i++) { - numOf_ms = numOf_ms + "S"; - } - } - String zone = ""; - if (!value.contains("Z")) { - zone = zone + "XXX"; - } - final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone); - + final TemporalAccessor p; try { - return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z")) - : value.replace('T', ' ')); - } catch (final ParseException e) { - throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e); + p = FORMATTER.parse(value); + } catch (DateTimeParseException e) { + throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e); } + return Instant.from(p); } /** @@ -1204,11 +1194,11 @@ public class RestconfImpl implements RestconfService { * @param start * - start-time of getting notification * @param filter - * - indicate wh ich subset of allpossible events are of interest + * - indicate which subset of all possible events are of interest * @return {@link URI} of location */ - private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, - final String filter) { + private URI notifStream(final String identifier, final UriInfo uriInfo, final Instant start, + final Instant stop, final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1221,7 +1211,7 @@ public class RestconfImpl implements RestconfService { for (final NotificationListenerAdapter listener : listeners) { this.broker.registerToListenNotification(listener); - listener.setQueryParams(start, stop, filter); + listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter)); } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); @@ -1253,7 +1243,7 @@ public class RestconfImpl implements RestconfService { * - indicate which subset of all possible events are of interest * @return {@link URI} of location */ - private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, + private URI dataSubs(final String identifier, final UriInfo uriInfo, final Instant start, final Instant stop, final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { @@ -1265,7 +1255,7 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } - listener.setQueryParams(start, stop, filter); + listener.setQueryParams(start, java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter)); final Map paramToValues = resolveValuesFromUri(identifier); final LogicalDatastoreType datastore = diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java index df9705cf76..67abe769a0 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java @@ -7,8 +7,11 @@ */ package org.opendaylight.netconf.sal.streams.listeners; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import java.io.StringReader; -import java.util.Date; +import java.time.Instant; +import java.util.Optional; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -45,10 +48,15 @@ abstract class AbstractQueryParams extends AbstractNotificationsData { } // FIXME: these should be final - private Date start = null; - private Date stop = null; + private Instant start = null; + private Instant stop = null; private String filter = null; + @VisibleForTesting + public final Instant getStart() { + return start; + } + /** * Set query parameters for listener * @@ -59,10 +67,10 @@ abstract class AbstractQueryParams extends AbstractNotificationsData { * @param filter * - indicate which subset of all possible events are of interest */ - public void setQueryParams(final Date start, final Date stop, final String filter) { - this.start = start; - this.stop = stop; - this.filter = filter; + public void setQueryParams(final Instant start, final Optional stop, final Optional filter) { + this.start = Preconditions.checkNotNull(start); + this.stop = stop.orElse(null); + this.filter = filter.orElse(null); } /** @@ -76,7 +84,7 @@ abstract class AbstractQueryParams extends AbstractNotificationsData { * false otherwise */ protected boolean checkQueryParams(final String xml, final T listener) { - final Date now = new Date(); + final Instant now = Instant.now(); if (this.stop != null) { if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) { return checkFilter(xml); diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java index 4a25fcb35e..efbb83a30d 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java @@ -14,11 +14,11 @@ import static org.opendaylight.restconf.restful.utils.RestconfStreamsConstants.S import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map.Entry; -import java.util.TimeZone; import javax.annotation.Nonnull; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @@ -58,7 +58,8 @@ import org.slf4j.LoggerFactory; */ public class RestconfDataServiceImpl implements RestconfDataService { - private final static Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss"); private final SchemaContextHandler schemaContextHandler; private final TransactionChainHandler transactionChainHandler; @@ -104,11 +105,11 @@ public class RestconfDataServiceImpl implements RestconfDataService { } boolean tagged = false; if (withDefa_used) { - if (withDefa.equals("report-all-tagged")) { + if ("report-all-tagged".equals(withDefa)) { tagged = true; withDefa = null; } - if (withDefa.equals("report-all")) { + if ("report-all".equals(withDefa)) { withDefa = null; } } @@ -143,26 +144,18 @@ public class RestconfDataServiceImpl implements RestconfDataService { RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.DATA_MISSING); } - final SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss"); - dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT")); - final String etag = '"' + node.getNodeType().getModule().getFormattedRevision() - + node.getNodeType().getLocalName() + '"'; - final Response resp; if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL)) || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) { - resp = Response.status(200) - .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) - .header("ETag", etag) - .header("Last-Modified", dateFormatGmt.format(new Date())) - .build(); - } else { - resp = Response.status(200) + return Response.status(200) .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) + .header("ETag", '"' + node.getNodeType().getModule().getFormattedRevision() + + node.getNodeType().getLocalName() + '"') + .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC()))) .build(); } - return resp; + return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build(); } @Override @@ -341,11 +334,10 @@ public class RestconfDataServiceImpl implements RestconfDataService { final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); if (domDataBrokerService.isPresent()) { return domDataBrokerService.get().createTransactionChain(RestConnectorProvider.transactionListener); - } else { - final String errMsg = "DOM data broker service isn't available for mount point " - + mountPoint.getIdentifier(); - LOG.warn(errMsg); - throw new RestconfDocumentedException(errMsg); } + + final String errMsg = "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier(); + LOG.warn(errMsg); + throw new RestconfDocumentedException(errMsg); } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java index fb2c76ee49..e19a175463 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java @@ -8,11 +8,13 @@ package org.opendaylight.restconf.restful.services.impl; import java.net.URI; -import java.util.Date; +import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import javax.annotation.Nonnull; import javax.ws.rs.core.UriInfo; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; @@ -67,8 +69,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu @Override public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { - final NotificationQueryParams notificationQueryParams = new NotificationQueryParams(); - notificationQueryParams.prepareParams(uriInfo); + final NotificationQueryParams notificationQueryParams = NotificationQueryParams.fromUriInfo(uriInfo); URI response = null; if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) { @@ -160,19 +161,24 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu * Parser and holder of query paramteres from uriInfo for notifications * */ - public final class NotificationQueryParams { + public static final class NotificationQueryParams { - private Date start = null; - private Date stop = null; - private String filter = null; - - private NotificationQueryParams() { + private final Instant start; + private final Instant stop; + private final String filter; + private NotificationQueryParams(final Instant start, final Instant stop, final String filter) { + this.start = start == null ? Instant.now() : start; + this.stop = stop; + this.filter = filter; } - private void prepareParams(final UriInfo uriInfo) { + static NotificationQueryParams fromUriInfo(final UriInfo uriInfo) { + Instant start = null; boolean startTime_used = false; + Instant stop = null; boolean stopTime_used = false; + String filter = null; boolean filter_used = false; for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { @@ -180,7 +186,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu case "start-time": if (!startTime_used) { startTime_used = true; - this.start = SubscribeToStreamUtil.parseDateFromQueryParam(entry); + start = SubscribeToStreamUtil.parseDateFromQueryParam(entry); } else { throw new RestconfDocumentedException("Start-time parameter can be used only once."); } @@ -188,7 +194,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu case "stop-time": if (!stopTime_used) { stopTime_used = true; - this.stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry); + stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry); } else { throw new RestconfDocumentedException("Stop-time parameter can be used only once."); } @@ -196,7 +202,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu case "filter": if (!filter_used) { filter_used = true; - this.filter = entry.getValue().iterator().next(); + filter = entry.getValue().iterator().next(); } break; default: @@ -208,9 +214,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter."); } - if (this.start == null) { - this.start = new Date(); - } + return new NotificationQueryParams(start, stop, filter); } /** @@ -218,8 +222,8 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu * * @return start-time */ - public Date getStart() { - return this.start; + public @Nonnull Instant getStart() { + return start; } /** @@ -227,8 +231,8 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu * * @return stop-time */ - public Date getStop() { - return this.stop; + public Optional getStop() { + return Optional.ofNullable(stop); } /** @@ -236,8 +240,8 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu * * @return filter */ - public String getFilter() { - return this.filter; + public Optional getFilter() { + return Optional.ofNullable(filter); } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java index 82bc4a0292..6a6f29ceed 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java @@ -7,7 +7,7 @@ */ package org.opendaylight.restconf.restful.utils; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableSet; import java.net.URI; import java.text.ParseException; import java.util.Date; @@ -21,7 +21,7 @@ import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +41,22 @@ public final class RestconfStreamsConstants { public static final QNameModule SAL_REMOTE_AUGMENT; - public static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER; + static { + final Date eventSubscriptionAugRevision; + try { + eventSubscriptionAugRevision = SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08"); + } catch (final ParseException e) { + final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date"; + LOG.debug(errMsg); + throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); + } + SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision) + .intern(); + } + + public static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = new AugmentationIdentifier( + ImmutableSet.of(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), + QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); public static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE; @@ -71,21 +86,6 @@ public final class RestconfStreamsConstants { public static final String STREAM_ACCESS_PATH_PART = "/access="; public static final String STREAM_LOCATION_PATH_PART = "/location"; - static { - Date eventSubscriptionAugRevision; - try { - eventSubscriptionAugRevision = SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08"); - } catch (final ParseException e) { - final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date"; - LOG.debug(errMsg); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } - SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision); - SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets - .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), - QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); - } - private RestconfStreamsConstants() { throw new UnsupportedOperationException("Util class."); } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java index 73ef1a91de..8786f67639 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java @@ -10,11 +10,13 @@ package org.opendaylight.restconf.restful.utils; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import java.net.URI; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -69,6 +71,15 @@ import org.slf4j.LoggerFactory; public final class SubscribeToStreamUtil { private static final Logger LOG = LoggerFactory.getLogger(SubscribeToStreamUtil.class); + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4).appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2).appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2).appendLiteral('T') + .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .appendOffset("+HH:MM", "Z").toFormatter(); private SubscribeToStreamUtil() { throw new UnsupportedOperationException("Util class"); @@ -234,37 +245,24 @@ public final class SubscribeToStreamUtil { /** * Parse input of query parameters - start-time or stop-time - from - * {@link DateAndTime} format to {@link Date} format + * {@link DateAndTime} format to {@link Instant} format * * @param entry * - start-time or stop-time as string in {@link DateAndTime} * format - * @return parsed {@link Date} by entry + * @return parsed {@link Instant} by entry */ - public static Date parseDateFromQueryParam(final Entry> entry) { + public static Instant parseDateFromQueryParam(final Entry> entry) { final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); - String numOf_ms = ""; final String value = event.getValue(); - if (value.contains(".")) { - numOf_ms = numOf_ms + "."; - final int lastChar = value.contains("Z") ? value.indexOf("Z") : (value.contains("+") ? value.indexOf("+") - : (value.contains("-") ? value.indexOf("-") : value.length())); - for (int i = 0; i < (lastChar - value.indexOf(".") - 1); i++) { - numOf_ms = numOf_ms + "S"; - } - } - String zone = ""; - if (!value.contains("Z")) { - zone = zone + "XXX"; - } - final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone); - + final TemporalAccessor p; try { - return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z")) - : value.replace('T', ' ')); - } catch (final ParseException e) { - throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e); + p = FORMATTER.parse(value); + } catch (DateTimeParseException e) { + throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e); } + return Instant.from(p); + } @SuppressWarnings("rawtypes") @@ -297,8 +295,7 @@ public final class SubscribeToStreamUtil { */ public static Map mapValuesFromUri(final String identifier) { final HashMap result = new HashMap<>(); - final String[] tokens = identifier.split(String.valueOf(RestconfConstants.SLASH)); - for (final String token : tokens) { + for (final String token : RestconfConstants.SLASH_SPLITTER.split(identifier)) { final String[] paramToken = token.split(String.valueOf(RestconfStreamsConstants.EQUAL)); if (paramToken.length == 2) { result.put(paramToken[0], paramToken[1]); diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java index 07a83205e2..d7539a6186 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java @@ -8,9 +8,11 @@ package org.opendaylight.restconf.utils.mapping; import java.net.URI; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Collection; -import java.util.Date; import java.util.Set; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.restconf.Rfc8040.IetfYangLibrary; @@ -471,8 +473,8 @@ public final class RestconfMappingNodeUtil { */ @SuppressWarnings("rawtypes") public static NormalizedNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName, - final Set notifications, final Date start, final String outputType, final URI uri, - final Module monitoringModule, final boolean existParent) { + final Set notifications, final Instant start, final String outputType, + final URI uri, final Module monitoringModule, final boolean existParent) { for (final NotificationDefinition notificationDefinition : notifications) { if (notificationDefinition.getQName().equals(notifiQName)) { final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule @@ -497,7 +499,8 @@ public final class RestconfMappingNodeUtil { if (start != null) { prepareLeafAndFillEntryBuilder(streamEntry, listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME), - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, + ZoneId.systemDefault()))); } prepareListAndFillEntryBuilder(streamEntry, (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), @@ -569,7 +572,7 @@ public final class RestconfMappingNodeUtil { */ @SuppressWarnings("rawtypes") public static NormalizedNode mapDataChangeNotificationStreamByIetfRestconfMonitoring( - final YangInstanceIdentifier path, final Date start, final String outputType, final URI uri, + final YangInstanceIdentifier path, final Instant start, final String outputType, final URI uri, final Module monitoringModule, final boolean existParent, final SchemaContext schemaContext) { final SchemaNode schemaNode = ParserIdentifier .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext), @@ -595,7 +598,7 @@ public final class RestconfMappingNodeUtil { listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true); prepareLeafAndFillEntryBuilder(streamEntry, listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME), - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, ZoneId.systemDefault()))); prepareListAndFillEntryBuilder(streamEntry, (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), outputType, uri); diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java index 2551e3ee33..9fd7e0ac44 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java @@ -12,7 +12,9 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; +import java.time.Instant; import java.util.Collection; +import java.util.Optional; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -133,7 +135,7 @@ public class ExpressionParserTest { final PathArgument pathValue = NodeIdentifier.create(QName.create("module", "2016-14-12", "localName")); Mockito.when(path.getLastPathArgument()).thenReturn(pathValue); final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON); - listener.setQueryParams(null, null, filter); + listener.setQueryParams(Instant.now(), Optional.empty(), Optional.ofNullable(filter)); // FIXME: do not use reflection here final Class superclass = listener.getClass().getSuperclass().getSuperclass(); diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java index fae6281d4a..bed8564037 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java @@ -7,9 +7,8 @@ */ package org.opendaylight.controller.sal.restconf.impl.test; -import java.lang.reflect.Field; +import java.time.Instant; import java.util.ArrayList; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; @@ -235,16 +234,11 @@ public class RestconfImplNotificationSubscribingTest { final AsyncDataChangeEvent> change = Mockito.mock(AsyncDataChangeEvent.class); - final Class superclass = listener.getClass().getSuperclass().getSuperclass(); - Field start = superclass.getDeclaredField("start"); - start.setAccessible(true); - Date startOrig = (Date) start.get(listener); + Instant startOrig = listener.getStart(); Assert.assertNotNull(startOrig); listener.onDataChanged(change); - start = superclass.getDeclaredField("start"); - start.setAccessible(true); - startOrig = (Date) start.get(listener); + startOrig = listener.getStart(); Assert.assertNull(startOrig); } diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java index a41f35270d..16ef10055f 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java @@ -32,20 +32,10 @@ abstract class AbstractBodyReaderTest { protected final static ControllerContext controllerContext = ControllerContext.getInstance(); protected final MediaType mediaType; - private static Field uriField; - private static Field requestField; protected final static DOMMountPointServiceHandler mountPointServiceHandler = mock( DOMMountPointServiceHandler.class); AbstractBodyReaderTest() throws NoSuchFieldException, IllegalAccessException { - uriField = AbstractIdentifierAwareJaxRsProvider.class - .getDeclaredField("uriInfo"); - uriField.setAccessible(true); - - requestField = AbstractIdentifierAwareJaxRsProvider.class - .getDeclaredField("request"); - requestField.setAccessible(true); - mediaType = getMediaType(); final Field mountPointServiceHandlerField = RestConnectorProvider.class. @@ -75,7 +65,7 @@ abstract class AbstractBodyReaderTest { when(uriInfoMock.getPathParameters()).thenReturn(pathParm); when(uriInfoMock.getPathParameters(false)).thenReturn(pathParm); when(uriInfoMock.getPathParameters(true)).thenReturn(pathParm); - uriField.set(normalizedNodeProvider, uriInfoMock); + normalizedNodeProvider.setUriInfo(uriInfoMock); final Request request = mock(Request.class); if (isPost) { @@ -84,7 +74,7 @@ abstract class AbstractBodyReaderTest { when(request.getMethod()).thenReturn("PUT"); } - requestField.set(normalizedNodeProvider, request); + normalizedNodeProvider.setRequest(request); } protected static void checkNormalizedNodeContext( diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java index 599184bdcd..4a554a9da2 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java @@ -12,10 +12,12 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.when; import java.net.URI; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -142,7 +144,7 @@ public class RestconfMappingNodeUtilTest { public void toStreamEntryNodeTest() throws Exception { final YangInstanceIdentifier path = ParserIdentifier.toInstanceIdentifier("nested-module:depth1-cont/depth2-leaf1", schemaContextMonitoring, null).getInstanceIdentifier(); - final Date start = new Date(); + final Instant start = Instant.now(); final String outputType = "XML"; final URI uri = new URI("uri"); final Module monitoringModule = schemaContextMonitoring @@ -160,7 +162,7 @@ public class RestconfMappingNodeUtilTest { @Test public void toStreamEntryNodeNotifiTest() throws Exception { - final Date start = new Date(); + final Instant start = Instant.now(); final String outputType = "JSON"; final URI uri = new URI("uri"); final Module monitoringModule = schemaContextMonitoring @@ -172,20 +174,20 @@ public class RestconfMappingNodeUtilTest { final QName notifiQName = QName.create("urn:nested:module", "2014-06-3", "notifi"); final NormalizedNode mappedData = - RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(notifiQName, schemaContextMonitoring.getNotifications(), start, - outputType, uri, monitoringModule, exist); + RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(notifiQName, + schemaContextMonitoring.getNotifications(), start, outputType, uri, monitoringModule, exist); assertNotNull(mappedData); testData(map, mappedData); } - private static Map prepareMap(final String name, final URI uri, final Date start, + private static Map prepareMap(final String name, final URI uri, final Instant start, final String outputType) { final Map map = new HashMap<>(); map.put(MonitoringModule.LEAF_NAME_STREAM_QNAME, name); map.put(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME, uri.toString()); map.put(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME, true); - map.put(MonitoringModule.LEAF_START_TIME_STREAM_QNAME, - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + map.put(MonitoringModule.LEAF_START_TIME_STREAM_QNAME, DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( + OffsetDateTime.ofInstant(start, ZoneId.systemDefault()))); map.put(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME, outputType); return map; } -- 2.36.6