diff --git a/bom/application/pom.xml b/bom/application/pom.xml index cd49c4517fd72e..a3041c162b736d 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -2803,6 +2803,11 @@ quarkus-cache-deployment-spi ${project.version} + + io.quarkus + quarkus-cache-runtime-spi + ${project.version} + io.quarkus quarkus-google-cloud-functions diff --git a/extensions/cache/deployment-spi/pom.xml b/extensions/cache/deployment-spi/pom.xml index 1cfd1d01e99c24..9e5bda717185fc 100644 --- a/extensions/cache/deployment-spi/pom.xml +++ b/extensions/cache/deployment-spi/pom.xml @@ -18,6 +18,10 @@ io.quarkus quarkus-core-deployment + + io.quarkus + quarkus-cache-runtime-spi + diff --git a/extensions/cache/deployment-spi/src/main/java/io/quarkus/cache/deployment/spi/CacheManagerInfoBuildItem.java b/extensions/cache/deployment-spi/src/main/java/io/quarkus/cache/deployment/spi/CacheManagerInfoBuildItem.java new file mode 100644 index 00000000000000..348791e84d2e5b --- /dev/null +++ b/extensions/cache/deployment-spi/src/main/java/io/quarkus/cache/deployment/spi/CacheManagerInfoBuildItem.java @@ -0,0 +1,20 @@ +package io.quarkus.cache.deployment.spi; + +import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.cache.CacheManagerInfo; + +/** + * TODO: add + */ +public final class CacheManagerInfoBuildItem extends MultiBuildItem { + + private final CacheManagerInfo info; + + public CacheManagerInfoBuildItem(CacheManagerInfo info) { + this.info = info; + } + + public CacheManagerInfo get() { + return info; + } +} diff --git a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheProcessor.java b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheProcessor.java index e6261081f5cf9f..cb5497b8fb8f9d 100644 --- a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheProcessor.java +++ b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheProcessor.java @@ -53,6 +53,7 @@ import io.quarkus.cache.deployment.exception.UnsupportedRepeatedAnnotationException; import io.quarkus.cache.deployment.exception.VoidReturnTypeTargetException; import io.quarkus.cache.deployment.spi.AdditionalCacheNameBuildItem; +import io.quarkus.cache.deployment.spi.CacheManagerInfoBuildItem; import io.quarkus.cache.runtime.CacheInvalidateAllInterceptor; import io.quarkus.cache.runtime.CacheInvalidateInterceptor; import io.quarkus.cache.runtime.CacheManagerRecorder; @@ -232,17 +233,26 @@ private List validateKeyGeneratorsDefaultConstructor(CombinedIndexBui @BuildStep @Record(RUNTIME_INIT) - SyntheticBeanBuildItem configureCacheManagerSyntheticBean(CacheNamesBuildItem cacheNames, - CacheManagerRecorder cacheManagerRecorder, Optional metricsCapability) { + void cacheManagerInfos(BuildProducer producer, + Optional metricsCapability, CacheManagerRecorder recorder) { + producer.produce(new CacheManagerInfoBuildItem(recorder.noOpCacheManagerInfo())); + producer.produce(new CacheManagerInfoBuildItem(recorder.getCacheManagerInfoWithoutMetrics())); + if (metricsCapability.isPresent() && metricsCapability.get().metricsSupported(MICROMETER)) { + // if we include this unconditionally the native image building will fail when Micrometer is not around + producer.produce(new CacheManagerInfoBuildItem(recorder.getCacheManagerInfoWithMicrometerMetrics())); + } + } - boolean micrometerSupported = metricsCapability.isPresent() && metricsCapability.get().metricsSupported(MICROMETER); + @BuildStep + @Record(RUNTIME_INIT) + SyntheticBeanBuildItem configureCacheManagerSyntheticBean(List infos, + CacheNamesBuildItem cacheNames, Optional metricsCapability, + CacheManagerRecorder cacheManagerRecorder) { - Supplier cacheManagerSupplier; - if (micrometerSupported) { - cacheManagerSupplier = cacheManagerRecorder.getCacheManagerSupplierWithMicrometerMetrics(cacheNames.getNames()); - } else { - cacheManagerSupplier = cacheManagerRecorder.getCacheManagerSupplierWithoutMetrics(cacheNames.getNames()); - } + boolean micrometerSupported = metricsCapability.isPresent() && metricsCapability.get().metricsSupported(MICROMETER); + Supplier cacheManagerSupplier = cacheManagerRecorder.resolveCacheInfo( + infos.stream().map(CacheManagerInfoBuildItem::get).collect(toList()), cacheNames.getNames(), + micrometerSupported); return SyntheticBeanBuildItem.configure(CacheManager.class) .scope(ApplicationScoped.class) diff --git a/extensions/cache/pom.xml b/extensions/cache/pom.xml index 092198414e79cd..a7f580a571601f 100644 --- a/extensions/cache/pom.xml +++ b/extensions/cache/pom.xml @@ -19,5 +19,6 @@ deployment deployment-spi runtime + runtime-spi diff --git a/extensions/cache/runtime-spi/pom.xml b/extensions/cache/runtime-spi/pom.xml new file mode 100644 index 00000000000000..19619b5e24a5fc --- /dev/null +++ b/extensions/cache/runtime-spi/pom.xml @@ -0,0 +1,40 @@ + + + + io.quarkus + quarkus-cache-parent + 999-SNAPSHOT + + 4.0.0 + + quarkus-cache-runtime-spi + Quarkus - Cache - Runtime SPI + + + + io.smallrye.reactive + mutiny + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/Cache.java similarity index 89% rename from extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java rename to extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/Cache.java index a7d7b3e9b2637b..baba8744869a7d 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java +++ b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/Cache.java @@ -7,7 +7,7 @@ /** * Use this interface to interact with a cache programmatically e.g. store, retrieve or delete cache values. The cache can be - * injected using the {@link CacheName} annotation or retrieved using {@link CacheManager#getCache(String)}. + * injected using the {@code @io.quarkus.cache.CacheName} annotation or retrieved using {@link CacheManager#getCache(String)}. */ public interface Cache { @@ -20,7 +20,8 @@ public interface Cache { /** * Returns the unique and immutable default key for the current cache. This key is used by the annotations caching API when - * a no-args method annotated with {@link CacheResult} or {@link CacheInvalidate} is invoked. It can also be used with the + * a no-args method annotated with {@code @io.quarkus.cache.CacheResult} or {@code @io.quarkus.cache.CacheInvalidate} is + * invoked. It can also be used with the * programmatic caching API. * * @return default cache key diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/CacheManager.java b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManager.java similarity index 91% rename from extensions/cache/runtime/src/main/java/io/quarkus/cache/CacheManager.java rename to extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManager.java index ffedd8ad288e19..c62628ca1e0893 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/CacheManager.java +++ b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManager.java @@ -7,7 +7,7 @@ *

* Use this interface to retrieve all existing {@link Cache} names and interact with any cache programmatically e.g. store, * retrieve or delete cache values. It shares the same caches collection the Quarkus caching annotations use. The - * {@link CacheName} annotation can also be used to inject and access a specific cache from its name. + * {@code @io.quarkus.cache.CacheName} annotation can also be used to inject and access a specific cache from its name. *

*

* Code example: diff --git a/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManagerInfo.java b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManagerInfo.java new file mode 100644 index 00000000000000..cd46de05491848 --- /dev/null +++ b/extensions/cache/runtime-spi/src/main/java/io/quarkus/cache/CacheManagerInfo.java @@ -0,0 +1,27 @@ +package io.quarkus.cache; + +import java.util.Set; +import java.util.function.Supplier; + +public interface CacheManagerInfo { + + boolean supports(Context context); + + Supplier get(Context context); + + interface Context { + + boolean cacheEnabled(); + + Metrics metrics(); + + String cacheType(); + + Set cacheNames(); + + enum Metrics { + NONE, + MICROMETER + } + } +} diff --git a/extensions/cache/runtime/pom.xml b/extensions/cache/runtime/pom.xml index c87ec82e4e0ef6..70993637b709a0 100644 --- a/extensions/cache/runtime/pom.xml +++ b/extensions/cache/runtime/pom.xml @@ -27,6 +27,10 @@ io.quarkus quarkus-mutiny + + io.quarkus + quarkus-cache-runtime-spi + io.vertx vertx-web diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheBuildConfig.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheBuildConfig.java new file mode 100644 index 00000000000000..be01933b30a96c --- /dev/null +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheBuildConfig.java @@ -0,0 +1,20 @@ +package io.quarkus.cache.runtime; + +import static io.quarkus.runtime.annotations.ConfigPhase.BUILD_AND_RUN_TIME_FIXED; + +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigRoot(phase = BUILD_AND_RUN_TIME_FIXED) +@ConfigMapping(prefix = "quarkus.cache") +public interface CacheBuildConfig { + + String CAFFEINE_CACHE_TYPE = "caffeine"; + + /** + * Cache type. + */ + @WithDefault(CAFFEINE_CACHE_TYPE) + String type(); +} diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheConfig.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheConfig.java index 56d46a8870da16..0ac4f0bad8331e 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheConfig.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheConfig.java @@ -10,60 +10,50 @@ import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigDocSection; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithParentName; @ConfigRoot(phase = RUN_TIME) -public class CacheConfig { - - public static final String CAFFEINE_CACHE_TYPE = "caffeine"; +@ConfigMapping(prefix = "quarkus.cache") +public interface CacheConfig { /** * Whether or not the cache extension is enabled. */ - @ConfigItem(defaultValue = "true") - public boolean enabled; - - /** - * Cache type. - */ - @ConfigItem(defaultValue = CAFFEINE_CACHE_TYPE) - public String type; + @WithDefault("true") + boolean enabled(); /** * Caffeine configuration. */ - @ConfigItem - public CaffeineConfig caffeine; + CaffeineConfig caffeine(); - @ConfigGroup - public static class CaffeineConfig { + interface CaffeineConfig { /** * Default configuration applied to all Caffeine caches (lowest precedence) */ - @ConfigItem(name = ConfigItem.PARENT) + @WithParentName @ConfigDocSection - public CaffeineCacheConfig defaultConfig; + CaffeineCacheConfig defaultConfig(); /** * Additional configuration applied to a specific Caffeine cache (highest precedence) */ - @ConfigItem(name = ConfigItem.PARENT) + @WithParentName @ConfigDocMapKey("cache-name") @ConfigDocSection - public Map cachesConfig; + Map cachesConfig(); - @ConfigGroup - public static class CaffeineCacheConfig { + interface CaffeineCacheConfig { /** * Minimum total size for the internal data structures. Providing a large enough estimate at construction time * avoids the need for expensive resizing operations later, but setting this value unnecessarily high wastes memory. */ - @ConfigItem - public OptionalInt initialCapacity; + OptionalInt initialCapacity(); /** * Maximum number of entries the cache may contain. Note that the cache may evict an entry before this limit is @@ -71,29 +61,25 @@ public static class CaffeineCacheConfig { * the cache evicts entries that are less likely to be used again. For example, the cache may evict an entry because * it hasn't been used recently or very often. */ - @ConfigItem - public OptionalLong maximumSize; + OptionalLong maximumSize(); /** * Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after * the entry's creation, or the most recent replacement of its value. */ - @ConfigItem - public Optional expireAfterWrite; + Optional expireAfterWrite(); /** * Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after * the entry's creation, the most recent replacement of its value, or its last read. */ - @ConfigItem - public Optional expireAfterAccess; + Optional expireAfterAccess(); /** * Whether or not metrics are recorded if the application depends on the Micrometer extension. Setting this * value to {@code true} will enable the accumulation of cache stats inside Caffeine. */ - @ConfigItem - public Optional metricsEnabled; + Optional metricsEnabled(); } } } diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheManagerRecorder.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheManagerRecorder.java index 8350ca55c6b280..2e2621edc114f7 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheManagerRecorder.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheManagerRecorder.java @@ -1,57 +1,105 @@ package io.quarkus.cache.runtime; -import static io.quarkus.cache.runtime.CacheConfig.CAFFEINE_CACHE_TYPE; +import static io.quarkus.cache.runtime.CacheBuildConfig.CAFFEINE_CACHE_TYPE; +import java.util.Collection; import java.util.Set; import java.util.function.Supplier; import jakarta.enterprise.inject.spi.DeploymentException; import io.quarkus.cache.CacheManager; +import io.quarkus.cache.CacheManagerInfo; import io.quarkus.cache.runtime.caffeine.CaffeineCacheManagerBuilder; import io.quarkus.cache.runtime.noop.NoOpCacheManagerBuilder; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; @Recorder public class CacheManagerRecorder { - private final CacheConfig cacheConfig; + private final CacheBuildConfig cacheBuildConfig; + private final RuntimeValue cacheConfigRV; - public CacheManagerRecorder(CacheConfig cacheConfig) { - this.cacheConfig = cacheConfig; + public CacheManagerRecorder(CacheBuildConfig cacheBuildConfig, RuntimeValue cacheConfigRV) { + this.cacheBuildConfig = cacheBuildConfig; + this.cacheConfigRV = cacheConfigRV; } - public Supplier getCacheManagerSupplierWithMicrometerMetrics(Set cacheNames) { - Supplier> caffeineCacheManagerSupplier = new Supplier>() { + public Supplier resolveCacheInfo(Collection infos, Set cacheNames, + boolean micrometerMetricsEnabled) { + CacheConfig cacheConfig = cacheConfigRV.getValue(); + CacheManagerInfo.Context context = new CacheManagerInfo.Context() { @Override - public Supplier get() { - return CaffeineCacheManagerBuilder.buildWithMicrometerMetrics(cacheNames, cacheConfig); + public boolean cacheEnabled() { + return cacheConfig.enabled(); + } + + @Override + public Metrics metrics() { + return micrometerMetricsEnabled ? Metrics.MICROMETER : Metrics.NONE; + } + + @Override + public String cacheType() { + return cacheBuildConfig.type(); + } + + @Override + public Set cacheNames() { + return cacheNames; } }; - return getCacheManagerSupplier(cacheNames, caffeineCacheManagerSupplier); + for (CacheManagerInfo info : infos) { + if (info.supports(context)) { + return info.get(context); + } + } + throw new DeploymentException("Unknown cache type: " + context.cacheType()); } - public Supplier getCacheManagerSupplierWithoutMetrics(Set cacheNames) { - Supplier> caffeineCacheManagerSupplier = new Supplier>() { + public CacheManagerInfo noOpCacheManagerInfo() { + return new CacheManagerInfo() { @Override - public Supplier get() { - return CaffeineCacheManagerBuilder.buildWithoutMetrics(cacheNames, cacheConfig); + public boolean supports(Context context) { + return !context.cacheEnabled(); + } + + @Override + public Supplier get(Context context) { + return NoOpCacheManagerBuilder.build(context.cacheNames()); } }; - return getCacheManagerSupplier(cacheNames, caffeineCacheManagerSupplier); } - private Supplier getCacheManagerSupplier(Set cacheNames, - Supplier> caffeineCacheManagerSupplier) { - if (cacheConfig.enabled) { - switch (cacheConfig.type) { - case CAFFEINE_CACHE_TYPE: - return caffeineCacheManagerSupplier.get(); - default: - throw new DeploymentException("Unknown cache type: " + cacheConfig.type); - } - } else { - return NoOpCacheManagerBuilder.build(cacheNames); - } + public CacheManagerInfo getCacheManagerInfoWithMicrometerMetrics() { + return new CacheManagerInfo() { + @Override + public boolean supports(Context context) { + return context.cacheEnabled() && context.cacheType().equals(CAFFEINE_CACHE_TYPE) + && (context.metrics() == Context.Metrics.MICROMETER); + } + + @Override + public Supplier get(Context context) { + return CaffeineCacheManagerBuilder.buildWithMicrometerMetrics(context.cacheNames(), cacheConfigRV.getValue()); + } + }; } + + public CacheManagerInfo getCacheManagerInfoWithoutMetrics() { + return new CacheManagerInfo() { + @Override + public boolean supports(Context context) { + return context.cacheEnabled() && context.cacheType().equals(CAFFEINE_CACHE_TYPE) + && (context.metrics() == Context.Metrics.NONE); + } + + @Override + public Supplier get(Context context) { + return CaffeineCacheManagerBuilder.buildWithoutMetrics(context.cacheNames(), cacheConfigRV.getValue()); + } + }; + } + } diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheInfoBuilder.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheInfoBuilder.java index e324300052acba..c5af6931e29d3c 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheInfoBuilder.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheInfoBuilder.java @@ -14,7 +14,7 @@ public static Set build(Set cacheNames, CacheConfig c if (cacheNames.isEmpty()) { return Collections.emptySet(); } else { - CaffeineCacheConfig defaultConfig = cacheConfig.caffeine.defaultConfig; + CaffeineCacheConfig defaultConfig = cacheConfig.caffeine().defaultConfig(); Set cacheInfos = HashSetFactory. getInstance().apply(cacheNames.size()); for (String cacheName : cacheNames) { @@ -22,36 +22,36 @@ public static Set build(Set cacheNames, CacheConfig c CaffeineCacheInfo cacheInfo = new CaffeineCacheInfo(); cacheInfo.name = cacheName; - CaffeineCacheConfig namedCacheConfig = cacheConfig.caffeine.cachesConfig.get(cacheInfo.name); + CaffeineCacheConfig namedCacheConfig = cacheConfig.caffeine().cachesConfig().get(cacheInfo.name); - if (namedCacheConfig != null && namedCacheConfig.initialCapacity.isPresent()) { - cacheInfo.initialCapacity = namedCacheConfig.initialCapacity.getAsInt(); - } else if (defaultConfig.initialCapacity.isPresent()) { - cacheInfo.initialCapacity = defaultConfig.initialCapacity.getAsInt(); + if (namedCacheConfig != null && namedCacheConfig.initialCapacity().isPresent()) { + cacheInfo.initialCapacity = namedCacheConfig.initialCapacity().getAsInt(); + } else if (defaultConfig.initialCapacity().isPresent()) { + cacheInfo.initialCapacity = defaultConfig.initialCapacity().getAsInt(); } - if (namedCacheConfig != null && namedCacheConfig.maximumSize.isPresent()) { - cacheInfo.maximumSize = namedCacheConfig.maximumSize.getAsLong(); - } else if (defaultConfig.maximumSize.isPresent()) { - cacheInfo.maximumSize = defaultConfig.maximumSize.getAsLong(); + if (namedCacheConfig != null && namedCacheConfig.maximumSize().isPresent()) { + cacheInfo.maximumSize = namedCacheConfig.maximumSize().getAsLong(); + } else if (defaultConfig.maximumSize().isPresent()) { + cacheInfo.maximumSize = defaultConfig.maximumSize().getAsLong(); } - if (namedCacheConfig != null && namedCacheConfig.expireAfterWrite.isPresent()) { - cacheInfo.expireAfterWrite = namedCacheConfig.expireAfterWrite.get(); - } else if (defaultConfig.expireAfterWrite.isPresent()) { - cacheInfo.expireAfterWrite = defaultConfig.expireAfterWrite.get(); + if (namedCacheConfig != null && namedCacheConfig.expireAfterWrite().isPresent()) { + cacheInfo.expireAfterWrite = namedCacheConfig.expireAfterWrite().get(); + } else if (defaultConfig.expireAfterWrite().isPresent()) { + cacheInfo.expireAfterWrite = defaultConfig.expireAfterWrite().get(); } - if (namedCacheConfig != null && namedCacheConfig.expireAfterAccess.isPresent()) { - cacheInfo.expireAfterAccess = namedCacheConfig.expireAfterAccess.get(); - } else if (defaultConfig.expireAfterAccess.isPresent()) { - cacheInfo.expireAfterAccess = defaultConfig.expireAfterAccess.get(); + if (namedCacheConfig != null && namedCacheConfig.expireAfterAccess().isPresent()) { + cacheInfo.expireAfterAccess = namedCacheConfig.expireAfterAccess().get(); + } else if (defaultConfig.expireAfterAccess().isPresent()) { + cacheInfo.expireAfterAccess = defaultConfig.expireAfterAccess().get(); } - if (namedCacheConfig != null && namedCacheConfig.metricsEnabled.isPresent()) { - cacheInfo.metricsEnabled = namedCacheConfig.metricsEnabled.get(); - } else if (defaultConfig.metricsEnabled.isPresent()) { - cacheInfo.metricsEnabled = defaultConfig.metricsEnabled.get(); + if (namedCacheConfig != null && namedCacheConfig.metricsEnabled().isPresent()) { + cacheInfo.metricsEnabled = namedCacheConfig.metricsEnabled().get(); + } else if (defaultConfig.metricsEnabled().isPresent()) { + cacheInfo.metricsEnabled = defaultConfig.metricsEnabled().get(); } cacheInfos.add(cacheInfo);