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:
Stephane Nicoll
2016-03-17 11:44:56 +01:00
parent 10012cfddc
commit caf11e4445
12 changed files with 253 additions and 3 deletions

View File

@@ -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 {

View File

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

View File

@@ -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.
*/

View File

@@ -51,6 +51,11 @@ public enum CacheType {
*/
INFINISPAN,
/**
* Couchbase backed caching.
*/
COUCHBASE,
/**
* Redis backed caching.
*/

View File

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

View File

@@ -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>() {