Skip to content

Commit 0409b2c

Browse files
authored
feat(spring): implement optional cache methods (#207)
* feat(spring-jdk17): implement optional cache retrieve methods * feat(spring): implement evictIfPresent method * fix(spring): ensure putIfAbsent is atomic
1 parent 7626e84 commit 0409b2c

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

spring-java17/src/main/java/io/github/xanthic/cache/springjdk17/XanthicSpringCache.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.springframework.cache.support.AbstractValueAdaptingCache;
66

77
import java.util.concurrent.Callable;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.function.Supplier;
810

911
public class XanthicSpringCache extends AbstractValueAdaptingCache {
1012
private final String name;
@@ -54,11 +56,21 @@ public void put(@NotNull Object key, Object value) {
5456
cache.put(key, toStoreValue(value));
5557
}
5658

59+
@Override
60+
public ValueWrapper putIfAbsent(@NotNull Object key, Object value) {
61+
return toValueWrapper(cache.putIfAbsent(key, toStoreValue(value)));
62+
}
63+
5764
@Override
5865
public void evict(@NotNull Object key) {
5966
cache.remove(key);
6067
}
6168

69+
@Override
70+
public boolean evictIfPresent(@NotNull Object key) {
71+
return cache.remove(key) != null;
72+
}
73+
6274
@Override
6375
public void clear() {
6476
cache.clear();
@@ -68,6 +80,22 @@ public void clear() {
6880
protected Object lookup(@NotNull Object key) {
6981
return cache.get(key);
7082
}
71-
}
7283

84+
@Override
85+
public CompletableFuture<?> retrieve(@NotNull Object key) {
86+
Object value = lookup(key);
87+
if (value == null) return null;
88+
return CompletableFuture.completedFuture(toValueWrapper(value));
89+
}
7390

91+
@NotNull
92+
@Override
93+
@SuppressWarnings("unchecked")
94+
public <T> CompletableFuture<T> retrieve(@NotNull Object key, @NotNull Supplier<CompletableFuture<T>> valueLoader) {
95+
return CompletableFuture.supplyAsync(
96+
() -> (T) fromStoreValue(
97+
cache.computeIfAbsent(key, k -> toStoreValue(valueLoader.get().join()))
98+
)
99+
);
100+
}
101+
}

spring-java17/src/test/java/io/github/xanthic/cache/springjdk17/SpringCacheTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.util.Objects;
1515
import java.util.concurrent.Callable;
16+
import java.util.concurrent.CompletableFuture;
1617
import java.util.concurrent.ExecutorService;
1718
import java.util.concurrent.Executors;
1819
import java.util.concurrent.TimeUnit;
@@ -28,7 +29,7 @@ public class SpringCacheTest {
2829
CacheManager cacheManager;
2930

3031
@Test
31-
@DisplayName("Tests cache get, put, putIfAbsent, clear")
32+
@DisplayName("Tests cache get, put, putIfAbsent, retrieve, clear")
3233
public void putGetClearTest() {
3334
Cache cache = Objects.requireNonNull(cacheManager.getCache("dev"));
3435

@@ -43,6 +44,18 @@ public void putGetClearTest() {
4344
Assertions.assertEquals(420, Objects.requireNonNull(cache.putIfAbsent("4/20", 69)).get());
4445
Assertions.assertEquals(420, Objects.requireNonNull(cache.get("4/20")).get());
4546

47+
// Test retrieve
48+
Assertions.assertEquals(420, unwrapValue(Objects.requireNonNull(cache.retrieve("4/20")).join()));
49+
CompletableFuture<?> missing = cache.retrieve("8/21");
50+
Assertions.assertTrue(missing == null || missing.join() == null);
51+
cache.put("5/11", null);
52+
Object wrappedMissing = Objects.requireNonNull(cache.retrieve("5/11")).join();
53+
Assertions.assertTrue(wrappedMissing instanceof Cache.ValueWrapper);
54+
Assertions.assertNull(unwrapValue(wrappedMissing));
55+
Assertions.assertNull(Objects.requireNonNull(cache.retrieve("5/11", () -> CompletableFuture.completedFuture(1605))).join());
56+
Assertions.assertEquals(69, cache.retrieve("6/9", () -> CompletableFuture.supplyAsync(() -> 69)).join());
57+
Assertions.assertEquals(69, cache.retrieve("6/9", () -> CompletableFuture.supplyAsync(() -> 70)).join());
58+
4659
// Test clear
4760
cache.clear();
4861
Assertions.assertNull(cache.get("4/20"));
@@ -139,4 +152,8 @@ public void valueLoaderConcurrentTest() throws InterruptedException {
139152
Assertions.assertEquals(1, callCounter.get(), "Value loader should only be called once");
140153
}
141154

155+
private static Object unwrapValue(Object value) {
156+
return value instanceof Cache.ValueWrapper ? ((Cache.ValueWrapper) value).get() : value;
157+
}
158+
142159
}

spring/src/main/java/io/github/xanthic/cache/spring/XanthicSpringCache.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,21 @@ public void put(@NotNull Object key, Object value) {
5454
cache.put(key, toStoreValue(value));
5555
}
5656

57+
@Override
58+
public ValueWrapper putIfAbsent(@NotNull Object key, Object value) {
59+
return toValueWrapper(cache.putIfAbsent(key, toStoreValue(value)));
60+
}
61+
5762
@Override
5863
public void evict(@NotNull Object key) {
5964
cache.remove(key);
6065
}
6166

67+
@Override
68+
public boolean evictIfPresent(@NotNull Object key) {
69+
return cache.remove(key) != null;
70+
}
71+
6272
@Override
6373
public void clear() {
6474
cache.clear();

0 commit comments

Comments
 (0)