apply new code formatter and save action

This commit is contained in:
2019-11-24 10:20:43 +01:00
parent 5ea82c6a4c
commit 06b379494f
184 changed files with 13455 additions and 13489 deletions

View File

@@ -15,144 +15,143 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CollectionUtils {
public interface Compare<T> {
public boolean test(T valueA);
public interface Compare<T> {
public static <T, V> Compare<T> compare(Function<? super T, ? extends V> keyExtractor, V value) {
Objects.requireNonNull(keyExtractor);
return t -> Objects.equals(keyExtractor.apply(t), value);
}
public boolean test(T valueA);
default Compare<T> thenCompare(Compare<? super T> other) {
Objects.requireNonNull(other);
return t -> {
final boolean res = test(t);
return res ? other.test(t) : false;
};
}
default <V> Compare<T> thenCompare(Function<T, ? extends V> keyExtractor, V value) {
return thenCompare(compare(keyExtractor, value));
}
}
public static <T> List<T> copySort(Collection<? extends T> collection, Comparator<T> comparator){
final List<T> result = new ArrayList<T>(collection);
Collections.sort(result, comparator);
return result;
}
public static <T, R extends T> void mapInPlace(final List<T> list, final Function<T, R> mapper) {
for (int i = 0; i < list.size(); i++) {
final T value = list.get(i);
final T newValue = mapper.apply(value);
list.set(i, newValue);
}
}
public static <T, R> List<R> map(final Collection<T> list, final Function<T, R> mapper) {
final List<R> result = new ArrayList<>(list.size());
for (final T t : list) {
result.add(mapper.apply(t));
}
return result;
}
public static <T, R> List<R> map(final T[] input, final Function<T, R> mapper) {
return Stream.of(input).map(mapper).collect(Collectors.toList());
}
public static <O extends Collection<R>, T, R> O map(final Collection<T> input, final O result,
final Function<T, R> mapper) {
for (final T t : input) {
final R e = mapper.apply(t);
result.add(e);
}
return result;
}
public static <T, V> Map<T, V> createMapFromValues(final Iterable<V> iterable, final Function<V, T> keyMapper) {
final Map<T, V> result = new HashMap<>();
for (final V value : iterable) {
final T key = keyMapper.apply(value);
result.put(key, value);
}
return result;
}
public static <KEY, VALUE> Map<KEY, VALUE> createMapFromKeys(final Iterable<KEY> iterable,
final Function<KEY, VALUE> valueMapper) {
final Map<KEY, VALUE> result = new HashMap<>();
for (final KEY key : iterable) {
final VALUE value = valueMapper.apply(key);
result.put(key, value);
}
return result;
}
public static <T> List<T> filter(final Collection<T> collection, final Predicate<T> predicate) {
return collection.stream().filter(predicate).collect(Collectors.toList());
}
public static <T> int indexOf(final List<T> list, final Predicate<T> predicate) {
for (int i = 0; i < list.size(); i++) {
if (predicate.test(list.get(i))) {
return i;
}
}
return -1;
}
public static <T> boolean contains(Collection<T> collection, final Compare<T> compare) {
for (T t : collection) {
boolean found = compare.test(t);
if (found ) {
return true;
public static <T, V> Compare<T> compare(Function<? super T, ? extends V> keyExtractor, V value) {
Objects.requireNonNull(keyExtractor);
return t -> Objects.equals(keyExtractor.apply(t), value);
}
}
return false;
}
public static <T> long count(Collection<T> collection, final Compare<T> compare) {
long count = 0;
for (T t : collection) {
boolean found = compare.test(t);
if (found ) {
count++;
default Compare<T> thenCompare(Compare<? super T> other) {
Objects.requireNonNull(other);
return t -> {
final boolean res = test(t);
return res ? other.test(t) : false;
};
}
default <V> Compare<T> thenCompare(Function<T, ? extends V> keyExtractor, V value) {
return thenCompare(compare(keyExtractor, value));
}
}
return count;
}
public static <V, T extends Collection<V>> T removeAll(final T collection, final T remove,
final Supplier<T> generator) {
public static <T> List<T> copySort(Collection<? extends T> collection, Comparator<T> comparator) {
final List<T> result = new ArrayList<T>(collection);
Collections.sort(result, comparator);
return result;
}
final T result = generator.get();
result.addAll(collection);
result.removeAll(remove);
return result;
}
public static <T, R extends T> void mapInPlace(final List<T> list, final Function<T, R> mapper) {
for (int i = 0; i < list.size(); i++) {
final T value = list.get(i);
final T newValue = mapper.apply(value);
list.set(i, newValue);
}
}
public static <V, T extends Collection<V>> T retainAll(final T collection, final T retain,
final Supplier<T> generator) {
public static <T, R> List<R> map(final Collection<T> list, final Function<T, R> mapper) {
final List<R> result = new ArrayList<>(list.size());
final T result = generator.get();
result.addAll(collection);
result.retainAll(retain);
return result;
}
for (final T t : list) {
result.add(mapper.apply(t));
}
return result;
}
public static <T, R> List<R> map(final T[] input, final Function<T, R> mapper) {
return Stream.of(input).map(mapper).collect(Collectors.toList());
}
public static <O extends Collection<R>, T, R> O map(final Collection<T> input, final O result,
final Function<T, R> mapper) {
for (final T t : input) {
final R e = mapper.apply(t);
result.add(e);
}
return result;
}
public static <T, V> Map<T, V> createMapFromValues(final Iterable<V> iterable, final Function<V, T> keyMapper) {
final Map<T, V> result = new HashMap<>();
for (final V value : iterable) {
final T key = keyMapper.apply(value);
result.put(key, value);
}
return result;
}
public static <KEY, VALUE> Map<KEY, VALUE> createMapFromKeys(final Iterable<KEY> iterable,
final Function<KEY, VALUE> valueMapper) {
final Map<KEY, VALUE> result = new HashMap<>();
for (final KEY key : iterable) {
final VALUE value = valueMapper.apply(key);
result.put(key, value);
}
return result;
}
public static <T> List<T> filter(final Collection<T> collection, final Predicate<T> predicate) {
return collection.stream().filter(predicate).collect(Collectors.toList());
}
public static <T> int indexOf(final List<T> list, final Predicate<T> predicate) {
for (int i = 0; i < list.size(); i++) {
if (predicate.test(list.get(i))) {
return i;
}
}
return -1;
}
public static <T> boolean contains(Collection<T> collection, final Compare<T> compare) {
for (T t : collection) {
boolean found = compare.test(t);
if (found) {
return true;
}
}
return false;
}
public static <T> long count(Collection<T> collection, final Compare<T> compare) {
long count = 0;
for (T t : collection) {
boolean found = compare.test(t);
if (found) {
count++;
}
}
return count;
}
public static <V, T extends Collection<V>> T removeAll(final T collection, final T remove,
final Supplier<T> generator) {
final T result = generator.get();
result.addAll(collection);
result.removeAll(remove);
return result;
}
public static <V, T extends Collection<V>> T retainAll(final T collection, final T retain,
final Supplier<T> generator) {
final T result = generator.get();
result.addAll(collection);
result.retainAll(retain);
return result;
}
}

View File

@@ -5,14 +5,14 @@ import java.time.ZoneOffset;
public class DateUtils {
public static OffsetDateTime getDate(final int year, final int month, final int day, final int hour,
final int minute, final int second) {
public static OffsetDateTime getDate(final int year, final int month, final int day, final int hour,
final int minute, final int second) {
final OffsetDateTime result = OffsetDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC);
return result;
}
final OffsetDateTime result = OffsetDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC);
return result;
}
public static OffsetDateTime nowInUtc() {
return OffsetDateTime.now(ZoneOffset.UTC);
}
public static OffsetDateTime nowInUtc() {
return OffsetDateTime.now(ZoneOffset.UTC);
}
}

View File

@@ -4,98 +4,98 @@ import java.text.MessageFormat;
import java.util.Objects;
public class Preconditions {
public static void checkEven(final long value, final String message) {
if (value % 2 != 0) {
throw new IllegalStateException(message + ". Was: " + value);
}
}
public static void checkEven(final long value, final String message) {
if (value % 2 != 0) {
throw new IllegalStateException(message + ". Was: " + value);
}
}
/**
*
* @param a
* @param b
* @param message formatted with {@link MessageFormat}
* @param args
*/
public static void checkGreater(final long a, final long b, final String message, final Object... args) {
/**
*
* @param a
* @param b
* @param message formatted with {@link MessageFormat}
* @param args
*/
public static void checkGreater(final long a, final long b, final String message, final Object... args) {
if (a <= b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " > " + b);
}
}
if (a <= b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " > " + b);
}
}
/**
*
* @param a
* @param b
* @param message formatted with {@link MessageFormat}
* @param args
* @throws IllegalStateException if {@code a} is not greater or equal to
* {@code b}
*/
public static void checkGreaterOrEqual(final long a, final long b, final String message, final Object... args) {
if (a < b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " >= " + b);
}
}
/**
*
* @param a
* @param b
* @param message formatted with {@link MessageFormat}
* @param args
* @throws IllegalStateException if {@code a} is not greater or equal to
* {@code b}
*/
public static void checkGreaterOrEqual(final long a, final long b, final String message, final Object... args) {
if (a < b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " >= " + b);
}
}
public static void checkSmaller(final long a, final long b, final String message, final Object... args) {
if (a >= b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " < " + b);
}
}
public static void checkSmaller(final long a, final long b, final String message, final Object... args) {
if (a >= b) {
throw new IllegalStateException(MessageFormat.format(message, args) + " Expected: " + a + " < " + b);
}
}
public static void checkEqual(final Object actual, final Object expected) {
checkEqual(actual, expected, "expected {0} is equal to {1}", actual, expected);
}
public static void checkEqual(final Object actual, final Object expected) {
checkEqual(actual, expected, "expected {0} is equal to {1}", actual, expected);
}
/**
* Check that the given values are equal. The check is done with
* {@link Objects#equals(Object, Object)}
*
* @param actual the actual value
* @param expected the expected value
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not equal to
* {@code expected}
*/
public static void checkEqual(final Object actual, final Object expected, final String message,
final Object... args) {
if (!Objects.equals(actual, expected)) {
throw new IllegalStateException(
MessageFormat.format(message, args) + " Expected: " + actual + " equals " + expected);
}
}
/**
* Check that the given values are equal. The check is done with
* {@link Objects#equals(Object, Object)}
*
* @param actual the actual value
* @param expected the expected value
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not equal to
* {@code expected}
*/
public static void checkEqual(final Object actual, final Object expected, final String message,
final Object... args) {
if (!Objects.equals(actual, expected)) {
throw new IllegalStateException(
MessageFormat.format(message, args) + " Expected: " + actual + " equals " + expected);
}
}
/**
* Check that the given value is true.
*
* @param actual must be true
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not true
*/
public static void checkTrue(final boolean actual, final String message, final Object... args) {
checkEqual(actual, true, message, args);
}
/**
* Check that the given value is true.
*
* @param actual must be true
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not true
*/
public static void checkTrue(final boolean actual, final String message, final Object... args) {
checkEqual(actual, true, message, args);
}
/**
* Check that the given value is false.
*
* @param actual must be false
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not false
*/
public static void checkFalse(final boolean actual, final String message, final Object... args) {
checkEqual(actual, false, message, args);
}
/**
* Check that the given value is false.
*
* @param actual must be false
* @param message formatted with {@link MessageFormat}
* @param args arguments for the message
* @throws IllegalStateException if {@code actual} is not false
*/
public static void checkFalse(final boolean actual, final String message, final Object... args) {
checkEqual(actual, false, message, args);
}
public static void checkNull(final Object actual, final String message, final Object... args) {
if (actual != null) {
throw new IllegalStateException(MessageFormat.format(message, args));
}
}
public static void checkNull(final Object actual, final String message, final Object... args) {
if (actual != null) {
throw new IllegalStateException(MessageFormat.format(message, args));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,32 +4,32 @@ import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> {
private final LinkedHashMap<K, V> cache;
private final LinkedHashMap<K, V> cache;
public LRUCache(final int maxEntries) {
this.cache = new LinkedHashMap<>(16, 0.75f, true) {
private static final long serialVersionUID = 1L;
public LRUCache(final int maxEntries) {
this.cache = new LinkedHashMap<>(16, 0.75f, true) {
private static final long serialVersionUID = 1L;
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
};
}
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
};
}
public V put(final K key, final V value) {
return cache.put(key, value);
}
public V put(final K key, final V value) {
return cache.put(key, value);
}
public V get(final K key) {
return cache.get(key);
}
public V get(final K key) {
return cache.get(key);
}
public V remove(final K key) {
return cache.remove(key);
}
public V remove(final K key) {
return cache.remove(key);
}
public int size() {
return cache.size();
}
public int size() {
return cache.size();
}
}

View File

@@ -4,10 +4,10 @@ import java.util.concurrent.ExecutionException;
public class RuntimeExcecutionException extends RuntimeException {
private static final long serialVersionUID = -3626851728980513527L;
private static final long serialVersionUID = -3626851728980513527L;
public RuntimeExcecutionException(final ExecutionException e) {
super(e);
}
public RuntimeExcecutionException(final ExecutionException e) {
super(e);
}
}

View File

@@ -26,380 +26,380 @@ import org.testng.annotations.Test;
@Test
public class HotEntryCacheTest {
static {
Configurator.setRootLevel(Level.TRACE);
}
static {
Configurator.setRootLevel(Level.TRACE);
}
private static final Logger LOGGER = LoggerFactory.getLogger(HotEntryCacheTest.class);
private static final Logger LOGGER = LoggerFactory.getLogger(HotEntryCacheTest.class);
private int cacheId = 0;
private int cacheId = 0;
@Test(invocationCount = 1)
public void testRemovalListenerCalledOnExpire() throws InterruptedException {
@Test(invocationCount = 1)
public void testRemovalListenerCalledOnExpire() throws InterruptedException {
LOGGER.info("");
LOGGER.info("");
LOGGER.info("start: testRemovalListenerCalledOnExpire");
LOGGER.info("");
LOGGER.info("");
LOGGER.info("start: testRemovalListenerCalledOnExpire");
final long originalMinSleepPeriod = HotEntryCache.getMinSleepPeriod();
final long originalMaxSleepPeriod = HotEntryCache.getMaxSleepPeriod();
try {
final String key = "key";
final String value = "value";
final CountDownLatch latch = new CountDownLatch(1);
final long originalMinSleepPeriod = HotEntryCache.getMinSleepPeriod();
final long originalMaxSleepPeriod = HotEntryCache.getMaxSleepPeriod();
try {
final String key = "key";
final String value = "value";
final CountDownLatch latch = new CountDownLatch(1);
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofMillis(1), 10,
"cache-" + ++cacheId);
HotEntryCache.setMinSleepPeriod(1);
HotEntryCache.setMaxSleepPeriod(2);
cache.addListener((k, v) -> {
Assert.assertEquals(k, key);
Assert.assertEquals(v, value);
latch.countDown();
});
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofMillis(1), 10,
"cache-" + ++cacheId);
HotEntryCache.setMinSleepPeriod(1);
HotEntryCache.setMaxSleepPeriod(2);
cache.addListener((k, v) -> {
Assert.assertEquals(k, key);
Assert.assertEquals(v, value);
latch.countDown();
});
cache.put(key, value);
final boolean listenerCalled = latch.await(100, TimeUnit.MILLISECONDS);
Assert.assertTrue(listenerCalled, "removal listener called");
} finally {
HotEntryCache.setMinSleepPeriod(originalMinSleepPeriod);
HotEntryCache.setMaxSleepPeriod(originalMaxSleepPeriod);
}
}
cache.put(key, value);
final boolean listenerCalled = latch.await(100, TimeUnit.MILLISECONDS);
Assert.assertTrue(listenerCalled, "removal listener called");
} finally {
HotEntryCache.setMinSleepPeriod(originalMinSleepPeriod);
HotEntryCache.setMaxSleepPeriod(originalMaxSleepPeriod);
}
}
public void testPutAndGet() throws InterruptedException, ExecutionException, TimeoutException {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
public void testPutAndGet() throws InterruptedException, ExecutionException, TimeoutException {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final String replacedNull = cache.put("key", "value1");
Assert.assertEquals(replacedNull, null);
final String replacedNull = cache.put("key", "value1");
Assert.assertEquals(replacedNull, null);
final String cachedValue1 = cache.get("key");
Assert.assertEquals(cachedValue1, "value1");
final String cachedValue1 = cache.get("key");
Assert.assertEquals(cachedValue1, "value1");
final String replacedValue1 = cache.put("key", "value2");
Assert.assertEquals(replacedValue1, "value1");
final String replacedValue1 = cache.put("key", "value2");
Assert.assertEquals(replacedValue1, "value1");
final String cachedValue2 = cache.get("key");
Assert.assertEquals(cachedValue2, "value2");
}
final String cachedValue2 = cache.get("key");
Assert.assertEquals(cachedValue2, "value2");
}
public void testPutTouches() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
public void testPutTouches() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
cache.put("key", "value1");
cache.put("key", "value1");
clock.plusSeconds(2);
cache.updateTime();
clock.plusSeconds(2);
cache.updateTime();
cache.put("key", "value2");
cache.put("key", "value2");
clock.plus(timeToLive.minusSeconds(1));
cache.triggerEvictionAndWait();
// at this point the entry would have been evicted it it was not touched by the
// second put.
clock.plus(timeToLive.minusSeconds(1));
cache.triggerEvictionAndWait();
// at this point the entry would have been evicted it it was not touched by the
// second put.
final String cachedValue2 = cache.get("key");
Assert.assertEquals(cachedValue2, "value2");
final String cachedValue2 = cache.get("key");
Assert.assertEquals(cachedValue2, "value2");
clock.plus(timeToLive.plusSeconds(1));
// time elapsed since the last put: timeToLive +1s
cache.triggerEvictionAndWait();
clock.plus(timeToLive.plusSeconds(1));
// time elapsed since the last put: timeToLive +1s
cache.triggerEvictionAndWait();
final String cachedValue1_evicted = cache.get("key");
Assert.assertEquals(cachedValue1_evicted, null);
}
final String cachedValue1_evicted = cache.get("key");
Assert.assertEquals(cachedValue1_evicted, null);
}
public void testGetTouches() throws Exception {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
public void testGetTouches() throws Exception {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
cache.put("key", "value1");
cache.put("key", "value1");
// skip forward in time, but do not yet trigger eviction
clock.plus(timeToLive.plusMillis(1));
cache.updateTime();
// skip forward in time, but do not yet trigger eviction
clock.plus(timeToLive.plusMillis(1));
cache.updateTime();
cache.get("key"); // will touch the entry
cache.get("key"); // will touch the entry
cache.triggerEvictionAndWait(); // if get didn't touch, then this will evict the entry
cache.triggerEvictionAndWait(); // if get didn't touch, then this will evict the entry
final String cachedValue1 = cache.get("key");
Assert.assertEquals(cachedValue1, "value1");
}
final String cachedValue1 = cache.get("key");
Assert.assertEquals(cachedValue1, "value1");
}
public void testEvictionByBackgroundThread() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
public void testEvictionByBackgroundThread() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
cache.addListener((key, value) -> {
evictionEventFuture.complete(value);
});
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
cache.addListener((key, value) -> {
evictionEventFuture.complete(value);
});
cache.put("key", "value1");
cache.put("key", "value1");
clock.plus(timeToLive.minusSeconds(1));
cache.updateTime();
clock.plus(timeToLive.minusSeconds(1));
cache.updateTime();
cache.put("key2", "value2");
clock.plus(Duration.ofSeconds(1).plusMillis(1));
cache.triggerEvictionAndWait();
cache.put("key2", "value2");
clock.plus(Duration.ofSeconds(1).plusMillis(1));
cache.triggerEvictionAndWait();
final String evictedValue1 = evictionEventFuture.get(5, TimeUnit.MINUTES); // enough time for debugging
Assert.assertEquals(evictedValue1, "value1");
}
final String evictedValue1 = evictionEventFuture.get(5, TimeUnit.MINUTES); // enough time for debugging
Assert.assertEquals(evictedValue1, "value1");
}
public void testRemove() throws InterruptedException, ExecutionException, TimeoutException {
LOGGER.info("");
LOGGER.info("");
LOGGER.info("start: testRemove");
public void testRemove() throws InterruptedException, ExecutionException, TimeoutException {
LOGGER.info("");
LOGGER.info("");
LOGGER.info("start: testRemove");
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final List<String> removedValues = new ArrayList<>();
cache.addListener((key, value) -> removedValues.add(value));
final List<String> removedValues = new ArrayList<>();
cache.addListener((key, value) -> removedValues.add(value));
cache.put("key", "value1");
cache.put("key", "value1");
final String removedValue = cache.remove("key");
Assert.assertEquals(removedValue, "value1");
final String removedValue = cache.remove("key");
Assert.assertEquals(removedValue, "value1");
Assert.assertEquals(removedValues, Arrays.asList("value1"));
Assert.assertEquals(removedValues, Arrays.asList("value1"));
Assert.assertEquals(cache.get("key"), null);
}
Assert.assertEquals(cache.get("key"), null);
}
public void testClear() throws InterruptedException, ExecutionException, TimeoutException {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
public void testClear() throws InterruptedException, ExecutionException, TimeoutException {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final List<String> removedValues = new ArrayList<>();
cache.addListener((key, value) -> removedValues.add(value));
final List<String> removedValues = new ArrayList<>();
cache.addListener((key, value) -> removedValues.add(value));
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.clear();
cache.clear();
Assert.assertEquals(cache.get("key1"), null);
Assert.assertEquals(cache.get("key2"), null);
Assert.assertEquals(cache.get("key1"), null);
Assert.assertEquals(cache.get("key2"), null);
Assert.assertEquals(removedValues, Arrays.asList("value1", "value2"));
}
Assert.assertEquals(removedValues, Arrays.asList("value1", "value2"));
}
public void testForEachTouches() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
public void testForEachTouches() throws InterruptedException, ExecutionException, TimeoutException {
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final Duration timeToLive = Duration.ofSeconds(10);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
cache.addListener((key, value) -> {
evictionEventFuture.complete(value);
});
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
cache.addListener((key, value) -> {
evictionEventFuture.complete(value);
});
// add value
cache.put("key", "value1");
// add value
cache.put("key", "value1");
// seek, so that it is almost evicted
clock.plus(timeToLive.minusMillis(1));
cache.updateTime();
// seek, so that it is almost evicted
clock.plus(timeToLive.minusMillis(1));
cache.updateTime();
// the for each should touch the entries
cache.forEach(s -> {
/* no-op */});
// the for each should touch the entries
cache.forEach(s -> {
/* no-op */});
// seek again
clock.plus(timeToLive.minusMillis(1));
cache.triggerEvictionAndWait();
// seek again
clock.plus(timeToLive.minusMillis(1));
cache.triggerEvictionAndWait();
// if the touch didn't happen, then the value is now evicted
Assert.assertEquals(evictionEventFuture.isDone(), false);
// if the touch didn't happen, then the value is now evicted
Assert.assertEquals(evictionEventFuture.isDone(), false);
// seek again, so that the entry will get evicted
clock.plus(timeToLive.minusMillis(1));
cache.triggerEvictionAndWait();
// seek again, so that the entry will get evicted
clock.plus(timeToLive.minusMillis(1));
cache.triggerEvictionAndWait();
Assert.assertEquals(cache.get("key"), null);
}
/**
* Checks that
* {@link HotEntryCache#putIfAbsent(Object, java.util.function.Function)
* putIfAbsent} is atomic by calling
* {@link HotEntryCache#putIfAbsent(Object, java.util.function.Function)
* putIfAbsent} in two threads and asserting that the supplier was only called
* once.
*
* @throws Exception
*/
public void testPutIfAbsentIsAtomic() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final ExecutorService pool = Executors.newCachedThreadPool();
try {
final CountDownLatch latch = new CountDownLatch(1);
final String key = "key";
final String valueA = "A";
final String valueB = "B";
pool.submit(() -> {
cache.putIfAbsent(key, k -> {
latch.countDown();
sleep(TimeUnit.MILLISECONDS, 20);
return valueA;
});
return null;
});
pool.submit(() -> {
waitFor(latch);
cache.putIfAbsent(key, k -> valueB);
return null;
});
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);
final String actual = cache.get(key);
Assert.assertEquals(actual, valueA);
} finally {
pool.shutdownNow();
}
}
public void testPutIfAbsentReturnsExistingValue() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final String key = "key";
final String valueA = "A";
final String valueB = "B";
cache.put(key, valueA);
final String returnedByPutIfAbsent = cache.putIfAbsent(key, k -> valueB);
Assert.assertEquals(returnedByPutIfAbsent, valueA);
final String actualInCache = cache.get(key);
Assert.assertEquals(actualInCache, valueA);
}
public void testPutIfAbsentDoesNotAddNull() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final String key = "key";
final String returnedByPutIfAbsent = cache.putIfAbsent(key, k -> null);
Assert.assertNull(returnedByPutIfAbsent, null);
final String actualInCache = cache.get(key);
Assert.assertEquals(actualInCache, null);
}
public void testMaxSizeIsRespected() {
final int maxSize = 10;
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
final Set<String> removedKeys = new LinkedHashSet<>();
cache.addListener((key, value) -> removedKeys.add(key));
// fill the cache
int count = 0;
for (count = 0; count < maxSize; count++) {
cache.put("key" + count, "value" + count);
clock.plus(2L, ChronoUnit.MILLIS);
cache.updateTime();
}
Assert.assertEquals(cache.size(), maxSize, "cache is full");
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
// add an item to a full cache -> the oldest 20% of the entries will be evicted
// before the new entry is added
cache.put("key" + count, "value" + count);
clock.plus(2L, ChronoUnit.MILLIS);
cache.updateTime();
count++;
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
Assert.assertEquals(removedKeys, Set.of("key0", "key1"), "removed keys at point B");
}
public void testEvictionDueToSizeLimitDoesNotRemoveMoreThan20Percent() {
final int maxSize = 10;
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
final Set<String> removedKeys = new LinkedHashSet<>();
cache.addListener((key, value) -> removedKeys.add(key));
// fill the cache
int count = 0;
for (count = 0; count < maxSize; count++) {
// all entries get the same eviction time due to the fixed clock
cache.put("key" + count, "value" + count);
}
Assert.assertEquals(cache.size(), maxSize, "cache is full");
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
// add an item to a full cache -> the oldest 20% of the entries will be evicted
// before the new entry is added
cache.put("key" + count, "value" + count);
count++;
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
Assert.assertEquals(removedKeys.size(), (int) (maxSize * 0.2), "number of removed keys at point B");
}
private void sleep(final TimeUnit timeUnit, final long timeout) {
try {
timeUnit.sleep(timeout);
} catch (final InterruptedException e) {
throw new IllegalStateException(e);
}
}
private void waitFor(final CountDownLatch latch) {
try {
latch.await(1, TimeUnit.MINUTES);
} catch (final InterruptedException e) {
throw new IllegalStateException(e);
}
}
public static void main(final String[] args) throws InterruptedException {
Configurator.setRootLevel(Level.TRACE);
final Duration timeToLive = Duration.ofSeconds(1);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10);
cache.addListener((key, value) -> {
System.out.println(Instant.now() + " evicting: " + key + " -> " + value);
});
cache.put("key", "value that is touched");
for (int i = 0; i < 20; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
cache.put("key", "value that is touched" + i);
TimeUnit.MILLISECONDS.sleep(450);
}
for (int i = 20; i < 23; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
}
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
for (int i = 23; i < 27; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
}
TimeUnit.SECONDS.sleep(300);
}
Assert.assertEquals(cache.get("key"), null);
}
/**
* Checks that
* {@link HotEntryCache#putIfAbsent(Object, java.util.function.Function)
* putIfAbsent} is atomic by calling
* {@link HotEntryCache#putIfAbsent(Object, java.util.function.Function)
* putIfAbsent} in two threads and asserting that the supplier was only called
* once.
*
* @throws Exception
*/
public void testPutIfAbsentIsAtomic() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final ExecutorService pool = Executors.newCachedThreadPool();
try {
final CountDownLatch latch = new CountDownLatch(1);
final String key = "key";
final String valueA = "A";
final String valueB = "B";
pool.submit(() -> {
cache.putIfAbsent(key, k -> {
latch.countDown();
sleep(TimeUnit.MILLISECONDS, 20);
return valueA;
});
return null;
});
pool.submit(() -> {
waitFor(latch);
cache.putIfAbsent(key, k -> valueB);
return null;
});
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);
final String actual = cache.get(key);
Assert.assertEquals(actual, valueA);
} finally {
pool.shutdownNow();
}
}
public void testPutIfAbsentReturnsExistingValue() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final String key = "key";
final String valueA = "A";
final String valueB = "B";
cache.put(key, valueA);
final String returnedByPutIfAbsent = cache.putIfAbsent(key, k -> valueB);
Assert.assertEquals(returnedByPutIfAbsent, valueA);
final String actualInCache = cache.get(key);
Assert.assertEquals(actualInCache, valueA);
}
public void testPutIfAbsentDoesNotAddNull() throws Exception {
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
final String key = "key";
final String returnedByPutIfAbsent = cache.putIfAbsent(key, k -> null);
Assert.assertNull(returnedByPutIfAbsent, null);
final String actualInCache = cache.get(key);
Assert.assertEquals(actualInCache, null);
}
public void testMaxSizeIsRespected() {
final int maxSize = 10;
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
final Set<String> removedKeys = new LinkedHashSet<>();
cache.addListener((key, value) -> removedKeys.add(key));
// fill the cache
int count = 0;
for (count = 0; count < maxSize; count++) {
cache.put("key" + count, "value" + count);
clock.plus(2L, ChronoUnit.MILLIS);
cache.updateTime();
}
Assert.assertEquals(cache.size(), maxSize, "cache is full");
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
// add an item to a full cache -> the oldest 20% of the entries will be evicted
// before the new entry is added
cache.put("key" + count, "value" + count);
clock.plus(2L, ChronoUnit.MILLIS);
cache.updateTime();
count++;
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
Assert.assertEquals(removedKeys, Set.of("key0", "key1"), "removed keys at point B");
}
public void testEvictionDueToSizeLimitDoesNotRemoveMoreThan20Percent() {
final int maxSize = 10;
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
final Set<String> removedKeys = new LinkedHashSet<>();
cache.addListener((key, value) -> removedKeys.add(key));
// fill the cache
int count = 0;
for (count = 0; count < maxSize; count++) {
// all entries get the same eviction time due to the fixed clock
cache.put("key" + count, "value" + count);
}
Assert.assertEquals(cache.size(), maxSize, "cache is full");
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
// add an item to a full cache -> the oldest 20% of the entries will be evicted
// before the new entry is added
cache.put("key" + count, "value" + count);
count++;
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
Assert.assertEquals(removedKeys.size(), (int) (maxSize * 0.2), "number of removed keys at point B");
}
private void sleep(final TimeUnit timeUnit, final long timeout) {
try {
timeUnit.sleep(timeout);
} catch (final InterruptedException e) {
throw new IllegalStateException(e);
}
}
private void waitFor(final CountDownLatch latch) {
try {
latch.await(1, TimeUnit.MINUTES);
} catch (final InterruptedException e) {
throw new IllegalStateException(e);
}
}
public static void main(final String[] args) throws InterruptedException {
Configurator.setRootLevel(Level.TRACE);
final Duration timeToLive = Duration.ofSeconds(1);
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10);
cache.addListener((key, value) -> {
System.out.println(Instant.now() + " evicting: " + key + " -> " + value);
});
cache.put("key", "value that is touched");
for (int i = 0; i < 20; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
cache.put("key", "value that is touched" + i);
TimeUnit.MILLISECONDS.sleep(450);
}
for (int i = 20; i < 23; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
}
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
for (int i = 23; i < 27; i++) {
System.out.println(Instant.now() + " putting value" + i);
cache.put("key" + i, "value" + i);
TimeUnit.MILLISECONDS.sleep(Duration.ofSeconds(5).plusMillis(10).toMillis());
}
TimeUnit.SECONDS.sleep(300);
}
}

