Merge branch '2.7.x'

This commit is contained in:
Stephane Nicoll 2022-03-21 10:06:56 +01:00
commit 2088381d4b
19 changed files with 385 additions and 2 deletions

View File

@ -93,6 +93,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"
}
@ -156,6 +158,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"
}
@ -177,6 +180,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")

View File

@ -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 @@ package org.springframework.boot.actuate.autoconfigure.metrics.cache;
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 @@ import org.springframework.data.redis.cache.RedisCache;
@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 {

View File

@ -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")

View File

@ -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")
}

View File

@ -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<SpringCache2kCache> {
@Override
public MeterBinder getMeterBinder(SpringCache2kCache cache, Iterable<Tag> tags) {
return new Cache2kCacheMetrics(cache.getNativeCache(), tags);
}
}

View File

@ -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);
}
}

View File

@ -77,6 +77,7 @@ dependencies {
optional("com.zaxxer:HikariCP")
optional("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
optional("org.aspectj:aspectjweaver")
optional("org.cache2k:cache2k-spring")
optional("org.eclipse.jetty:jetty-webapp") {
exclude group: "javax.servlet", module: "javax.servlet-api"
exclude(group: "org.eclipse.jetty", module: "jetty-jndi")

View File

@ -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.autoconfigure.cache;
import org.cache2k.Cache2kBuilder;
/**
* Callback interface that can be implemented by beans wishing to customize the default
* setup for caches added to the manager via addCaches and for dynamically created caches.
*
* @author Jens Wilke
* @author Stephane Nicoll
* @since 2.7.0
*/
public interface Cache2kBuilderCustomizer {
/**
* Customize the default cache settings.
* @param builder the builder to customize
*/
void customize(Cache2kBuilder<?, ?> builder);
}

View File

@ -0,0 +1,66 @@
/*
* 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 java.util.function.Function;
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
* @author Stephane Nicoll
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Cache2kBuilder.class, SpringCache2kCacheManager.class })
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class Cache2kCacheConfiguration {
@Bean
SpringCache2kCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers customizers,
ObjectProvider<Cache2kBuilderCustomizer> cache2kBuilderCustomizers) {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
cacheManager.defaultSetup(configureDefaults(cache2kBuilderCustomizers));
Collection<String> cacheNames = cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
cacheManager.setDefaultCacheNames(cacheNames);
}
return customizers.customize(cacheManager);
}
private Function<Cache2kBuilder<?, ?>, Cache2kBuilder<?, ?>> configureDefaults(
ObjectProvider<Cache2kBuilderCustomizer> cache2kBuilderCustomizers) {
return (builder) -> {
cache2kBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
};
}
}

View File

@ -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);

View File

@ -51,6 +51,11 @@ public enum CacheType {
*/
REDIS,
/**
* Cache2k backed caching.
*/
CACHE2K,
/**
* Caffeine backed caching.
*/

View File

@ -22,6 +22,7 @@ import java.util.List;
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 @@ abstract class AbstractCacheAutoConfigurationTests {
};
}
@Bean
CacheManagerCustomizer<SpringCache2kCacheManager> cache2kCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<SpringCache2kCacheManager>() {
};
}
@Bean
CacheManagerCustomizer<CaffeineCacheManager> caffeineCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<>() {

View File

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.cache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import javax.cache.Caching;
import javax.cache.configuration.CompleteConfiguration;
@ -32,6 +33,7 @@ import com.hazelcast.cache.impl.HazelcastServerCachingProvider;
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;
@ -43,6 +45,7 @@ import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfigurati
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
@ -550,6 +553,70 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests {
}
}
@Test
void cache2kCacheWithExplicitCaches() {
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");
});
}
@Test
void cache2kCacheWithCustomizedDefaults() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k")
.withBean(Cache2kBuilderCustomizer.class,
() -> (builder) -> builder.valueType(String.class).loader((key) -> "default"))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).isEmpty();
Cache dynamic = manager.getCache("dynamic");
assertThat(dynamic.get("1")).satisfies(hasEntry("default"));
assertThat(dynamic.get("2")).satisfies(hasEntry("default"));
});
}
@Test
void cache2kCacheWithCustomizedDefaultsAndExplicitCaches() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar")
.withBean(Cache2kBuilderCustomizer.class,
() -> (builder) -> builder.valueType(String.class).loader((key) -> "default"))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar");
assertThat(manager.getCache("foo").get("1")).satisfies(hasEntry("default"));
assertThat(manager.getCache("bar").get("1")).satisfies(hasEntry("default"));
});
}
@Test
void cache2kCacheWithCacheManagerCustomizer() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k")
.withBean(CacheManagerCustomizer.class,
() -> cache2kCacheManagerCustomizer((cacheManager) -> cacheManager.addCache("custom",
(builder) -> builder.valueType(String.class).loader((key) -> "custom"))))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("custom");
assertThat(manager.getCache("custom").get("1")).satisfies(hasEntry("custom"));
});
}
private CacheManagerCustomizer<SpringCache2kCacheManager> cache2kCacheManagerCustomizer(
Consumer<SpringCache2kCacheManager> cacheManager) {
return cacheManager::accept;
}
@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)
@ -604,6 +671,10 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests {
});
}
private Consumer<ValueWrapper> hasEntry(Object value) {
return (valueWrapper) -> assertThat(valueWrapper.get()).isEqualTo(value);
}
private void validateCaffeineCacheWithStats(AssertableApplicationContext context) {
CaffeineCacheManager manager = getCacheManager(context, CaffeineCacheManager.class);
assertThat(manager.getCacheNames()).containsOnly("foo", "bar");

View File

@ -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 = [

View File

@ -102,6 +102,7 @@ dependencies {
}
implementation("org.apache.tomcat.embed:tomcat-embed-core")
implementation("org.assertj:assertj-core")
implementation("org.cache2k:cache2k-spring")
implementation("org.apache.groovy:groovy")
implementation("org.hibernate:hibernate-jcache") {
exclude group: "javax.activation", module: "javax.activation-api"

View File

@ -815,6 +815,7 @@ Additional, cache-specific metrics are also available.
The following cache libraries are supported:
* Cache2k
* Caffeine
* Hazelcast
* Any compliant JCache (JSR-107) implementation

View File

@ -42,6 +42,7 @@ If you have not defined a bean of type `CacheManager` or a `CacheResolver` named
. <<io#io.caching.provider.couchbase,Couchbase>>
. <<io#io.caching.provider.redis,Redis>>
. <<io#io.caching.provider.caffeine,Caffeine>>
. <<io#io.caching.provider.cache2k,Cache2k>>
. <<io#io.caching.provider.simple,Simple>>
Additionally, {spring-boot-for-apache-geode}[Spring Boot for Apache Geode] provides {spring-boot-for-apache-geode-docs}#geode-caching-provider[auto-configuration for using Apache Geode as a cache provider].
@ -190,6 +191,19 @@ The auto-configuration ignores any other generic type.
[[io.caching.provider.cache2k]]
==== Cache2k
https://cache2k.org/[Cache2k] is an in-memory cache.
If the Cache2k spring integration is present, a `SpringCache2kCacheManager` is auto-configured.
Caches can be created on startup by setting the configprop:spring.cache.cache-names[] property.
Cache defaults can be customized using a `Cache2kBuilderCustomizer` bean.
The following example shows a customizer that configures the capacity of the cache to 200 entries, with an expiration of 5 minutes:
include::code:MyCache2kDefaultsConfiguration[]
[[io.caching.provider.simple]]
==== Simple
If none of the other providers can be found, a simple implementation using a `ConcurrentHashMap` as the cache store is configured.

View File

@ -0,0 +1,36 @@
/*
* 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.docs.io.caching.provider.cache2k;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyCache2kDefaultsConfiguration {
@Bean
public Cache2kBuilderCustomizer myCache2kDefaultsCustomizer() {
// @formatter:off
return (builder) -> builder.entryCapacity(200)
.expireAfterWrite(5, TimeUnit.MINUTES);
// @formatter:on
}
}

View File

@ -0,0 +1,18 @@
package org.springframework.boot.docs.io.caching.provider.cache2k
import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.TimeUnit
@Configuration(proxyBeanMethods = false)
class MyCache2kDefaultsConfiguration {
@Bean
fun myCache2kDefaultsCustomizer(): Cache2kBuilderCustomizer {
return Cache2kBuilderCustomizer { builder ->
builder.entryCapacity(200)
.expireAfterWrite(5, TimeUnit.MINUTES)
}
}
}