diff --git a/pom.xml b/pom.xml index f6bd8cd2..6f5735ff 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,8 @@ - 3.0.1 - 3.0.1 + 3.0.2 + 3.0.2 2.3.0.BUILD-SNAPSHOT spring.data.couchbase @@ -39,7 +39,7 @@ org.springframework - spring-context + spring-context-support org.springframework diff --git a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java index c501ca05..b33d90ea 100644 --- a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java +++ b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCacheManager.java @@ -17,18 +17,21 @@ package org.springframework.data.couchbase.cache; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.springframework.cache.Cache; -import org.springframework.cache.support.AbstractCacheManager; +import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -public class CouchbaseCacheManager extends AbstractCacheManager { +public class CouchbaseCacheManager extends AbstractTransactionSupportingCacheManager { private final CouchbaseCacheWriter cacheWriter; private final CouchbaseCacheConfiguration defaultCacheConfig; @@ -45,20 +48,51 @@ public class CouchbaseCacheManager extends AbstractCacheManager { * @param allowInFlightCacheCreation allow create unconfigured caches. */ private CouchbaseCacheManager(final CouchbaseCacheWriter cacheWriter, - final CouchbaseCacheConfiguration defaultCacheConfiguration, final boolean allowInFlightCacheCreation) { + final CouchbaseCacheConfiguration defaultCacheConfiguration, + final Map initialCacheConfiguration, + final boolean allowInFlightCacheCreation) { Assert.notNull(cacheWriter, "CacheWriter must not be null!"); Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!"); this.cacheWriter = cacheWriter; this.defaultCacheConfig = defaultCacheConfiguration; - this.initialCacheConfiguration = new LinkedHashMap<>(); + this.initialCacheConfiguration = initialCacheConfiguration; this.allowInFlightCacheCreation = allowInFlightCacheCreation; } - public static CouchbaseCacheManager create(final CouchbaseClientFactory connectionFactory) { - return new CouchbaseCacheManager(new DefaultCouchbaseCacheWriter(connectionFactory), - CouchbaseCacheConfiguration.defaultCacheConfig(), true); + /** + * Create a new {@link CouchbaseCacheManager} with defaults applied. + * + * @param clientFactory must not be {@literal null}. + * @return new instance of {@link CouchbaseCacheManager}. + */ + public static CouchbaseCacheManager create(CouchbaseClientFactory clientFactory) { + Assert.notNull(clientFactory, "ConnectionFactory must not be null!"); + return new CouchbaseCacheManager(new DefaultCouchbaseCacheWriter(clientFactory), + CouchbaseCacheConfiguration.defaultCacheConfig(), new LinkedHashMap<>(), true); + } + + /** + * Entry point for builder style {@link CouchbaseCacheManager} configuration. + * + * @param clientFactory must not be {@literal null}. + * @return new {@link CouchbaseCacheManagerBuilder}. + */ + public static CouchbaseCacheManagerBuilder builder(CouchbaseClientFactory clientFactory) { + Assert.notNull(clientFactory, "ConnectionFactory must not be null!"); + return CouchbaseCacheManagerBuilder.fromConnectionFactory(clientFactory); + } + + /** + * Entry point for builder style {@link CouchbaseCacheManager} configuration. + * + * @param cacheWriter must not be {@literal null}. + * @return new {@link CouchbaseCacheManagerBuilder}. + */ + public static CouchbaseCacheManagerBuilder builder(CouchbaseCacheWriter cacheWriter) { + Assert.notNull(cacheWriter, "CacheWriter must not be null!"); + return CouchbaseCacheManagerBuilder.fromCacheWriter(cacheWriter); } @Override @@ -89,4 +123,155 @@ public class CouchbaseCacheManager extends AbstractCacheManager { return new CouchbaseCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig); } + public static class CouchbaseCacheManagerBuilder { + + private final CouchbaseCacheWriter cacheWriter; + private CouchbaseCacheConfiguration defaultCacheConfiguration = CouchbaseCacheConfiguration.defaultCacheConfig(); + private final Map initialCaches = new LinkedHashMap<>(); + private boolean enableTransactions; + boolean allowInFlightCacheCreation = true; + + private CouchbaseCacheManagerBuilder(CouchbaseCacheWriter cacheWriter) { + this.cacheWriter = cacheWriter; + } + + /** + * Entry point for builder style {@link CouchbaseCacheManager} configuration. + * + * @param clientFactory must not be {@literal null}. + * @return new {@link CouchbaseCacheManagerBuilder}. + */ + public static CouchbaseCacheManagerBuilder fromConnectionFactory(CouchbaseClientFactory clientFactory) { + Assert.notNull(clientFactory, "ConnectionFactory must not be null!"); + return builder(new DefaultCouchbaseCacheWriter(clientFactory)); + } + + /** + * Entry point for builder style {@link CouchbaseCacheManager} configuration. + * + * @param cacheWriter must not be {@literal null}. + * @return new {@link CouchbaseCacheManagerBuilder}. + */ + public static CouchbaseCacheManagerBuilder fromCacheWriter(CouchbaseCacheWriter cacheWriter) { + Assert.notNull(cacheWriter, "CacheWriter must not be null!"); + return new CouchbaseCacheManagerBuilder(cacheWriter); + } + + /** + * Define a default {@link CouchbaseCacheConfiguration} applied to dynamically created {@link CouchbaseCache}s. + * + * @param defaultCacheConfiguration must not be {@literal null}. + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder cacheDefaults(CouchbaseCacheConfiguration defaultCacheConfiguration) { + Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!"); + this.defaultCacheConfiguration = defaultCacheConfiguration; + return this; + } + + /** + * Enable {@link CouchbaseCache}s to synchronize cache put/evict operations with ongoing Spring-managed transactions. + * + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder transactionAware() { + this.enableTransactions = true; + return this; + } + + /** + * Append a {@link Set} of cache names to be pre initialized with current {@link CouchbaseCacheConfiguration}. + * NOTE: This calls depends on {@link #cacheDefaults(CouchbaseCacheConfiguration)} using whatever + * default {@link CouchbaseCacheConfiguration} is present at the time of invoking this method. + * + * @param cacheNames must not be {@literal null}. + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder initialCacheNames(Set cacheNames) { + Assert.notNull(cacheNames, "CacheNames must not be null!"); + cacheNames.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration)); + return this; + } + + /** + * Append a {@link Map} of cache name/{@link CouchbaseCacheConfiguration} pairs to be pre initialized. + * + * @param cacheConfigurations must not be {@literal null}. + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder withInitialCacheConfigurations( + Map cacheConfigurations) { + + Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null!"); + cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration, + String.format("CouchbaseCacheConfiguration for cache %s must not be null!", cacheName))); + + this.initialCaches.putAll(cacheConfigurations); + return this; + } + + /** + * @param cacheName + * @param cacheConfiguration + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder withCacheConfiguration(String cacheName, + CouchbaseCacheConfiguration cacheConfiguration) { + + Assert.notNull(cacheName, "CacheName must not be null!"); + Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null!"); + + this.initialCaches.put(cacheName, cacheConfiguration); + return this; + } + + /** + * Disable in-flight {@link org.springframework.cache.Cache} creation for unconfigured caches. + *

+ * {@link CouchbaseCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured + * {@link org.springframework.cache.Cache} instead of a new {@link CouchbaseCache} instance. This allows eg. + * {@link org.springframework.cache.support.CompositeCacheManager} to chime in. + * + * @return this {@link CouchbaseCacheManagerBuilder}. + */ + public CouchbaseCacheManagerBuilder disableCreateOnMissingCache() { + + this.allowInFlightCacheCreation = false; + return this; + } + + /** + * Get the {@link Set} of cache names for which the builder holds {@link CouchbaseCacheConfiguration configuration}. + * + * @return an unmodifiable {@link Set} holding the name of caches for which a {@link CouchbaseCacheConfiguration + * configuration} has been set. + */ + public Set getConfiguredCaches() { + return Collections.unmodifiableSet(this.initialCaches.keySet()); + } + + /** + * Get the {@link CouchbaseCacheConfiguration} for a given cache by its name. + * + * @param cacheName must not be {@literal null}. + * @return {@link Optional#empty()} if no {@link CouchbaseCacheConfiguration} set for the given cache name. + */ + public Optional getCacheConfigurationFor(String cacheName) { + return Optional.ofNullable(this.initialCaches.get(cacheName)); + } + + /** + * Create new instance of {@link CouchbaseCacheManager} with configuration options applied. + * + * @return new instance of {@link CouchbaseCacheManager}. + */ + public CouchbaseCacheManager build() { + CouchbaseCacheManager cm = new CouchbaseCacheManager(cacheWriter, defaultCacheConfiguration, initialCaches, + allowInFlightCacheCreation); + cm.setTransactionAware(enableTransactions); + return cm; + } + + } + } diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index 8f4404e1..52d7346e 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -94,13 +94,13 @@ public abstract class AbstractCouchbaseConfiguration { } - @Bean + @Bean(name = BeanNames.COUCHBASE_TEMPLATE) public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, MappingCouchbaseConverter mappingCouchbaseConverter) { return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); } - @Bean + @Bean(name = BeanNames.REACTIVE_COUCHBASE_TEMPLATE) public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, MappingCouchbaseConverter mappingCouchbaseConverter) { return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); @@ -195,7 +195,7 @@ public abstract class AbstractCouchbaseConfiguration { public CouchbaseMappingContext couchbaseMappingContext() throws Exception { CouchbaseMappingContext mappingContext = new CouchbaseMappingContext(); mappingContext.setInitialEntitySet(getInitialEntitySet()); - mappingContext.setSimpleTypeHolder(couchbaseCustomConversions().getSimpleTypeHolder()); + mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); return mappingContext; } @@ -207,8 +207,8 @@ public abstract class AbstractCouchbaseConfiguration { * * @return must not be {@literal null}. */ - @Bean - public CustomConversions couchbaseCustomConversions() { + @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) + public CustomConversions customConversions() { return new CouchbaseCustomConversions(Collections.emptyList()); } diff --git a/src/main/java/org/springframework/data/couchbase/config/BeanNames.java b/src/main/java/org/springframework/data/couchbase/config/BeanNames.java index 71997f7e..d2ee2c0b 100644 --- a/src/main/java/org/springframework/data/couchbase/config/BeanNames.java +++ b/src/main/java/org/springframework/data/couchbase/config/BeanNames.java @@ -32,14 +32,12 @@ import org.springframework.data.couchbase.core.convert.translation.TranslationSe */ public class BeanNames { - /** - * The name for the default {@link CouchbaseOperations} bean. See - * {@link AbstractCouchbaseConfiguration#couchbaseTemplate(CouchbaseClientFactory, MappingCouchbaseConverter)} )} - * for java config, and the "<couchbase:template />" - * element for xml config. - */ public static final String COUCHBASE_TEMPLATE = "couchbaseTemplate"; + public static final String REACTIVE_COUCHBASE_TEMPLATE = "reactiveCouchbaseTemplate"; + + public static final String COUCHBASE_CUSTOM_CONVERSIONS = "couchbaseCustomConversions"; + /** * The name for the bean that stores custom mapping between repositories and their backing couchbaseOperations. */