View File

@@ -12,87 +12,87 @@ import java.time.temporal.TemporalUnit;
* useful in tests, so that you can explicitly set the time.
*/
public class ModifiableFixedTimeClock extends Clock implements Serializable {
private static final long serialVersionUID = 1955332545617873736L;
private Instant instant;
private final ZoneId zone;
private static final long serialVersionUID = 1955332545617873736L;
private Instant instant;
private final ZoneId zone;
public ModifiableFixedTimeClock() {
this(Instant.now());
}
public ModifiableFixedTimeClock() {
this(Instant.now());
}
public ModifiableFixedTimeClock(final Instant fixedInstant) {
this(fixedInstant, ZoneId.systemDefault());
}
public ModifiableFixedTimeClock(final Instant fixedInstant) {
this(fixedInstant, ZoneId.systemDefault());
}
public ModifiableFixedTimeClock(final Instant fixedInstant, final ZoneId zone) {
this.instant = fixedInstant;
this.zone = zone;
}
public ModifiableFixedTimeClock(final Instant fixedInstant, final ZoneId zone) {
this.instant = fixedInstant;
this.zone = zone;
}
public void setTime(final Instant instant) {
this.instant = instant;
}
public void setTime(final Instant instant) {
this.instant = instant;
}
public void plus(final TemporalAmount amountToAdd) {
instant = instant.plus(amountToAdd);
}
public void plus(final TemporalAmount amountToAdd) {
instant = instant.plus(amountToAdd);
}
public void plus(final long amountToAdd, final TemporalUnit unit) {
instant = instant.plus(amountToAdd, unit);
}
public void plus(final long amountToAdd, final TemporalUnit unit) {
instant = instant.plus(amountToAdd, unit);
}
public void plusMillis(final long millisToAdd) {
instant = instant.plusMillis(millisToAdd);
}
public void plusMillis(final long millisToAdd) {
instant = instant.plusMillis(millisToAdd);
}
public void plusNanos(final long nanosToAdd) {
instant = instant.plusNanos(nanosToAdd);
}
public void plusNanos(final long nanosToAdd) {
instant = instant.plusNanos(nanosToAdd);
}
public void plusSeconds(final long secondsToAdd) {
instant = instant.plusSeconds(secondsToAdd);
}
public void plusSeconds(final long secondsToAdd) {
instant = instant.plusSeconds(secondsToAdd);
}
@Override
public ZoneId getZone() {
return zone;
}
@Override
public ZoneId getZone() {
return zone;
}
@Override
public Clock withZone(final ZoneId zone) {
if (zone.equals(this.zone)) {
return this;
}
return new ModifiableFixedTimeClock(instant, zone);
}
@Override
public Clock withZone(final ZoneId zone) {
if (zone.equals(this.zone)) {
return this;
}
return new ModifiableFixedTimeClock(instant, zone);
}
@Override
public long millis() {
return instant.toEpochMilli();
}
@Override
public long millis() {
return instant.toEpochMilli();
}
@Override
public Instant instant() {
return instant;
}
@Override
public Instant instant() {
return instant;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof ModifiableFixedTimeClock) {
final ModifiableFixedTimeClock other = (ModifiableFixedTimeClock) obj;
return instant.equals(other.instant) && zone.equals(other.zone);
}
return false;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof ModifiableFixedTimeClock) {
final ModifiableFixedTimeClock other = (ModifiableFixedTimeClock) obj;
return instant.equals(other.instant) && zone.equals(other.zone);
}
return false;
}
@Override
public int hashCode() {
return instant.hashCode() ^ zone.hashCode();
}
@Override
public int hashCode() {
return instant.hashCode() ^ zone.hashCode();
}
@Override
public String toString() {
return "FixedClock[" + instant + "," + zone + "]";
}
@Override
public String toString() {
return "FixedClock[" + instant + "," + zone + "]";
}
}