Add Couchbase cache support
This commit updates the cache auto-configuration to provide a `CouchbaseCacheManager` if a `Bucket` has been configured. The global customizer infrastructure allows to further tune the cache manager if necessary. Closes gh-5176
This commit is contained in:
parent
10012cfddc
commit
caf11e4445
|
|
@ -35,6 +35,11 @@
|
|||
<artifactId>transactions-jta</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.couchbase.client</groupId>
|
||||
<artifactId>couchbase-spring-cache</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.Cache
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
|
||||
|
|
@ -64,7 +65,8 @@ import org.springframework.util.Assert;
|
|||
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
|
||||
@EnableConfigurationProperties(CacheProperties.class)
|
||||
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ HazelcastAutoConfiguration.class, RedisAutoConfiguration.class })
|
||||
@AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class })
|
||||
@Import({ CacheManagerCustomizers.class, CacheConfigurationImportSelector.class })
|
||||
public class CacheAutoConfiguration {
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ final class CacheConfigurations {
|
|||
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
|
||||
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
|
||||
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
|
||||
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
|
||||
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
|
||||
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
|
||||
mappings.put(CacheType.GUAVA, GuavaCacheConfiguration.class);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ public class CacheProperties {
|
|||
|
||||
private final Caffeine caffeine = new Caffeine();
|
||||
|
||||
private final Couchbase couchbase = new Couchbase();
|
||||
|
||||
private final EhCache ehcache = new EhCache();
|
||||
|
||||
private final Hazelcast hazelcast = new Hazelcast();
|
||||
|
|
@ -76,6 +78,10 @@ public class CacheProperties {
|
|||
return this.caffeine;
|
||||
}
|
||||
|
||||
public Couchbase getCouchbase() {
|
||||
return this.couchbase;
|
||||
}
|
||||
|
||||
public EhCache getEhcache() {
|
||||
return this.ehcache;
|
||||
}
|
||||
|
|
@ -133,6 +139,26 @@ public class CacheProperties {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Couchbase specific cache properties.
|
||||
*/
|
||||
public static class Couchbase {
|
||||
|
||||
/**
|
||||
* Entry expiration in milliseconds. By default the entries never expire.
|
||||
*/
|
||||
private int expiration;
|
||||
|
||||
public int getExpiration() {
|
||||
return this.expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(int expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* EhCache specific cache properties.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ public enum CacheType {
|
|||
*/
|
||||
INFINISPAN,
|
||||
|
||||
/**
|
||||
* Couchbase backed caching.
|
||||
*/
|
||||
COUCHBASE,
|
||||
|
||||
/**
|
||||
* Redis backed caching.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.List;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.spring.cache.CacheBuilder;
|
||||
import com.couchbase.client.spring.cache.CouchbaseCacheManager;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Couchbase cache configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({Bucket.class, CouchbaseCacheManager.class})
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@ConditionalOnSingleCandidate(Bucket.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
public class CouchbaseCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
private final Bucket bucket;
|
||||
|
||||
public CouchbaseCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers, Bucket bucket) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CouchbaseCacheManager cacheManager() {
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
CouchbaseCacheManager cacheManager = new CouchbaseCacheManager(
|
||||
CacheBuilder.newInstance(this.bucket)
|
||||
.withExpirationInMillis(this.cacheProperties.getCouchbase().getExpiration()),
|
||||
cacheNames.toArray(new String[cacheNames.size()]));
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,6 +30,10 @@ import javax.cache.configuration.MutableConfiguration;
|
|||
import javax.cache.expiry.CreatedExpiryPolicy;
|
||||
import javax.cache.expiry.Duration;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.java.bucket.BucketManager;
|
||||
import com.couchbase.client.spring.cache.CouchbaseCache;
|
||||
import com.couchbase.client.spring.cache.CouchbaseCacheManager;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.CaffeineSpec;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
|
@ -207,6 +211,43 @@ public class CacheAutoConfigurationTests {
|
|||
assertThat(cacheManager.getCacheNames()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseCacheExplicit() {
|
||||
load(CouchbaseCacheConfiguration.class, "spring.cache.type=couchbase");
|
||||
CouchbaseCacheManager cacheManager = validateCacheManager(CouchbaseCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseCacheWithCustomizers() {
|
||||
testCustomizers(CouchbaseCacheAndCustomizersConfiguration.class, "couchbase",
|
||||
"allCacheManagerCustomizer", "couchbaseCacheManagerCustomizer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseCacheExplicitWithCaches() {
|
||||
load(CouchbaseCacheConfiguration.class, "spring.cache.type=couchbase",
|
||||
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
|
||||
CouchbaseCacheManager cacheManager = validateCacheManager(CouchbaseCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames()).containsOnly("foo", "bar");
|
||||
Cache cache = cacheManager.getCache("foo");
|
||||
assertThat(cache).isInstanceOf(CouchbaseCache.class);
|
||||
assertThat(((CouchbaseCache) cache).getTtl()).isEqualTo(0);
|
||||
assertThat(((CouchbaseCache) cache).getNativeCache()).isEqualTo(this.context.getBean("bucket"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseCacheExplicitWithTtl() {
|
||||
load(CouchbaseCacheConfiguration.class, "spring.cache.type=couchbase",
|
||||
"spring.cache.cacheNames=foo,bar", "spring.cache.couchbase.expiration=2000");
|
||||
CouchbaseCacheManager cacheManager = validateCacheManager(CouchbaseCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames()).containsOnly("foo", "bar");
|
||||
Cache cache = cacheManager.getCache("foo");
|
||||
assertThat(cache).isInstanceOf(CouchbaseCache.class);
|
||||
assertThat(((CouchbaseCache) cache).getTtl()).isEqualTo(2000);
|
||||
assertThat(((CouchbaseCache) cache).getNativeCache()).isEqualTo(this.context.getBean("bucket"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisCacheExplicit() {
|
||||
load(RedisCacheConfiguration.class, "spring.cache.type=redis");
|
||||
|
|
@ -733,6 +774,26 @@ public class CacheAutoConfigurationTests {
|
|||
static class GenericCacheAndCustomizersConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class CouchbaseCacheConfiguration {
|
||||
|
||||
@Bean
|
||||
public Bucket bucket() {
|
||||
BucketManager bucketManager = mock(BucketManager.class);
|
||||
Bucket bucket = mock(Bucket.class);
|
||||
given(bucket.bucketManager()).willReturn(bucketManager);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ CouchbaseCacheConfiguration.class, CacheManagerCustomizersConfiguration.class })
|
||||
static class CouchbaseCacheAndCustomizersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class RedisCacheConfiguration {
|
||||
|
|
@ -955,6 +1016,12 @@ public class CacheAutoConfigurationTests {
|
|||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManagerCustomizer<CouchbaseCacheManager> couchbaseCacheManagerCustomizer() {
|
||||
return new CacheManagerTestCustomizer<CouchbaseCacheManager>() {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManagerCustomizer<RedisCacheManager> redisCacheManagerCustomizer() {
|
||||
return new CacheManagerTestCustomizer<RedisCacheManager>() {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
<commons-pool.version>1.6</commons-pool.version>
|
||||
<commons-pool2.version>2.4.2</commons-pool2.version>
|
||||
<couchbase-client.version>2.2.3</couchbase-client.version>
|
||||
<couchbase-cache-client.version>2.0.0</couchbase-cache-client.version>
|
||||
<crashub.version>1.3.2</crashub.version>
|
||||
<derby.version>10.12.1.1</derby.version>
|
||||
<dropwizard-metrics.version>3.1.2</dropwizard-metrics.version>
|
||||
|
|
@ -525,6 +526,11 @@
|
|||
<artifactId>java-client</artifactId>
|
||||
<version>${couchbase-client.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.couchbase.client</groupId>
|
||||
<artifactId>couchbase-spring-cache</artifactId>
|
||||
<version>${couchbase-cache-client.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.datastax.cassandra</groupId>
|
||||
<artifactId>cassandra-driver-core</artifactId>
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ content into your application; rather pick only the properties that you need.
|
|||
# SPRING CACHE ({sc-spring-boot-autoconfigure}/cache/CacheProperties.{sc-ext}[CacheProperties])
|
||||
spring.cache.cache-names= # Comma-separated list of cache names to create if supported by the underlying cache manager.
|
||||
spring.cache.caffeine.spec= # The spec to use to create caches. Check CaffeineSpec for more details on the spec format.
|
||||
spring.cache.couchbase.expiration=0 # Entry expiration in milliseconds. By default the entries never expire.
|
||||
spring.cache.ehcache.config= # The location of the configuration file to use to initialize EhCache.
|
||||
spring.cache.guava.spec= # The spec to use to create caches. Check CacheBuilderSpec for more details on the spec format.
|
||||
spring.cache.hazelcast.config= # The location of the configuration file to use to initialize Hazelcast.
|
||||
|
|
|
|||
|
|
@ -3311,6 +3311,7 @@ providers (in this order):
|
|||
* <<boot-features-caching-provider-ehcache2,EhCache 2.x>>
|
||||
* <<boot-features-caching-provider-hazelcast,Hazelcast>>
|
||||
* <<boot-features-caching-provider-infinispan,Infinispan>>
|
||||
* <<boot-features-caching-provider-couchbase,Couchbase>>
|
||||
* <<boot-features-caching-provider-redis,Redis>>
|
||||
* <<boot-features-caching-provider-caffeine,Caffeine>>
|
||||
* <<boot-features-caching-provider-guava,Guava>>
|
||||
|
|
@ -3320,8 +3321,8 @@ It is also possible to _force_ the cache provider to use via the `spring.cache.t
|
|||
property.
|
||||
|
||||
If the `CacheManager` is auto-configured by Spring Boot, you can further tune its
|
||||
configuration before it is fully initialized by exposing a bean implementing the
|
||||
`CacheManagerCustomizer` interface. The following set the cache names to use.
|
||||
configuration before it is fully initialized by exposing a bean implementing the
|
||||
`CacheManagerCustomizer` interface. The following sets the cache names to use.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
|
|
@ -3433,6 +3434,56 @@ Caches can be created on startup via the `spring.cache.cache-names` property. If
|
|||
|
||||
|
||||
|
||||
[[boot-features-caching-provider-couchbase]]
|
||||
==== Couchbase
|
||||
If Couchbase is available and <<boot-features-couchbase,configured>>, a
|
||||
`CouchbaseCacheManager` is auto-configured. It is also possible to create additional
|
||||
caches on startup using the `spring.cache.cache-names` property. These will operate on
|
||||
the `Bucket` that was auto-configured. You can _also_ create additional caches on another
|
||||
`Bucket` using the customizer: assume you need two caches on the "main" `Bucket` (`foo`
|
||||
and `bar`) and one `biz` cache with a custom time to live of 2sec on the `another`
|
||||
`Bucket`. First, you can create the two first caches simply via configuration:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.cache.cache-names=foo,bar
|
||||
----
|
||||
|
||||
Then define this extra `@Configuration` to configure the extra `Bucket` and the `biz`
|
||||
cache:
|
||||
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
public class CouchbaseCacheConfiguration {
|
||||
|
||||
private final Cluster cluster;
|
||||
|
||||
public CouchbaseCacheConfiguration(Cluster cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Bucket anotherBucket() {
|
||||
return this.cluster.openBucket("another", "secret");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
|
||||
return c -> {
|
||||
c.prepareCache("biz", CacheBuilder.newInstance(anotherBucket())
|
||||
.withExpirationInMillis(2000));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
This sample configuration reuses the `Cluster` that was created via auto-configuration.
|
||||
|
||||
|
||||
|
||||
[[boot-features-caching-provider-redis]]
|
||||
==== Redis
|
||||
If Redis is available and configured, the `RedisCacheManager` is auto-configured. It is
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ abstraction is supported by many caching libraries, including:
|
|||
* `EhCache`
|
||||
* `Hazelcast`
|
||||
* `Infinispan`
|
||||
* `Couchbase`
|
||||
* `Redis`
|
||||
* `Guava`
|
||||
* Simple provider based on `ConcurrentHashMap`
|
||||
|
|
@ -78,6 +79,12 @@ can set the `spring.cache.infinispan.config` property to use the provided
|
|||
|
||||
|
||||
|
||||
=== Couchbase
|
||||
Add the `java-client` and `couchbase-spring-cache` dependencies and make sure that you
|
||||
have setup at least a `spring.couchbase.bootstrap-hosts` property.
|
||||
|
||||
|
||||
|
||||
=== Redis
|
||||
Add the `spring-boot-starter-data-redis` and make sure it is configured properly (by default,
|
||||
a redis instance with the default settings is expected on your local box).
|
||||
|
|
|
|||
|
|
@ -69,6 +69,16 @@
|
|||
</dependency>
|
||||
-->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.couchbase.client</groupId>
|
||||
<artifactId>java-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.couchbase.client</groupId>
|
||||
<artifactId>couchbase-spring-cache</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
|
|
|
|||
Loading…
Reference in New Issue