Use FilterParameter for communicating its value 02/98102/1
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 24 Oct 2021 12:19:24 +0000 (14:19 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 24 Oct 2021 12:55:56 +0000 (14:55 +0200)
This improves type-safety and moves concerns.

JIRA: NETCONF-773
Change-Id: I806563be3520feb31711b7e45f4956231b6e11b2
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/FilterParameter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfStreamsSubscriptionServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/SubscribeToStreamUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractQueryParams.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/NotificationListenerAdapter.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapterTest.java

index 5c6aed3efeebec0504e471ed505c099fe6092a73..2c2777081ee5b5520c49e189164dc68a28067d74 100644 (file)
@@ -10,12 +10,8 @@ package org.opendaylight.restconf.nb.rfc8040;
 import static java.util.Objects.requireNonNull;
 
 import java.net.URI;
-import javax.xml.xpath.XPathExpressionException;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.yangtools.concepts.Immutable;
-import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
-import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
-import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
 
 /**
  * This class represents a {@code filter} parameter as defined in
@@ -25,25 +21,25 @@ import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
 public final class FilterParameter implements Immutable {
     private static final URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:filter:1.0");
 
-    private final YangXPathExpression value;
+    // FIXME: can we have a parsed, but not bound version of an XPath, please?
+    private final String value;
 
-    private FilterParameter(final YangXPathExpression value) {
+    private FilterParameter(final String value) {
         this.value = requireNonNull(value);
     }
 
-    public static FilterParameter forUriValue(final YangXPathParserFactory parserFactory, final String uriValue)
-            throws XPathExpressionException {
-        return new FilterParameter(parserFactory.newParser(YangXPathMathMode.EXACT).parseExpression(uriValue));
-    }
-
-    public YangXPathExpression value() {
-        return value;
+    public static FilterParameter forUriValue(final String uriValue) {
+        return new FilterParameter(uriValue);
     }
 
     public static String uriName() {
         return "filter";
     }
 
+    public String uriValue() {
+        return value;
+    }
+
     public static URI capabilityUri() {
         return CAPABILITY;
     }
index e2d95d9fabd3d740af8a3b5ebfd90b56e658d4e8..35cdea9e8792007fc006950f3a98b3988d40bcb6 100644 (file)
@@ -8,21 +8,13 @@
 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 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.List;
 import java.util.Map.Entry;
 import java.util.Optional;
 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;
@@ -36,7 +28,6 @@ import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.nb.rfc8040.streams.Configuration;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -167,33 +158,23 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
      * Parser and holder of query paramteres from uriInfo for notifications.
      */
     public static final class NotificationQueryParams {
-        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 final @NonNull Instant startTime;
-        private final Instant stopTime;
-        private final String filter;
+        private final StartTimeParameter startTime;
+        private final StopTimeParameter stopTime;
+        private final FilterParameter filter;
         private final boolean skipNotificationData;
 
-        private NotificationQueryParams(final Instant startTime, final Instant stopTime, final String filter,
-                final boolean skipNotificationData) {
-            this.startTime = requireNonNull(startTime);
+        private NotificationQueryParams(final StartTimeParameter startTime, final StopTimeParameter stopTime,
+                final FilterParameter filter, final boolean skipNotificationData) {
+            this.startTime = startTime;
             this.stopTime = stopTime;
             this.filter = filter;
             this.skipNotificationData = skipNotificationData;
         }
 
         static NotificationQueryParams fromUriInfo(final UriInfo uriInfo) {
-            Instant startTime = null;
-            Instant stopTime = null;
-            String filter = null;
+            StartTimeParameter startTime = null;
+            StopTimeParameter stopTime = null;
+            FilterParameter filter = null;
             boolean skipNotificationData = false;
 
             for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
@@ -204,7 +185,12 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
                         case 0:
                             break;
                         case 1:
-                            startTime = parseDateFromQueryParam(paramValues.get(0));
+                            final String str = paramValues.get(0);
+                            try {
+                                startTime = StartTimeParameter.forUriValue(str);
+                            } catch (IllegalArgumentException e) {
+                                throw new RestconfDocumentedException("Invalid start-time date: " + str, e);
+                            }
                             break;
                         default:
                             throw new RestconfDocumentedException("Start-time parameter can be used only once.");
@@ -214,15 +200,19 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
                         case 0:
                             break;
                         case 1:
-                            stopTime = parseDateFromQueryParam(paramValues.get(0));
+                            final String str = paramValues.get(0);
+                            try {
+                                stopTime = StopTimeParameter.forUriValue(str);
+                            } catch (IllegalArgumentException e) {
+                                throw new RestconfDocumentedException("Invalid stop-time date: " + str, e);
+                            }
                             break;
                         default:
                             throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
                     }
                 } else if (paramName.equals(FilterParameter.uriName())) {
                     if (!paramValues.isEmpty()) {
-                        // FIXME: use FilterParameter
-                        filter = paramValues.get(0);
+                        filter = FilterParameter.forUriValue(paramValues.get(0));
                     }
                 } else if (paramName.equals("odl-skip-notification-data")) {
                     switch (paramValues.size()) {
@@ -243,26 +233,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
                 throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
             }
 
-            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 uriValue Start-time or stop-time as string in {@link DateAndTime} format.
-         * @return Parsed {@link Instant} by entry.
-         */
-        private static @NonNull Instant parseDateFromQueryParam(final String uriValue) {
-            final TemporalAccessor accessor;
-            try {
-                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 new NotificationQueryParams(startTime, stopTime, filter, skipNotificationData);
         }
 
         /**
@@ -270,7 +241,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
          *
          * @return start-time
          */
-        public @NonNull Instant startTime() {
+        public @Nullable StartTimeParameter startTime() {
             return startTime;
         }
 
@@ -279,7 +250,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
          *
          * @return stop-time
          */
-        public @Nullable Instant stopTime() {
+        public @Nullable StopTimeParameter stopTime() {
             return stopTime;
         }
 
@@ -288,7 +259,7 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
          *
          * @return filter
          */
-        public @Nullable String filter() {
+        public @Nullable FilterParameter filter() {
             return filter;
         }
 
index e9ac883752a3fbf64d36c76c724e000405a372b9..6d872c8227b60e93adc20e2d26967db4c3383fd9 100644 (file)
@@ -140,7 +140,7 @@ abstract class SubscribeToStreamUtil {
         notificationListenerAdapter.setCloseVars(dataBroker, handlersHolder.getSchemaHandler());
         final MapEntryNode mapToStreams = RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
                     notificationListenerAdapter.getSchemaPath().lastNodeIdentifier(),
-                    schemaContext.getNotifications(), notificationQueryParams.startTime(),
+                    schemaContext.getNotifications(), notificationListenerAdapter.getStart(),
                     notificationListenerAdapter.getOutputType(), uri);
 
         // FIXME: how does this correlate with the transaction notificationListenerAdapter.close() will do?
@@ -200,7 +200,7 @@ abstract class SubscribeToStreamUtil {
 
         final MapEntryNode mapToStreams =
             RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(listener.getPath(),
-                notificationQueryParams.startTime(), listener.getOutputType(), uri, schemaContext, serializedPath);
+                listener.getStart(), listener.getOutputType(), uri, schemaContext, serializedPath);
         final DOMDataTreeWriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeDataToDS(writeTransaction, mapToStreams);
         submitData(writeTransaction);
index 1e7ad2b5dffb95f76bc80731376d0ca6be9fe9a2..d9248e3ccefc6d1a1aff1bc0a22a110eafff9e3c 100644 (file)
@@ -7,25 +7,43 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
 
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.VisibleForTesting;
 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 javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 
 /**
  * Features of query parameters part of both notifications.
  */
 abstract class AbstractQueryParams extends AbstractNotificationsData {
+    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();
+
     // FIXME: these should be final
-    private Instant startTime = null;
-    private Instant stopTime = null;
+    private Instant start = null;
+    private Instant stop = null;
     private boolean leafNodesOnly = false;
     private boolean skipNotificationData = false;
 
-    @VisibleForTesting
     public final Instant getStart() {
-        return startTime;
+        return start;
     }
 
     /**
@@ -36,16 +54,40 @@ abstract class AbstractQueryParams extends AbstractNotificationsData {
      * @param filter        Indicates which subset of all possible events are of interest.
      * @param leafNodesOnly If TRUE, notifications will contain changes of leaf nodes only.
      */
-    public abstract void setQueryParams(Instant startTime, Instant stopTime, String filter,
-            boolean leafNodesOnly, boolean skipNotificationData);
-
     @SuppressWarnings("checkstyle:hiddenField")
-    final void setQueryParams(final Instant startTime, final Instant stopTime, final boolean leafNodesOnly,
-            final boolean skipNotificationData) {
-        this.startTime = requireNonNull(startTime);
-        this.stopTime = stopTime;
+    public final void setQueryParams(final StartTimeParameter startTime, final StopTimeParameter stopTime,
+            final FilterParameter filter, final boolean leafNodesOnly, final boolean skipNotificationData) {
+        start = startTime == null ? Instant.now() : parseDateAndTime(startTime.value());
+        stop = stopTime == null ? null : parseDateAndTime(stopTime.value());
         this.leafNodesOnly = leafNodesOnly;
         this.skipNotificationData = skipNotificationData;
+
+        if (filter != null) {
+            try {
+                setFilter(filter.uriValue());
+            } catch (XPathExpressionException e) {
+                throw new IllegalArgumentException("Failed to get filter", e);
+            }
+        }
+    }
+
+    abstract void setFilter(@Nullable String xpathString) throws XPathExpressionException;
+
+    /**
+     * Parse input of query parameters - start-time or stop-time - from {@link DateAndTime} format
+     * to {@link Instant} format.
+     *
+     * @param uriValue Start-time or stop-time as string in {@link DateAndTime} format.
+     * @return Parsed {@link Instant} by entry.
+     */
+    private static @NonNull Instant parseDateAndTime(final DateAndTime dateAndTime) {
+        final TemporalAccessor accessor;
+        try {
+            accessor = FORMATTER.parse(dateAndTime.getValue());
+        } catch (final DateTimeParseException e) {
+            throw new RestconfDocumentedException("Cannot parse of value in date: " + dateAndTime, e);
+        }
+        return Instant.from(accessor);
     }
 
     /**
@@ -68,20 +110,20 @@ abstract class AbstractQueryParams extends AbstractNotificationsData {
 
     @SuppressWarnings("checkstyle:IllegalCatch")
     <T extends BaseListenerInterface> boolean checkStartStop(final Instant now, final T listener) {
-        if (stopTime != null) {
-            if (startTime.compareTo(now) < 0 && stopTime.compareTo(now) > 0) {
+        if (stop != null) {
+            if (start.compareTo(now) < 0 && stop.compareTo(now) > 0) {
                 return true;
             }
-            if (stopTime.compareTo(now) < 0) {
+            if (stop.compareTo(now) < 0) {
                 try {
                     listener.close();
                 } catch (final Exception e) {
                     throw new RestconfDocumentedException("Problem with unregister listener." + e);
                 }
             }
-        } else if (startTime != null) {
-            if (startTime.compareTo(now) < 0) {
-                startTime = null;
+        } else if (start != null) {
+            if (start.compareTo(now) < 0) {
+                start = null;
                 return true;
             }
         } else {
index 416a4c62711b4a45e508ca08cbb2d90507610f14..8094d7be44ee662939d3102ecb3a75e6436a9f33 100644 (file)
@@ -78,20 +78,10 @@ public class ListenerAdapter extends AbstractCommonSubscriber implements Cluster
         }
     }
 
-    private DataTreeCandidateFormatter getFormatter(final String filter) throws XPathExpressionException {
-        final DataTreeCandidateFormatterFactory factory = getFormatterFactory();
-        return filter == null || filter.isEmpty() ? factory.getFormatter() : factory.getFormatter(filter);
-    }
-
     @Override
-    public void setQueryParams(final Instant startTime, final Instant stopTime, final String filter,
-                               final boolean leafNodesOnly, final boolean skipNotificationData) {
-        setQueryParams(startTime, stopTime, leafNodesOnly, skipNotificationData);
-        try {
-            formatter = getFormatter(filter);
-        } catch (final XPathExpressionException e) {
-            throw new IllegalArgumentException("Failed to get filter", e);
-        }
+    final void setFilter(final String filter) throws XPathExpressionException {
+        final DataTreeCandidateFormatterFactory factory = getFormatterFactory();
+        formatter = filter == null || filter.isEmpty() ? factory.getFormatter() : factory.getFormatter(filter);
     }
 
     @Override
index ce76d77ed5d86f4146f3c9efc38fc0f54204c790..3fc2c7af4708fb6c7174a657e82d58629dad91f9 100644 (file)
@@ -15,6 +15,7 @@ import com.google.common.base.MoreObjects;
 import java.time.Instant;
 import java.util.Optional;
 import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
@@ -74,20 +75,10 @@ public class NotificationListenerAdapter extends AbstractCommonSubscriber implem
         }
     }
 
-    private NotificationFormatter getFormatter(final String filter) throws XPathExpressionException {
-        NotificationFormatterFactory factory = getFormatterFactory();
-        return filter == null || filter.isEmpty() ? factory.getFormatter() : factory.getFormatter(filter);
-    }
-
     @Override
-    public void setQueryParams(final Instant startTime, final Instant stopTime, final String filter,
-                               final boolean leafNodesOnly, final boolean skipNotificationData) {
-        setQueryParams(startTime, stopTime, leafNodesOnly, skipNotificationData);
-        try {
-            formatter = getFormatter(filter);
-        } catch (XPathExpressionException e) {
-            throw new IllegalArgumentException("Failed to get filter", e);
-        }
+    final void setFilter(final @Nullable String filter) throws XPathExpressionException {
+        final NotificationFormatterFactory factory = getFormatterFactory();
+        formatter = filter == null || filter.isEmpty() ? factory.getFormatter() : factory.getFormatter(filter);
     }
 
     /**
index 6d8999c3eaf6d3211fd2f66c8a5f6120eb99f3eb..aa1bab668206a88070252ff53c12ce50fe2b6317 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
 
-import static java.time.Instant.EPOCH;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
@@ -35,6 +34,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.StartTimeParameter;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
@@ -120,12 +120,13 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
                               final NotificationOutputTypeGrouping.NotificationOutputType outputType,
                               final boolean leafNodesOnly, final boolean skipNotificationData) {
             super(path, streamName, outputType);
-            setQueryParams(EPOCH, null, null, leafNodesOnly, skipNotificationData);
+            setQueryParams(StartTimeParameter.forUriValue("1970-01-01T00:00:00Z"), null, null, leafNodesOnly,
+                skipNotificationData);
         }
 
         @Override
         protected void post(final String data) {
-            this.lastNotification = data;
+            lastNotification = data;
             notificationLatch.countDown();
         }
 
@@ -140,11 +141,11 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
             LOG.info("Comparing: \n{}\n{}", json, withFakeDate);
 
             JSONAssert.assertEquals(json, withFakeDate, false);
-            this.lastNotification = null;
+            lastNotification = null;
             notificationLatch = new CountDownLatch(1);
         }
 
-        public void assertXmlSimilar(String xml) {
+        public void assertXmlSimilar(final String xml) {
             awaitUntillNotification(xml);
 
             LOG.info("lastNotification: {}", lastNotification);
@@ -152,11 +153,11 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
             LOG.info("Comparing: \n{}\n{}", xml, withFakeDate);
 
             XmlAssert.assertThat(xml).and(withFakeDate).ignoreWhitespace().ignoreChildNodesOrder().areSimilar();
-            this.lastNotification = null;
+            lastNotification = null;
             notificationLatch = new CountDownLatch(1);
         }
 
-        public String awaitUntillNotification(String xml) {
+        public String awaitUntillNotification(final String xml) {
             // FIXME: use awaitility
             if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
                 fail("Timed out waiting for notification for: " + xml);
@@ -180,7 +181,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         return doc.toString();
     }
 
-    static String withFakeXmlDate(String in) {
+    static String withFakeXmlDate(final String in) {
         return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
     }