diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index 001d00f8a810..b83784640ac2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -92,6 +92,8 @@ dependencies { optional("org.apache.tomcat.embed:tomcat-embed-el") optional("org.apache.tomcat:tomcat-jdbc") optional("org.aspectj:aspectjweaver") + optional("org.cache2k:cache2k-micrometer") + optional("org.cache2k:cache2k-spring") optional("org.eclipse.jetty:jetty-server") { exclude group: "javax.servlet", module: "javax.servlet-api" } @@ -155,6 +157,7 @@ dependencies { testImplementation("org.aspectj:aspectjrt") testImplementation("org.assertj:assertj-core") testImplementation("org.awaitility:awaitility") + testImplementation("org.cache2k:cache2k-api") testImplementation("org.eclipse.jetty:jetty-webapp") { exclude group: "javax.servlet", module: "javax.servlet-api" } @@ -176,6 +179,7 @@ dependencies { testRuntimeOnly("jakarta.management.j2ee:jakarta.management.j2ee-api") testRuntimeOnly("jakarta.transaction:jakarta.transaction-api") + testRuntimeOnly("org.cache2k:cache2k-core") testRuntimeOnly("org.springframework.security:spring-security-oauth2-jose") testRuntimeOnly("org.springframework.security:spring-security-oauth2-resource-server") testRuntimeOnly("org.springframework.security:spring-security-saml2-service-provider") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration.java index 8236f9160594..fd72d69d0a4f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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,7 +19,11 @@ import com.hazelcast.core.Hazelcast; import com.hazelcast.spring.cache.HazelcastCache; import io.micrometer.core.instrument.binder.MeterBinder; +import org.cache2k.Cache2kBuilder; +import org.cache2k.extra.micrometer.Cache2kCacheMetrics; +import org.cache2k.extra.spring.SpringCache2kCache; +import org.springframework.boot.actuate.metrics.cache.Cache2kCacheMeterBinderProvider; import org.springframework.boot.actuate.metrics.cache.CacheMeterBinderProvider; import org.springframework.boot.actuate.metrics.cache.CaffeineCacheMeterBinderProvider; import org.springframework.boot.actuate.metrics.cache.HazelcastCacheMeterBinderProvider; @@ -41,6 +45,17 @@ @ConditionalOnClass(MeterBinder.class) class CacheMeterBinderProvidersConfiguration { + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ Cache2kBuilder.class, SpringCache2kCache.class, Cache2kCacheMetrics.class }) + static class Cache2kCacheMeterBinderProviderConfiguration { + + @Bean + Cache2kCacheMeterBinderProvider cache2kCacheMeterBinderProvider() { + return new Cache2kCacheMeterBinderProvider(); + } + + } + @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ CaffeineCache.class, com.github.benmanes.caffeine.cache.Cache.class }) static class CaffeineCacheMeterBinderProviderConfiguration { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java index 8850c2979990..556b53128950 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -39,6 +39,16 @@ class CacheMetricsAutoConfigurationTests { .withUserConfiguration(CachingConfiguration.class).withConfiguration( AutoConfigurations.of(CacheAutoConfiguration.class, CacheMetricsAutoConfiguration.class)); + @Test + void autoConfiguredCache2kIsInstrumented() { + this.contextRunner.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cache-names=cache1,cache2") + .run((context) -> { + MeterRegistry registry = context.getBean(MeterRegistry.class); + registry.get("cache.gets").tags("name", "cache1").tags("cacheManager", "cacheManager").meter(); + registry.get("cache.gets").tags("name", "cache2").tags("cacheManager", "cacheManager").meter(); + }); + } + @Test void autoConfiguredCacheManagerIsInstrumented() { this.contextRunner.withPropertyValues("spring.cache.type=caffeine", "spring.cache.cache-names=cache1,cache2") diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle index 57e9b49672c4..3c0961dfa201 100644 --- a/spring-boot-project/spring-boot-actuator/build.gradle +++ b/spring-boot-project/spring-boot-actuator/build.gradle @@ -40,6 +40,8 @@ dependencies { } optional("org.apache.tomcat.embed:tomcat-embed-core") optional("org.aspectj:aspectjweaver") + optional("org.cache2k:cache2k-micrometer") + optional("org.cache2k:cache2k-spring") optional("org.eclipse.jetty:jetty-server") { exclude(group: "javax.servlet", module: "javax.servlet-api") } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProvider.java new file mode 100644 index 000000000000..d2954b4b46bf --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.metrics.cache; + +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.binder.MeterBinder; +import org.cache2k.extra.micrometer.Cache2kCacheMetrics; +import org.cache2k.extra.spring.SpringCache2kCache; + +/** + * {@link CacheMeterBinderProvider} implementation for cache2k. + * + * @author Jens Wilke + * @since 2.7.0 + */ +public class Cache2kCacheMeterBinderProvider implements CacheMeterBinderProvider { + + @Override + public MeterBinder getMeterBinder(SpringCache2kCache cache, Iterable tags) { + return new Cache2kCacheMetrics(cache.getNativeCache(), tags); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProviderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProviderTests.java new file mode 100644 index 000000000000..38bccbb10d78 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/Cache2kCacheMeterBinderProviderTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.metrics.cache; + +import java.util.Collections; + +import io.micrometer.core.instrument.binder.MeterBinder; +import org.cache2k.extra.micrometer.Cache2kCacheMetrics; +import org.cache2k.extra.spring.SpringCache2kCacheManager; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link Cache2kCacheMeterBinderProvider}. + * + * @author Stephane Nicoll + */ +class Cache2kCacheMeterBinderProviderTests { + + @Test + void cache2kCacheProvider() { + SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager() + .addCaches((builder) -> builder.name("test")); + MeterBinder meterBinder = new Cache2kCacheMeterBinderProvider().getMeterBinder(cacheManager.getCache("test"), + Collections.emptyList()); + assertThat(meterBinder).isInstanceOf(Cache2kCacheMetrics.class); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index dc32fef9c373..765f303659d2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -77,6 +77,8 @@ dependencies { optional("com.zaxxer:HikariCP") optional("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect") optional("org.aspectj:aspectjweaver") + optional("org.cache2k:cache2k-spring") + optional("org.cache2k:cache2k-micrometer") optional("org.eclipse.jetty:jetty-webapp") { exclude group: "javax.servlet", module: "javax.servlet-api" exclude(group: "org.eclipse.jetty", module: "jetty-jndi") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kCacheConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kCacheConfiguration.java new file mode 100644 index 000000000000..a6b7bd00ecec --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kCacheConfiguration.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.cache; + +import java.util.Collection; + +import org.cache2k.Cache2kBuilder; +import org.cache2k.extra.spring.SpringCache2kCacheManager; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.CollectionUtils; + +/** + * Cache2k cache configuration. + * + * @author Jens Wilke + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ Cache2kBuilder.class, SpringCache2kCacheManager.class }) +@ConditionalOnMissingBean(CacheManager.class) +@Conditional(CacheCondition.class) +class Cache2kCacheConfiguration { + + @Bean + SpringCache2kCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers customizers, + ObjectProvider defaults) { + SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager(); + Cache2kDefaults specifiedDefaults = defaults.getIfAvailable(); + if (specifiedDefaults != null) { + cacheManager.defaultSetup((builder) -> { + specifiedDefaults.customize(builder); + return builder; + }); + } + Collection cacheNames = cacheProperties.getCacheNames(); + if (!CollectionUtils.isEmpty(cacheNames)) { + cacheManager.setDefaultCacheNames(cacheNames); + } + return customizers.customize(cacheManager); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kDefaults.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kDefaults.java new file mode 100644 index 000000000000..e43ea68b0357 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/Cache2kDefaults.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.cache; + +import org.cache2k.Cache2kBuilder; + +/** + * Default configuration for cache2k when Spring boot auto configuration is creating the + * Cache Manager. + * + * @author Jens Wilke + * @since 2.7.0 + */ +public interface Cache2kDefaults { + + void customize(Cache2kBuilder builder); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java index 608843e3ea1c..193d6e76eab8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java @@ -41,6 +41,7 @@ final class CacheConfigurations { mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName()); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName()); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName()); + mappings.put(CacheType.CACHE2K, Cache2kCacheConfiguration.class.getName()); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName()); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName()); MAPPINGS = Collections.unmodifiableMap(mappings); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheType.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheType.java index 8c97cd98eec7..a7185e9cae22 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheType.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheType.java @@ -51,6 +51,11 @@ public enum CacheType { */ REDIS, + /** + * Cache2k backed caching. + */ + CACHE2K, + /** * Caffeine backed caching. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/AbstractCacheAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/AbstractCacheAutoConfigurationTests.java index a311a4626661..3045039f1c5d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/AbstractCacheAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/AbstractCacheAutoConfigurationTests.java @@ -22,6 +22,7 @@ import java.util.Map; import com.hazelcast.spring.cache.HazelcastCacheManager; +import org.cache2k.extra.spring.SpringCache2kCacheManager; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; @@ -119,6 +120,13 @@ CacheManagerCustomizer hazelcastCacheManagerCustomizer() }; } + @Bean + CacheManagerCustomizer cache2kCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { + + }; + } + @Bean CacheManagerCustomizer caffeineCacheManagerCustomizer() { return new CacheManagerTestCustomizer<>() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java index b2947f2a71ae..3048ef20b15e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java @@ -32,6 +32,7 @@ import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.spring.cache.HazelcastCacheManager; +import org.cache2k.extra.spring.SpringCache2kCacheManager; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; @@ -69,6 +70,7 @@ import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -550,6 +552,147 @@ void jCacheCacheWithCachesAndCustomizer() { } } + @Test + void cache2kCacheWithDynamicCacheCreation() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k").run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).isEmpty(); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + Cache foo = manager.getCache("dynamic"); + foo.get("1"); + }); + } + + @Test + void cache2kCacheWithCacheNamesInProperties() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar").run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar"); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + manager.getCache("foo").get("1"); + manager.getCache("bar").get("2"); + assertThat(manager.getCache("unknown")).isNull(); + }); + } + + @Test + void cache2kCacheWithDynamicCacheCreationAndDefaults() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k") + .withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default")) + .run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).isEmpty(); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + Cache foo = manager.getCache("fooDynamic"); + assertThat(foo.get("1").get()).isEqualTo("default"); + Cache bar = manager.getCache("barDynamic"); + assertThat(bar.get("1").get()).isEqualTo("default"); + assertThat(manager.getCache("barDynamic")).isSameAs(bar); + }); + } + + @Test + void cache2kCacheWithNamesInPropertiesAndDefaults() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar") + .withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default")) + .run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar"); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + Cache foo = manager.getCache("foo"); + assertThat(foo.get("1").get()).isEqualTo("default"); + Cache bar = manager.getCache("bar"); + assertThat(bar.get("1").get()).isEqualTo("default"); + }); + } + + @Test + void cache2kCacheWithDynamicCacheCreationAndDefaultAndCustomCachesViaCacheManagerCustomizer() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k").withBean(CacheManagerCustomizer.class, + () -> (CacheManagerCustomizer) (cm) -> { + cm.defaultSetup((b) -> b.valueType(String.class).loader((key) -> "default")); + cm.addCache("custom", (b) -> b.valueType(String.class).loader((key) -> "custom")); + }) + .run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("custom"); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + Cache foo = manager.getCache("fooDynamic"); + assertThat(foo.get("1").get()).isEqualTo("default"); + Cache custom = manager.getCache("custom"); + assertThat(custom.get("1").get()).isEqualTo("custom"); + }); + } + + @Test + void cache2kCacheWithNamesInPropertiesAndDefaultsAndCacheManagerCustomizer() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k", + "spring.cache.cacheNames=foo,bar") + .withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default")) + .withBean(CacheManagerCustomizer.class, + () -> (CacheManagerCustomizer) (cm) -> cm.addCache("custom", + (b) -> b.valueType(String.class).loader((key) -> "custom"))) + .run((context) -> { + SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class); + assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar", "custom"); + assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault"); + Cache foo = manager.getCache("foo"); + assertThat(foo.get("1").get()).isEqualTo("default"); + Cache bar = manager.getCache("bar"); + assertThat(bar.get("1").get()).isEqualTo("default"); + assertThat(manager.isAllowUnknownCache()).isFalse(); + }); + } + + /** + * Default cannot be changed in CacheManagerCustomizer, if cache names are present in + * the properties + */ + @Test + void cache2kCacheNamesAndCacheManagerDefaultsYieldsException() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar") + .withBean(CacheManagerCustomizer.class, + () -> (CacheManagerCustomizer) (cm) -> cm + .defaultSetup((b) -> b.entryCapacity(1234))) + .run((context) -> { + assertThatCode(() -> getCacheManager(context, SpringCache2kCacheManager.class)).getRootCause() + .isInstanceOf(IllegalStateException.class); + // close underlying cache manager directly since Spring bean was not + // created + org.cache2k.CacheManager.getInstance(SpringCache2kCacheManager.DEFAULT_SPRING_CACHE_MANAGER_NAME) + .close(); + }); + } + + /** + * Applying defaults via customizer and cache manager is not possible + */ + @Test + void cache2kCacheDefaultsTwiceYieldsException() { + this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k") + .withBean(Cache2kDefaults.class, () -> (b) -> b.entryCapacity(1234)) + .withBean(CacheManagerCustomizer.class, + () -> (CacheManagerCustomizer) (cm) -> cm + .defaultSetup((b) -> b.entryCapacity(1234))) + .run((context) -> assertThatCode(() -> getCacheManager(context, SpringCache2kCacheManager.class)) + .getRootCause().isInstanceOf(IllegalStateException.class)); + } + + @Test + void cache2kCacheWithCustomizers() { + this.contextRunner.withUserConfiguration(DefaultCacheAndCustomizersConfiguration.class) + .withPropertyValues("spring.cache.type=cache2k") + .run(verifyCustomizers("allCacheManagerCustomizer", "cache2kCacheManagerCustomizer")); + } + @Test void caffeineCacheWithExplicitCaches() { this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 1b1cf1a42d2e..fb095d4be6d0 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -87,6 +87,18 @@ bom { ] } } + library("cache2k", "2.6.1.Final") { + group("org.cache2k") { + modules = [ + "cache2k-api", + "cache2k-config", + "cache2k-core", + "cache2k-jcache", + "cache2k-micrometer", + "cache2k-spring" + ] + } + } library("Caffeine", "3.0.5") { group("com.github.ben-manes.caffeine") { modules = [