diff --git a/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/ProgrammaticApiTest.java b/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/ProgrammaticApiTest.java index 937e3c2620269e..4570539e2ed16f 100644 --- a/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/ProgrammaticApiTest.java +++ b/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/ProgrammaticApiTest.java @@ -29,6 +29,7 @@ import io.quarkus.cache.CaffeineCache; import io.quarkus.cache.runtime.caffeine.CaffeineCacheImpl; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.mutiny.Uni; public class ProgrammaticApiTest { @@ -212,10 +213,25 @@ public void testPutShouldPopulateCache() { } } + @Test + public void testAsyncLoader() throws Exception { + // Action: value retrieval from the cache. + // Expected effect: asyncvalue loader function used. + // Verified by: same object reference between STEPS 1 and 2 results. + String key = "alpha"; + String result = "foo"; + Uni uni = Uni.createFrom().item(result); + String value = cache.getAsync(key, k -> uni).await().indefinitely(); + assertSame(value, result); + assertKeySetContains(key); + assertGetIfPresent(key, value); + + } + private void assertKeySetContains(Object... expectedKeys) { Set expectedKeySet = new HashSet<>(Arrays.asList(expectedKeys)); Set actualKeySet = cache.as(CaffeineCache.class).keySet(); - assertEquals(expectedKeySet, actualKeySet); + assertTrue(actualKeySet.containsAll(expectedKeySet)); } private void assertGetIfPresent(Object key, Object value) throws Exception { diff --git a/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/SpecializedCacheTest.java b/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/SpecializedCacheTest.java index 0e9b9b39bed18f..85f87dc9f2f141 100644 --- a/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/SpecializedCacheTest.java +++ b/extensions/cache/deployment/src/test/java/io/quarkus/cache/test/runtime/SpecializedCacheTest.java @@ -65,6 +65,11 @@ public Uni get(K key, Function valueLoader) { throw new UnsupportedOperationException("This method is not tested here"); } + @Override + public Uni getAsync(K key, Function> valueLoader) { + throw new UnsupportedOperationException("This method is not tested here"); + } + @Override public Uni invalidate(Object key) { throw new UnsupportedOperationException("This method is not tested here"); diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java index 6ffde2b3ee7c8b..f0bd9c0d0fb6f4 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java @@ -40,6 +40,19 @@ public interface Cache { */ Uni get(K key, Function valueLoader); + /** + * Returns a lazy asynchronous action that will emit the cache value identified by {@code key}, obtaining that value from + * {@code valueLoader} if necessary. + * + * @param + * @param + * @param key + * @param valueLoader + * @return a lazy asynchronous action that will emit a cache value + * @throws NullPointerException if the key is {@code null} + */ + Uni getAsync(K key, Function> valueLoader); + /** * Removes the cache entry identified by {@code key} from the cache. If the key does not identify any cache entry, nothing * will happen. @@ -61,4 +74,5 @@ public interface Cache { * @throws IllegalStateException if this cache is not an instance of {@code type} */ T as(Class type); + } diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java index 98a13cfe97e9df..aa77556d9e7981 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java @@ -93,6 +93,21 @@ public CompletionStage get() { }); } + @SuppressWarnings("unchecked") + @Override + public Uni getAsync(K key, Function> valueLoader) { + Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED_MSG); + return (Uni) Uni.createFrom() + .completionStage(cache.asMap().computeIfAbsent(key, new Function>() { + @Override + public CompletableFuture apply(Object key) { + return (CompletableFuture) valueLoader.apply((K) key) + .map(i -> NullValueConverter.toCacheValue(i)) + .subscribeAsCompletionStage(); + } + })); + } + @Override public CompletableFuture getIfPresent(Object key) { Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED_MSG); diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/noop/NoOpCache.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/noop/NoOpCache.java index 68bcdddb44498e..467a84ff3ec280 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/noop/NoOpCache.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/noop/NoOpCache.java @@ -29,6 +29,11 @@ public V get() { }); } + @Override + public Uni getAsync(K key, Function> valueLoader) { + return valueLoader.apply(key); + } + @Override public Uni invalidate(Object key) { return Uni.createFrom().voidItem();