Commit 46fe373a by Johannes Edmeier

replace long timestamps with instant

parent 562e402f
......@@ -22,7 +22,7 @@ import de.codecentric.boot.admin.server.notify.LoggingNotifier;
import de.codecentric.boot.admin.server.notify.RemindingNotifier;
import de.codecentric.boot.admin.server.notify.filter.FilteringNotifier;
import java.util.concurrent.TimeUnit;
import java.time.Duration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
......@@ -84,7 +84,7 @@ public class SpringBootAdminApplication {
@Primary
public RemindingNotifier remindingNotifier() {
RemindingNotifier notifier = new RemindingNotifier(filteringNotifier(), repository);
notifier.setReminderPeriod(TimeUnit.SECONDS.toMillis(10));
notifier.setReminderPeriod(Duration.ofMinutes(10));
return notifier;
}
......
......@@ -22,7 +22,7 @@ import de.codecentric.boot.admin.server.notify.LoggingNotifier;
import de.codecentric.boot.admin.server.notify.RemindingNotifier;
import de.codecentric.boot.admin.server.notify.filter.FilteringNotifier;
import java.util.concurrent.TimeUnit;
import java.time.Duration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
......@@ -92,7 +92,7 @@ public class SpringBootAdminApplication {
@Primary
public RemindingNotifier remindingNotifier() {
RemindingNotifier notifier = new RemindingNotifier(filteringNotifier(), repository);
notifier.setReminderPeriod(TimeUnit.SECONDS.toMillis(10));
notifier.setReminderPeriod(Duration.ofMinutes(10));
return notifier;
}
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -30,6 +30,7 @@ import de.codecentric.boot.admin.server.domain.values.Registration;
import de.codecentric.boot.admin.server.domain.values.StatusInfo;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
......@@ -53,21 +54,20 @@ public class Instance implements Serializable {
private final Registration registration;
private final boolean registered;
private final StatusInfo statusInfo;
private final long statusTimestamp;
private final Instant statusTimestamp;
private final Info info;
private final List<InstanceEvent> unsavedEvents;
private final Endpoints endpoints;
private Instance(InstanceId id) {
this(id, -1L, null, false, StatusInfo.ofUnknown(), -1L, Info.empty(), Endpoints.empty(), emptyList());
this(id, -1L, null, false, StatusInfo.ofUnknown(), Instant.EPOCH, Info.empty(), Endpoints.empty(), emptyList());
}
private Instance(InstanceId id,
long version,
Registration registration,
boolean registered,
StatusInfo statusInfo,
long statusTimestamp,
StatusInfo statusInfo, Instant statusTimestamp,
Info info,
Endpoints endpoints,
List<InstanceEvent> unsavedEvents) {
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,6 +18,7 @@ package de.codecentric.boot.admin.server.domain.events;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import java.io.Serializable;
import java.time.Instant;
/**
* Abstract Event regarding registered instances
......@@ -29,13 +30,13 @@ public abstract class InstanceEvent implements Serializable {
private static final long serialVersionUID = 1L;
private final InstanceId instance;
private final long version;
private final long timestamp;
private final Instant timestamp;
private final String type;
protected InstanceEvent(InstanceId instance, long version, String type) {
this.instance = instance;
this.version = version;
this.timestamp = System.currentTimeMillis();
this.timestamp = Instant.now();
this.type = type;
}
}
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -33,13 +33,13 @@ import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.Comparator.comparingLong;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;
public abstract class ConcurrentMapEventStore extends InstanceEventPublisher implements InstanceEventStore {
private static final Logger log = LoggerFactory.getLogger(ConcurrentMapEventStore.class);
private static final Comparator<InstanceEvent> byTimestampAndIdAndVersion = comparingLong(
private static final Comparator<InstanceEvent> byTimestampAndIdAndVersion = comparing(
InstanceEvent::getTimestamp).thenComparing(InstanceEvent::getInstance)
.thenComparing(InstanceEvent::getVersion);
private final int maxLogSizePerAggregate;
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -23,10 +23,11 @@ import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.springframework.util.Assert;
/**
......@@ -36,7 +37,7 @@ import org.springframework.util.Assert;
*/
public class RemindingNotifier extends AbstractEventNotifier {
private final ConcurrentHashMap<InstanceId, Reminder> reminders = new ConcurrentHashMap<>();
private long reminderPeriod = TimeUnit.MINUTES.toMillis(10L);
private Duration reminderPeriod = Duration.ofMinutes(10);
private String[] reminderStatuses = {"DOWN", "OFFLINE"};
private final Notifier delegate;
......@@ -58,9 +59,9 @@ public class RemindingNotifier extends AbstractEventNotifier {
}
public void sendReminders() {
long now = System.currentTimeMillis();
Instant now = Instant.now();
for (Reminder reminder : new ArrayList<>(reminders.values())) {
if (now - reminder.getLastNotification() > reminderPeriod) {
if (reminder.getLastNotification().plus(reminderPeriod).isBefore(now)) {
reminder.setLastNotification(now);
delegate.notify(reminder.getEvent());
}
......@@ -86,7 +87,7 @@ public class RemindingNotifier extends AbstractEventNotifier {
return false;
}
public void setReminderPeriod(long reminderPeriod) {
public void setReminderPeriod(Duration reminderPeriod) {
this.reminderPeriod = reminderPeriod;
}
......@@ -98,18 +99,18 @@ public class RemindingNotifier extends AbstractEventNotifier {
private static class Reminder {
private final InstanceEvent event;
private long lastNotification;
private Instant lastNotification;
private Reminder(InstanceEvent event) {
this.event = event;
this.lastNotification = event.getTimestamp();
}
public void setLastNotification(long lastNotification) {
public void setLastNotification(Instant lastNotification) {
this.lastNotification = lastNotification;
}
public long getLastNotification() {
public Instant getLastNotification() {
return lastNotification;
}
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,10 +18,12 @@ package de.codecentric.boot.admin.server.notify.filter;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import java.time.Instant;
public class ApplicationNameNotificationFilter extends ExpiringNotificationFilter {
private final String name;
public ApplicationNameNotificationFilter(String name, long expiry) {
public ApplicationNameNotificationFilter(String name, Instant expiry) {
super(expiry);
this.name = name;
}
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,15 +18,17 @@ package de.codecentric.boot.admin.server.notify.filter;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import java.time.Instant;
public abstract class ExpiringNotificationFilter implements NotificationFilter {
private final long expiry;
private final Instant expiry;
public ExpiringNotificationFilter(long expiry) {
public ExpiringNotificationFilter(Instant expiry) {
this.expiry = expiry;
}
public boolean isExpired() {
return expiry >= 0 && expiry < System.currentTimeMillis();
return expiry != null && expiry.isBefore(Instant.now());
}
@Override
......@@ -36,7 +38,7 @@ public abstract class ExpiringNotificationFilter implements NotificationFilter {
protected abstract boolean doFilter(InstanceEvent event, Instance instance);
public long getExpiry() {
public Instant getExpiry() {
return expiry;
}
}
\ No newline at end of file
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -23,6 +23,7 @@ import de.codecentric.boot.admin.server.notify.Notifier;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
......@@ -43,7 +44,7 @@ public class FilteringNotifier extends AbstractEventNotifier {
private static final Logger LOGGER = LoggerFactory.getLogger(FilteringNotifier.class);
private final ConcurrentMap<String, NotificationFilter> filters = new ConcurrentHashMap<>();
private final Notifier delegate;
private long lastCleanup;
private Instant lastCleanup = Instant.EPOCH;
private Duration cleanupInterval = Duration.ofSeconds(10);
private AtomicLong counter = new AtomicLong();
......@@ -79,8 +80,8 @@ public class FilteringNotifier extends AbstractEventNotifier {
}
private void cleanUp() {
long now = System.currentTimeMillis();
if (lastCleanup + cleanupInterval.toMillis() > now) {
Instant now = Instant.now();
if (lastCleanup.plus(cleanupInterval).isAfter(now)) {
return;
}
lastCleanup = now;
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -19,10 +19,12 @@ import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import java.time.Instant;
public class InstanceIdNotificationFilter extends ExpiringNotificationFilter {
private final InstanceId id;
public InstanceIdNotificationFilter(InstanceId id, long expiry) {
public InstanceIdNotificationFilter(InstanceId id, Instant expiry) {
super(expiry);
this.id = id;
}
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -22,6 +22,8 @@ import de.codecentric.boot.admin.server.notify.filter.InstanceIdNotificationFilt
import de.codecentric.boot.admin.server.notify.filter.NotificationFilter;
import de.codecentric.boot.admin.server.web.AdminController;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import org.springframework.http.ResponseEntity;
......@@ -57,7 +59,7 @@ public class NotificationFilterController {
@PostMapping(path = "/notifications/filters", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addFilter(@RequestParam(name = "id", required = false) String id,
@RequestParam(name = "name", required = false) String name,
@RequestParam(name = "ttl", required = false, defaultValue = "-1") long ttl) {
@RequestParam(name = "ttl", required = false) Duration ttl) {
if (hasText(id) || hasText(name)) {
NotificationFilter filter = createFilter(hasText(id) ? InstanceId.of(id) : null, name, ttl);
String filterId = filteringNotifier.addFilter(filter);
......@@ -77,8 +79,8 @@ public class NotificationFilterController {
}
}
private NotificationFilter createFilter(InstanceId id, String name, long ttl) {
long expiry = ttl > 0L ? System.currentTimeMillis() + ttl : ttl;
private NotificationFilter createFilter(InstanceId id, String name, Duration ttl) {
Instant expiry = ttl != null ? Instant.now().plus(ttl) : null;
return id != null ?
new InstanceIdNotificationFilter(id, expiry) :
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -26,6 +26,7 @@ import reactor.core.scheduler.Schedulers;
import reactor.retry.Retry;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
......@@ -36,7 +37,7 @@ import org.slf4j.LoggerFactory;
public class StatusUpdateTrigger extends ResubscribingEventHandler<InstanceRegisteredEvent> {
private static final Logger log = LoggerFactory.getLogger(StatusUpdateTrigger.class);
private final StatusUpdater statusUpdater;
private Map<InstanceId, Long> lastQueried = new HashMap<>();
private Map<InstanceId, Instant> lastQueried = new HashMap<>();
private Duration updateInterval = Duration.ofSeconds(10);
private Duration statusLifetime = Duration.ofSeconds(10);
private Disposable intervalSubscription;
......@@ -79,17 +80,15 @@ public class StatusUpdateTrigger extends ResubscribingEventHandler<InstanceRegis
protected Mono<Void> updateStatusForAllInstances() {
log.debug("Updating status for all instances");
long expiryInstant = System.currentTimeMillis() - statusLifetime.toMillis();
return Flux.fromIterable(lastQueried.entrySet())
.filter(e -> e.getValue() < expiryInstant)
Instant expiryInstant = Instant.now().minus(statusLifetime);
return Flux.fromIterable(lastQueried.entrySet()).filter(e -> e.getValue().isBefore(expiryInstant))
.map(Map.Entry::getKey)
.flatMap(this::updateStatus)
.then();
}
protected Mono<Void> updateStatus(InstanceId instanceId) {
return statusUpdater.updateStatus(instanceId)
.doFinally((s) -> lastQueried.put(instanceId, System.currentTimeMillis()));
return statusUpdater.updateStatus(instanceId).doFinally((s) -> lastQueried.put(instanceId, Instant.now()));
}
public void setUpdateInterval(Duration updateInterval) {
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -26,6 +26,7 @@ import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
......@@ -100,7 +101,7 @@ public class ApplicationsController {
Application group = new Application(name);
group.setInstances(instanceList);
group.setVersion(getVersion(instanceList));
Tuple2<String, Long> status = getStatus(instanceList);
Tuple2<String, Instant> status = getStatus(instanceList);
group.setStatus(status.getT1());
group.setStatusTimestamp(status.getT2());
return group;
......@@ -123,24 +124,24 @@ public class ApplicationsController {
}
}
protected Tuple2<String, Long> getStatus(List<Instance> instances) {
protected Tuple2<String, Instant> getStatus(List<Instance> instances) {
//TODO: Correct is just a second readmodel for groups
Map<String, Long> statusWithTime = instances.stream()
Map<String, Instant> statusWithTime = instances.stream()
.collect(toMap(instance -> instance.getStatusInfo().getStatus(),
Instance::getStatusTimestamp, Math::min));
Instance::getStatusTimestamp, this::getMax));
if (statusWithTime.size() == 1) {
Map.Entry<String, Long> e = statusWithTime.entrySet().iterator().next();
Map.Entry<String, Instant> e = statusWithTime.entrySet().iterator().next();
return Tuples.of(e.getKey(), e.getValue());
}
if (statusWithTime.containsKey(StatusInfo.STATUS_UP)) {
Long oldestNonUp = statusWithTime.entrySet()
Instant oldestNonUp = statusWithTime.entrySet()
.stream()
.filter(e -> !StatusInfo.STATUS_UP.equals(e.getKey()))
.map(Map.Entry::getValue)
.min(naturalOrder())
.orElse(-1L);
long latest = Math.max(oldestNonUp, statusWithTime.getOrDefault(StatusInfo.STATUS_UP, -1L));
.orElse(Instant.EPOCH);
Instant latest = getMax(oldestNonUp, statusWithTime.getOrDefault(StatusInfo.STATUS_UP, Instant.EPOCH));
return Tuples.of(StatusInfo.STATUS_RESTRICTED, latest);
}
......@@ -148,7 +149,11 @@ public class ApplicationsController {
.stream()
.min(Map.Entry.comparingByKey(StatusInfo.severity()))
.map(e -> Tuples.of(e.getKey(), e.getValue()))
.orElse(Tuples.of(StatusInfo.STATUS_UNKNOWN, -1L));
.orElse(Tuples.of(StatusInfo.STATUS_UNKNOWN, Instant.EPOCH));
}
private Instant getMax(Instant t1, Instant t2) {
return t1.compareTo(t2) >= 0 ? t1 : t2;
}
@SuppressWarnings("unchecked")
......@@ -161,7 +166,7 @@ public class ApplicationsController {
private final String name;
private String version;
private String status;
private long statusTimestamp;
private Instant statusTimestamp;
private List<Instance> instances;
}
}
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -26,6 +26,7 @@ import de.codecentric.boot.admin.server.domain.values.StatusInfo;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.Duration;
import org.junit.Before;
import org.junit.Test;
......@@ -49,7 +50,7 @@ public class RemindingNotifierTest {
private InstanceRepository repository;
@Before
public void setUp() throws Exception {
public void setUp() {
repository = mock(InstanceRepository.class);
when(repository.find(instance1.getId())).thenReturn(Mono.just(instance1));
when(repository.find(instance2.getId())).thenReturn(Mono.just(instance2));
......@@ -61,10 +62,10 @@ public class RemindingNotifierTest {
}
@Test
public void test_remind() throws Exception {
public void test_remind() {
TestNotifier notifier = new TestNotifier();
RemindingNotifier reminder = new RemindingNotifier(notifier, repository);
reminder.setReminderPeriod(-1000L);
reminder.setReminderPeriod(Duration.ZERO);
StepVerifier.create(reminder.notify(appDown)).verifyComplete();
StepVerifier.create(reminder.notify(otherAppUp)).verifyComplete();
......@@ -76,10 +77,10 @@ public class RemindingNotifierTest {
}
@Test
public void test_no_remind_after_up() throws Exception {
public void test_no_remind_after_up() {
TestNotifier notifier = new TestNotifier();
RemindingNotifier reminder = new RemindingNotifier(notifier, repository);
reminder.setReminderPeriod(0L);
reminder.setReminderPeriod(Duration.ZERO);
StepVerifier.create(reminder.notify(appDown)).verifyComplete();
StepVerifier.create(reminder.notify(appUp)).verifyComplete();
......@@ -89,10 +90,10 @@ public class RemindingNotifierTest {
}
@Test
public void test_no_remind_before_end() throws Exception {
public void test_no_remind_before_end() {
TestNotifier notifier = new TestNotifier();
RemindingNotifier reminder = new RemindingNotifier(notifier, repository);
reminder.setReminderPeriod(Long.MAX_VALUE);
reminder.setReminderPeriod(Duration.ofHours(24));
StepVerifier.create(reminder.notify(appDown)).verifyComplete();
reminder.sendReminders();
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -26,6 +26,7 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.Duration;
import java.time.Instant;
import org.junit.Before;
import org.junit.Test;
......@@ -56,8 +57,9 @@ public class FilteringNotifierTest {
FilteringNotifier notifier = new FilteringNotifier(new TestNotifier(), repository);
notifier.setCleanupInterval(Duration.ZERO);
String id1 = notifier.addFilter(new ApplicationNameNotificationFilter("foo", 0L));
String id2 = notifier.addFilter(new ApplicationNameNotificationFilter("bar", -1L));
String id1 = notifier.addFilter(
new ApplicationNameNotificationFilter("foo", Instant.now().minus(Duration.ofSeconds(1))));
String id2 = notifier.addFilter(new ApplicationNameNotificationFilter("bar", null));
assertThat(notifier.getNotificationFilters()).containsKey(id1).containsKey(id2);
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -28,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class InstanceIdNotificationFilterTest {
@Test
public void test_filterByName() {
NotificationFilter filter = new InstanceIdNotificationFilter(InstanceId.of("cafebabe"), -1L);
NotificationFilter filter = new InstanceIdNotificationFilter(InstanceId.of("cafebabe"), null);
Instance filteredInstance = Instance.create(InstanceId.of("cafebabe"))
.register(Registration.create("foo", "http://health").build());
......
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -21,6 +21,8 @@ import de.codecentric.boot.admin.server.domain.events.InstanceRegisteredEvent;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
......@@ -30,7 +32,7 @@ public class InstanceNameNotificationFilterTest {
@Test
public void test_filterByName() {
NotificationFilter filter = new ApplicationNameNotificationFilter("foo", -1L);
NotificationFilter filter = new ApplicationNameNotificationFilter("foo", null);
Instance filteredInstance = Instance.create(InstanceId.of("-"))
.register(Registration.create("foo", "http://health").build());
......@@ -47,16 +49,17 @@ public class InstanceNameNotificationFilterTest {
@Test
public void test_expiry() throws InterruptedException {
ExpiringNotificationFilter filterForever = new ApplicationNameNotificationFilter("foo", -1L);
ExpiringNotificationFilter filterExpired = new ApplicationNameNotificationFilter("foo", 0L);
ExpiringNotificationFilter filterForever = new ApplicationNameNotificationFilter("foo", null);
ExpiringNotificationFilter filterExpired = new ApplicationNameNotificationFilter("foo",
Instant.now().minus(Duration.ofSeconds(1)));
ExpiringNotificationFilter filterLong = new ApplicationNameNotificationFilter("foo",
System.currentTimeMillis() + 500L);
Instant.now().plus(Duration.ofMillis(100)));
assertThat(filterForever.isExpired()).isFalse();
assertThat(filterLong.isExpired()).isFalse();
assertThat(filterExpired.isExpired()).isTrue();
TimeUnit.MILLISECONDS.sleep(501);
TimeUnit.MILLISECONDS.sleep(200);
assertThat(filterLong.isExpired()).isTrue();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment