Clean up NotificationMessage 82/105682/7
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 25 Apr 2023 20:13:01 +0000 (22:13 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 25 Apr 2023 23:39:56 +0000 (01:39 +0200)
Use java.time.Instant to carry an immutable event time -- addressing two
SpotBugs warnings and making integration with DOMEvent/EventInstantAware
much mode sealmess.

JIRA: NETCONF-1000
Change-Id: I043c21abf7eb96368acfd73a6fddbc036e3ed7d3
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
netconf/mdsal-netconf-notification/src/main/java/org/opendaylight/netconf/mdsal/notification/impl/CreateSubscription.java
netconf/mdsal-netconf-notification/src/main/java/org/opendaylight/netconf/mdsal/notification/impl/NotificationsTransformUtil.java
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/netconf/mdsal/notification/impl/NetconfNotificationManagerTest.java
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/netconf/mdsal/notification/impl/NotificationsTransformUtilTest.java
plugins/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfMessageTransformUtil.java
plugins/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfNestedNotificationTest.java
plugins/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfToNotificationTest.java
protocol/netconf-api/src/main/java/org/opendaylight/netconf/api/messages/NotificationMessage.java
protocol/netconf-api/src/test/java/org/opendaylight/netconf/api/messages/NotificationMessageTest.java

index 697c81f55f4b67fac4ad370f548d8fe783ce77bc..fc6db7fe8a8af6dd218d9a3879a4bbd2b0eaf2b4 100644 (file)
@@ -11,7 +11,6 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 import java.util.Optional;
 import org.opendaylight.netconf.api.DocumentedException;
@@ -139,8 +138,8 @@ public class CreateSubscription extends AbstractSingletonNetconfOperation
                 final Optional<Document> filtered =
                         SubtreeFilter.applySubtreeNotificationFilter(filter, notification.getDocument());
                 if (filtered.isPresent()) {
-                    final Date eventTime = notification.getEventTime();
-                    currentSession.sendMessage(new NotificationMessage(filtered.orElseThrow(), eventTime));
+                    currentSession.sendMessage(new NotificationMessage(filtered.orElseThrow(),
+                        notification.getEventTime()));
                 }
             } catch (DocumentedException e) {
                 LOG.warn("Failed to process notification {}", notification, e);
index 26eac2aa909da322ab88a65da9d3f2a9946f0908..9c71f4430b1b3e3e053df9567afbd8cbf38576b9 100644 (file)
@@ -10,11 +10,9 @@ package org.opendaylight.netconf.mdsal.notification.impl;
 import static com.google.common.base.Verify.verify;
 
 import java.io.IOException;
-import java.util.Date;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.dom.DOMResult;
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecFactory;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeGenerator;
@@ -54,12 +52,7 @@ public final class NotificationsTransformUtil {
      * Transform base notification for capabilities into NetconfNotification.
      */
     public @NonNull NotificationMessage transform(final Notification<?> notification, final Absolute path) {
-        return transform(notification,
-            notification instanceof EventInstantAware aware ? Date.from(aware.eventInstant()) : null, path);
-    }
-
-    public @NonNull NotificationMessage transform(final Notification<?> notification, final @Nullable Date eventTime,
-            final Absolute path) {
+        final var eventTime = notification instanceof EventInstantAware aware ? aware.eventInstant() : null;
         final var containerNode = serializer.toNormalizedNodeNotification(notification);
         final var result = new DOMResult(XmlUtil.newDocument());
         try {
index a554c0d7f6ba53d46f6e5ac43973cca4fff02fc8..ba7e270c3561ab058a4621a35dd643f2ddf72c41 100644 (file)
@@ -88,21 +88,21 @@ public class NetconfNotificationManagerTest {
                 "2015-10-23T23:59:60.20001Z",
                 "1985-04-12T23:59:60.52Z"
         )) {
-            final Date apply = NotificationMessage.RFC3339_DATE_PARSER.apply(time);
-            final Date parse = new SimpleDateFormat(RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT).parse(iterator.next());
-            assertEquals(parse.getTime(), apply.getTime());
+            final var apply = NotificationMessage.RFC3339_DATE_PARSER.apply(time);
+            final var parse = new SimpleDateFormat(RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT).parse(iterator.next());
+            assertEquals(parse, Date.from(apply));
             // Testing that we're consistent from formatting to parsing.
             final String dateString = NotificationMessage.RFC3339_DATE_FORMATTER.apply(apply);
-            final Date date1 = NotificationMessage.RFC3339_DATE_PARSER.apply(dateString);
+            final var date1 = NotificationMessage.RFC3339_DATE_PARSER.apply(dateString);
             final String dateString1 = NotificationMessage.RFC3339_DATE_FORMATTER.apply(date1);
             assertEquals(apply, date1);
             assertEquals(dateString, dateString1);
         }
 
         // Testing that we're consistent from formatting to parsing.
-        final Date date0 = Date.from(Instant.ofEpochMilli(0));
+        final var date0 = Instant.ofEpochMilli(0);
         final String dateString0 = NotificationMessage.RFC3339_DATE_FORMATTER.apply(date0);
-        final Date date1 = NotificationMessage.RFC3339_DATE_PARSER.apply(dateString0);
+        final var date1 = NotificationMessage.RFC3339_DATE_PARSER.apply(dateString0);
         final String dateString1 = NotificationMessage.RFC3339_DATE_FORMATTER.apply(date1);
         assertEquals(date0, date1);
         assertEquals(dateString0, dateString1);
index efbbb54f032bf192d4115a94a96b1e51100f31c1..9056ac2c4ac3366bed9de8d2a4536db5a7d4b529 100644 (file)
@@ -12,7 +12,7 @@ import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.withSettings;
 
-import java.util.Date;
+import java.time.Instant;
 import java.util.Set;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -32,7 +32,7 @@ import org.xmlunit.diff.DefaultNodeMatcher;
 import org.xmlunit.diff.ElementSelectors;
 
 public class NotificationsTransformUtilTest {
-    private static final Date DATE = new Date();
+    private static final Instant EVENT_TIME = Instant.now();
     private static final String INNER_NOTIFICATION = """
             <netconf-capability-change xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
                 <deleted-capability>uri4</deleted-capability>
@@ -44,7 +44,7 @@ public class NotificationsTransformUtilTest {
     private static final String EXPECTED_NOTIFICATION =
         "<notification xmlns=\"urn:ietf:params:netconf:capability:notification:1.0\">\n"
         + INNER_NOTIFICATION
-        + "    <eventTime>" + NotificationMessage.RFC3339_DATE_FORMATTER.apply(DATE) + "</eventTime>\n"
+        + "    <eventTime>" + NotificationMessage.RFC3339_DATE_FORMATTER.apply(EVENT_TIME) + "</eventTime>\n"
         + "</notification>\n";
 
     private static NotificationsTransformUtil UTIL;
@@ -61,7 +61,7 @@ public class NotificationsTransformUtilTest {
             withSettings().extraInterfaces(EventInstantAware.class).defaultAnswer(Answers.CALLS_REAL_METHODS));
         doReturn(Set.of(new Uri("uri1"))).when(capabilityChange).getAddedCapability();
         doReturn(Set.of(new Uri("uri4"), new Uri("uri3"))).when(capabilityChange).getDeletedCapability();
-        doReturn(DATE.toInstant()).when((EventInstantAware) capabilityChange).eventInstant();
+        doReturn(EVENT_TIME).when((EventInstantAware) capabilityChange).eventInstant();
 
         final var notification = UTIL.transform(capabilityChange, Absolute.of(NetconfCapabilityChange.QNAME));
 
@@ -70,7 +70,7 @@ public class NotificationsTransformUtilTest {
 
     @Test
     public void testTransformFromDOM() throws Exception {
-        final var notification = new NotificationMessage(XmlUtil.readXmlToDocument(INNER_NOTIFICATION), DATE);
+        final var notification = new NotificationMessage(XmlUtil.readXmlToDocument(INNER_NOTIFICATION), EVENT_TIME);
 
         compareXml(EXPECTED_NOTIFICATION, notification.toString());
     }
index 5c788fd2b4c8a2200c7f23e846de8a8e54f8e4d9..9c6d58ee95840a6469584d6c534238ab19f5b637 100644 (file)
@@ -454,7 +454,7 @@ public final class NetconfMessageTransformUtil {
 
         try {
             return Map.entry(
-                    NotificationMessage.RFC3339_DATE_PARSER.apply(eventTimeElement.getTextContent()).toInstant(),
+                    NotificationMessage.RFC3339_DATE_PARSER.apply(eventTimeElement.getTextContent()),
                     notificationElement);
         } catch (final DocumentedException e) {
             throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message,
@@ -462,7 +462,7 @@ public final class NetconfMessageTransformUtil {
         } catch (final DateTimeParseException e) {
             LOG.warn("Unable to parse event time from {}. Setting time to {}", eventTimeElement,
                 NotificationMessage.UNKNOWN_EVENT_TIME, e);
-            return Map.entry(NotificationMessage.UNKNOWN_EVENT_TIME.toInstant(), notificationElement);
+            return Map.entry(NotificationMessage.UNKNOWN_EVENT_TIME, notificationElement);
         }
     }
 
index 89ed25bb2f9da7cfd609b1c8f293754b14409731..abcc3005071362953bc66dfbf4eb598e6fd8d4f3 100644 (file)
@@ -52,7 +52,7 @@ public class NetconfNestedNotificationTest extends AbstractBaseSchemasTest {
         assertNotNull(root);
         assertEquals(1, root.body().size());
         assertEquals("interface-enabled", root.getIdentifier().getNodeType().getLocalName());
-        assertEquals(NotificationMessage.RFC3339_DATE_PARSER.apply("2008-07-08T00:01:00Z").toInstant(),
+        assertEquals(NotificationMessage.RFC3339_DATE_PARSER.apply("2008-07-08T00:01:00Z"),
                 ((DOMEvent) domNotification).getEventInstant());
         assertEquals(Absolute.of(INTERFACES_QNAME, INTERFACE_QNAME, INTERFACE_ENABLED_NOTIFICATION_QNAME),
                 domNotification.getType());
index ebb2a93b659f98ac247a1ff09a93801bd7f49a27..2e3c7c744a747b17352e739a11d2cc3bf9f2295d 100644 (file)
@@ -80,7 +80,7 @@ public class NetconfToNotificationTest extends AbstractBaseSchemasTest {
         assertNotNull(root);
         assertEquals(6, root.body().size());
         assertEquals("user-visited-page", root.getIdentifier().getNodeType().getLocalName());
-        assertEquals(NotificationMessage.RFC3339_DATE_PARSER.apply("2015-10-23T09:42:27.67175+00:00").toInstant(),
+        assertEquals(NotificationMessage.RFC3339_DATE_PARSER.apply("2015-10-23T09:42:27.67175+00:00"),
                 ((DOMEvent) domNotification).getEventInstant());
     }
 }
index f06db1a26ae5676f98de02d139fff39b227e554d..dc7eed47293fb4012372ee03a3e72484bdc19861 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.netconf.api.messages;
 
 import static java.util.Objects.requireNonNull;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.text.ParsePosition;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -20,8 +19,8 @@ import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoField;
 import java.time.temporal.TemporalAccessor;
-import java.util.Date;
 import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
 import org.slf4j.Logger;
@@ -40,8 +39,8 @@ public final class NotificationMessage extends NetconfMessage {
     /**
      * Used for unknown/un-parse-able event-times.
      */
-    public static final Date UNKNOWN_EVENT_TIME = new Date(0);
-
+    // FIXME: we should differentiate unknown and invalid event times
+    public static final Instant UNKNOWN_EVENT_TIME = Instant.EPOCH;
 
     /**
      * The ISO-like date-time formatter that formats or parses a date-time with
@@ -51,14 +50,14 @@ public final class NotificationMessage extends NetconfMessage {
     private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
 
     /**
-     * Provide a {@link String} representation of a {@link Date} object,
+     * Provide a {@link String} representation of a {@link Instant} object,
      * using the time-zone offset for UTC, {@code ZoneOffset.UTC}.
      */
-    public static final Function<Date, String> RFC3339_DATE_FORMATTER = date ->
-            DATE_TIME_FORMATTER.format(date.toInstant().atOffset(ZoneOffset.UTC));
+    public static final Function<Instant, String> RFC3339_DATE_FORMATTER = date ->
+            DATE_TIME_FORMATTER.format(date.atOffset(ZoneOffset.UTC));
 
     /**
-     * Parse a {@link String} object into a {@link Date} using the time-zone
+     * Parse a {@link String} object into a {@link Instant} using the time-zone
      * offset for UTC, {@code ZoneOffset.UTC}, and the system default time-zone,
      * {@code ZoneId.systemDefault()}.
      * <p>
@@ -69,7 +68,7 @@ public final class NotificationMessage extends NetconfMessage {
      *     caller.
      * </p>
      */
-    public static final Function<String, Date> RFC3339_DATE_PARSER = time -> {
+    public static final Function<String, Instant> RFC3339_DATE_PARSER = time -> {
         try {
             final ZonedDateTime localDateTime = ZonedDateTime.parse(time, DATE_TIME_FORMATTER);
             final int startAt = 0;
@@ -95,9 +94,9 @@ public final class NotificationMessage extends NetconfMessage {
                         reminderBuilder.toString());
             }
 
-            return Date.from(Instant.from(localDateTime));
+            return Instant.from(localDateTime);
         } catch (DateTimeParseException exception) {
-            Date res = handlePotentialLeapSecond(time);
+            final var res = handlePotentialLeapSecond(time);
             if (res != null) {
                 return res;
             }
@@ -111,10 +110,10 @@ public final class NotificationMessage extends NetconfMessage {
      * conversion is applied, replacing the second-of-minute of 60 with 59.
      *
      * @param time {@link String} representation of a time
-     *     @return {@code null} if time isn't ISO compliant or if the time doesn't have a leap second
-     *     else a {@link Date} as per as the RFC3339_DATE_PARSER.
+     * @return {@code null} if time isn't ISO compliant or if the time doesn't have a leap second else an
+     *         {@link Instant} as per as the RFC3339_DATE_PARSER.
      */
-    private static Date handlePotentialLeapSecond(final String time) {
+    private static Instant handlePotentialLeapSecond(final String time) {
         // Parse the string from offset 0, so we get the whole value.
         final int offset = 0;
         final TemporalAccessor parsed = DATE_TIME_FORMATTER.parseUnresolved(time, new ParsePosition(offset));
@@ -166,22 +165,21 @@ public final class NotificationMessage extends NetconfMessage {
         return accessor.isSupported(field) ? (int) accessor.getLong(field) : 0;
     }
 
-    private final Date eventTime;
+    private final @NonNull Instant eventTime;
 
     /**
      * Create new notification and capture the timestamp in the constructor.
      */
     public NotificationMessage(final Document notificationContent) {
-        this(notificationContent, new Date());
+        this(notificationContent, Instant.now());
     }
 
     /**
      * Create new notification with provided timestamp.
      */
-    @SuppressFBWarnings("EI_EXPOSE_REP2") // stores a reference to an externally mutable Date object
-    public NotificationMessage(final Document notificationContent, final Date eventTime) {
+    public NotificationMessage(final Document notificationContent, final Instant eventTime) {
         super(wrapNotification(notificationContent, eventTime));
-        this.eventTime = eventTime;
+        this.eventTime = requireNonNull(eventTime);
     }
 
     /**
@@ -189,12 +187,11 @@ public final class NotificationMessage extends NetconfMessage {
      *
      * @return notification event time
      */
-    @SuppressFBWarnings("EI_EXPOSE_REP")
-    public Date getEventTime() {
+    public @NonNull Instant getEventTime() {
         return eventTime;
     }
 
-    private static Document wrapNotification(final Document notificationContent, final Date eventTime) {
+    private static Document wrapNotification(final Document notificationContent, final Instant eventTime) {
         requireNonNull(notificationContent);
         requireNonNull(eventTime);
 
@@ -205,14 +202,10 @@ public final class NotificationMessage extends NetconfMessage {
 
         final Element eventTimeElement = notificationContent.createElementNS(
             XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_NOTIFICATION_1_0, EVENT_TIME_TAG);
-        eventTimeElement.setTextContent(getSerializedEventTime(eventTime));
+        eventTimeElement.setTextContent(RFC3339_DATE_FORMATTER.apply(eventTime));
         entireNotification.appendChild(eventTimeElement);
 
         notificationContent.appendChild(entireNotification);
         return notificationContent;
     }
-
-    private static String getSerializedEventTime(final Date eventTime) {
-        return RFC3339_DATE_FORMATTER.apply(eventTime);
-    }
 }
index cf4f4248e25e1745f119c8e80f3f1d95f8494c42..f8a9ee8f15d93b1618e724d4781b3031c80c8a81 100644 (file)
@@ -5,13 +5,12 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.netconf.api.messages;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import java.util.Date;
+import java.time.Instant;
 import org.junit.Test;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
 import org.w3c.dom.Document;
@@ -26,8 +25,7 @@ public class NotificationMessageTest {
         final Element rootElement = document.createElement("test-root");
         document.appendChild(rootElement);
 
-        final Date eventTime = new Date();
-        eventTime.setTime(10000000);
+        final Instant eventTime = Instant.ofEpochMilli(10_000_000);
 
         final NotificationMessage netconfNotification = new NotificationMessage(document, eventTime);
         final Document resultDoc = netconfNotification.getDocument();
@@ -48,9 +46,7 @@ public class NotificationMessageTest {
 
         final Element eventTimeElement = (Element) childNodes.item(0);
 
-        assertEquals(eventTime.getTime(), NotificationMessage.RFC3339_DATE_PARSER
-                .apply(eventTimeElement.getTextContent()).getTime());
-
+        assertEquals(eventTime, NotificationMessage.RFC3339_DATE_PARSER.apply(eventTimeElement.getTextContent()));
         assertEquals(eventTime, netconfNotification.getEventTime());
     }
 }