diff --git a/src/integration/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfigurationTest.java b/src/integration/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfigurationTest.java new file mode 100644 index 00000000..64abc6fb --- /dev/null +++ b/src/integration/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfigurationTest.java @@ -0,0 +1,162 @@ +package org.springframework.data.couchbase.config; + +import static org.junit.Assert.*; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.document.JsonDocument; +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.annotation.Id; +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; +import org.springframework.stereotype.Repository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * This test case demonstrates that the {@link AbstractCouchbaseDataConfiguration} can take its SDK beans + * from a sibling {@link Configuration}. + * + * @author Simon Baslé + */ +@SuppressWarnings("SpringJavaAutowiringInspection") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class AbstractCouchbaseDataConfigurationTest { + + @Autowired + ItemRepository repository; + + @Autowired + Bucket client; + + @Configuration + static class SdkConfig { + + private static final String IP = "127.0.0.1"; + private static final String BUCKET_NAME = "default"; + private static final String BUCKET_PASSWORD = ""; + + public static Bucket bucket; + + @Bean + public Cluster couchbaseCluster() { + return CouchbaseCluster.create(couchbaseEnv(), IP); + } + + @Bean + public ClusterInfo couchbaseClusterInfo() { + return couchbaseCluster().clusterManager(BUCKET_NAME, BUCKET_PASSWORD).info(); + } + + @Bean + public Bucket couchbaseBucket() { + Bucket b = couchbaseCluster().openBucket(BUCKET_NAME, BUCKET_PASSWORD); + bucket = b; + return b; + } + + @Bean + public CouchbaseEnvironment couchbaseEnv() { + return DefaultCouchbaseEnvironment.create(); + } + } + + @Configuration + @EnableCouchbaseRepositories(basePackageClasses = AbstractCouchbaseDataConfigurationTest.class, considerNestedRepositories = true) + static class Config extends AbstractCouchbaseDataConfiguration { + + @Autowired + Cluster c; + + @Autowired + ClusterInfo ci; + + @Autowired + Bucket b; + + @Autowired + CouchbaseEnvironment e; + + @Override + protected CouchbaseConfigurer couchbaseConfigurer() { + return new TestCouchbaseConfigurer(e, c, ci, b); + } + } + + @Test + public void testInjectedBucketIsFromAdditionalConfig() { + assertSame(client, SdkConfig.bucket); + } + + @Test + public void testTemplateIsUsable() { + String key = "simpleConfigTest"; + assertNotNull(repository); + + Item item = new Item(); + item.id = key; + item.value = "Test if the SimpleCouchbaseConfiguration can correctly get Bucket/Cluster/etc... beans injected"; + + repository.save(item); + JsonDocument testDoc = client.get(key); + + assertNotNull(testDoc); + assertNotNull(testDoc.content()); + assertEquals(item.value, testDoc.content().getString("value")); + } + + private static class Item { + @Id + public String id; + + public String value; + } + + private static class TestCouchbaseConfigurer implements CouchbaseConfigurer { + + private CouchbaseEnvironment env; + private Cluster cluster; + private ClusterInfo info; + private Bucket bucket; + + public TestCouchbaseConfigurer(CouchbaseEnvironment env, Cluster cluster, ClusterInfo info, Bucket bucket) { + this.env = env; + this.cluster = cluster; + this.info = info; + this.bucket = bucket; + } + + @Override + public CouchbaseEnvironment couchbaseEnvironment() throws Exception { + return this.env; + } + + @Override + public Cluster couchbaseCluster() throws Exception { + return this.cluster; + } + + @Override + public ClusterInfo couchbaseClusterInfo() throws Exception { + return this.info; + } + + @Override + public Bucket couchbaseClient() throws Exception { + return this.bucket; + } + } + + @Repository + interface ItemRepository extends CouchbaseRepository {} +} 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 f48cc0c7..639312a5 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -16,10 +16,7 @@ package org.springframework.data.couchbase.config; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; @@ -27,44 +24,20 @@ import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.java.cluster.ClusterInfo; import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import com.couchbase.client.java.query.N1qlQuery; -import com.couchbase.client.java.view.ViewQuery; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.data.annotation.Persistent; -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.data.couchbase.core.convert.CustomConversions; -import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; -import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService; -import org.springframework.data.couchbase.core.convert.translation.TranslationService; -import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; -import org.springframework.data.couchbase.core.mapping.Document; -import org.springframework.data.couchbase.core.query.Consistency; -import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; -import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed; -import org.springframework.data.couchbase.core.query.ViewIndexed; -import org.springframework.data.couchbase.repository.CouchbaseRepository; -import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; -import org.springframework.data.couchbase.repository.support.IndexManager; -import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; -import org.springframework.data.mapping.model.FieldNamingStrategy; -import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Base class for Spring Data Couchbase configuration using JavaConfig. * * @author Michael Nitschinger * @author Simon Baslé + * @author Stephane Nicoll */ @Configuration -public abstract class AbstractCouchbaseConfiguration { +public abstract class AbstractCouchbaseConfiguration + extends AbstractCouchbaseDataConfiguration implements CouchbaseConfigurer { /** * The list of hostnames (or IP addresses) to bootstrap from. @@ -108,6 +81,12 @@ public abstract class AbstractCouchbaseConfiguration { return DefaultCouchbaseEnvironment.create(); } + @Override + protected CouchbaseConfigurer couchbaseConfigurer() { + return this; + } + + @Override @Bean(destroyMethod = "shutdown", name = BeanNames.COUCHBASE_ENV) public CouchbaseEnvironment couchbaseEnvironment() { CouchbaseEnvironment env = getEnvironment(); @@ -122,11 +101,13 @@ public abstract class AbstractCouchbaseConfiguration { * * @throws Exception on Bean construction failure. */ + @Override @Bean(destroyMethod = "disconnect", name = BeanNames.COUCHBASE_CLUSTER) public Cluster couchbaseCluster() throws Exception { return CouchbaseCluster.create(couchbaseEnvironment(), getBootstrapHosts()); } + @Override @Bean(name = BeanNames.COUCHBASE_CLUSTER_INFO) public ClusterInfo couchbaseClusterInfo() throws Exception { return couchbaseCluster().clusterManager(getBucketName(), getBucketPassword()).info(); @@ -137,192 +118,10 @@ public abstract class AbstractCouchbaseConfiguration { * * @throws Exception on Bean construction failure. */ + @Override @Bean(destroyMethod = "close", name = BeanNames.COUCHBASE_BUCKET) public Bucket couchbaseClient() throws Exception { //@Bean method can use another @Bean method in the same @Configuration by directly invoking it return couchbaseCluster().openBucket(getBucketName(), getBucketPassword()); } - - /** - * Creates a {@link CouchbaseTemplate}. - * - * This uses {@link #couchbaseClusterInfo()}, {@link #couchbaseClient()}, {@link #mappingCouchbaseConverter()}, - * , {@link #translationService()} and {@link #getDefaultConsistency()} for construction. - * - * @throws Exception on Bean construction failure. - */ - @Bean(name = BeanNames.COUCHBASE_TEMPLATE) - public CouchbaseTemplate couchbaseTemplate() throws Exception { - CouchbaseTemplate template = new CouchbaseTemplate(couchbaseClusterInfo(), couchbaseClient(), mappingCouchbaseConverter(), translationService()); - template.setDefaultConsistency(getDefaultConsistency()); - return template; - } - - /** - * Creates the {@link RepositoryOperationsMapping} bean which will be used by the framework to choose which - * {@link CouchbaseOperations} should back which {@link CouchbaseRepository}. - * Override {@link #configureRepositoryOperationsMapping(RepositoryOperationsMapping)} in order to customize this. - * - * @throws Exception - */ - @Bean(name = BeanNames.COUCHBASE_OPERATIONS_MAPPING) - public RepositoryOperationsMapping repositoryOperationsMapping() throws Exception { - //create a base mapping that associates all repositories to the default template - RepositoryOperationsMapping baseMapping = new RepositoryOperationsMapping(couchbaseTemplate()); - //let the user tune it - configureRepositoryOperationsMapping(baseMapping); - return baseMapping; - } - - /** - * In order to customize the mapping between repositories/entity types to couchbase templates, - * use the provided mapping's api (eg. in order to have different buckets backing different repositories). - * - * @param mapping the default mapping (will associate all repositories to the default template). - */ - protected void configureRepositoryOperationsMapping(RepositoryOperationsMapping mapping) { - //NO_OP - } - - /** - * Determines the name of the field that will store the type information for complex types when - * using the {@link #mappingCouchbaseConverter()}. - * Defaults to {@value MappingCouchbaseConverter#TYPEKEY_DEFAULT}. - * - * @see MappingCouchbaseConverter#TYPEKEY_DEFAULT - * @see MappingCouchbaseConverter#TYPEKEY_SYNCGATEWAY_COMPATIBLE - */ - public String typeKey() { - return MappingCouchbaseConverter.TYPEKEY_DEFAULT; - } - - /** - * Creates a {@link MappingCouchbaseConverter} using the configured {@link #couchbaseMappingContext}. - * - * @throws Exception on Bean construction failure. - */ - @Bean(name = BeanNames.COUCHBASE_MAPPING_CONVERTER) - public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception { - MappingCouchbaseConverter converter = new MappingCouchbaseConverter(couchbaseMappingContext(), typeKey()); - converter.setCustomConversions(customConversions()); - return converter; - } - - /** - * Creates a {@link TranslationService}. - * - * @return TranslationService, defaulting to JacksonTranslationService. - */ - @Bean(name = BeanNames.COUCHBASE_TRANSLATION_SERVICE) - public TranslationService translationService() { - final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService(); - jacksonTranslationService.afterPropertiesSet(); - return jacksonTranslationService; - } - - /** - * Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package. - * - * @throws Exception on Bean construction failure. - */ - @Bean(name = BeanNames.COUCHBASE_MAPPING_CONTEXT) - public CouchbaseMappingContext couchbaseMappingContext() throws Exception { - CouchbaseMappingContext mappingContext = new CouchbaseMappingContext(); - mappingContext.setInitialEntitySet(getInitialEntitySet()); - mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); - mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); - return mappingContext; - } - - /** - * Register custom Converters in a {@link CustomConversions} object if required. These - * {@link CustomConversions} will be registered with the {@link #mappingCouchbaseConverter()} and - * {@link #couchbaseMappingContext()}. Returns an empty {@link CustomConversions} instance by default. - * - * @return must not be {@literal null}. - */ - @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) - public CustomConversions customConversions() { - return new CustomConversions(Collections.emptyList()); - } - - /** - * Register an {@link IndexManager} bean that will be used to process {@link ViewIndexed}, - * {@link N1qlPrimaryIndexed} and {@link N1qlSecondaryIndexed} annotations on repositories - * to automatically create indexes. By default, since such automatic creations are discouraged in - * production envrironment, the configuration will assume the worst and will ignore these annotations. - *

- * If you are sure this configuration used in a context where such automatic creations are desired (eg. - * you want automatic index creation in Dev, just not in Prod, and this configuration is the Dev one), - * override the bean and use the {@link IndexManager#IndexManager()} constructor (or - * {@link IndexManager#IndexManager(boolean, boolean, boolean)} constructor with appropriate flags set to true to - * activate). - */ - @Bean(name = BeanNames.COUCHBASE_INDEX_MANAGER) - public IndexManager indexManager() { - return new IndexManager(false, false, false); //this ignores view, N1QL primary and secondary annotations - } - - /** - * Scans the mapping base package for classes annotated with {@link Document}. - * - * @throws ClassNotFoundException if initial entity sets could not be loaded. - */ - protected Set> getInitialEntitySet() throws ClassNotFoundException { - String basePackage = getMappingBasePackage(); - Set> initialEntitySet = new HashSet>(); - - if (StringUtils.hasText(basePackage)) { - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); - for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { - initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), AbstractCouchbaseConfiguration.class.getClassLoader())); - } - } - - return initialEntitySet; - } - - /** - * Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration - * class (the concrete class, not this one here) by default. - *

- *

So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package - * will be considered {@code com.acme} unless the method is overridden to implement alternate behavior.

- * - * @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for - * entities. - */ - protected String getMappingBasePackage() { - return getClass().getPackage().getName(); - } - - /** - * Set to true if field names should be abbreviated with the {@link CamelCaseAbbreviatingFieldNamingStrategy}. - * - * @return true if field names should be abbreviated, default is false. - */ - protected boolean abbreviateFieldNames() { - return false; - } - - /** - * Configures a {@link FieldNamingStrategy} on the {@link CouchbaseMappingContext} instance created. - * - * @return the naming strategy. - */ - protected FieldNamingStrategy fieldNamingStrategy() { - return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy() : PropertyNameFieldNamingStrategy.INSTANCE; - } - - /** - * Configures the default consistency for generated {@link ViewQuery view queries} - * and {@link N1qlQuery N1QL queries} in repositories. - * - * @return the {@link Consistency consistency} to apply by default on generated queries. - */ - protected Consistency getDefaultConsistency() { - return Consistency.DEFAULT_CONSISTENCY; - } } \ No newline at end of file diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfiguration.java new file mode 100644 index 00000000..de09b46d --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseDataConfiguration.java @@ -0,0 +1,249 @@ +/* + * 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.data.couchbase.config; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.view.ViewQuery; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.data.couchbase.core.convert.CustomConversions; +import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; +import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService; +import org.springframework.data.couchbase.core.convert.translation.TranslationService; +import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; +import org.springframework.data.couchbase.core.mapping.Document; +import org.springframework.data.couchbase.core.query.Consistency; +import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; +import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed; +import org.springframework.data.couchbase.core.query.ViewIndexed; +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.support.IndexManager; +import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; +import org.springframework.data.mapping.model.FieldNamingStrategy; +import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +@Configuration +public abstract class AbstractCouchbaseDataConfiguration { + + protected abstract CouchbaseConfigurer couchbaseConfigurer(); + + /** + * Creates a {@link CouchbaseTemplate}. + * + * This uses {@link #mappingCouchbaseConverter()}, {@link #translationService()} and {@link #getDefaultConsistency()} + * for construction. + * + * Additionally, it will expect injection of a {@link ClusterInfo} and a {@link Bucket} beans from the context (most + * probably from another configuration). For a self-sufficient configuration that defines such beans, see + * {@link AbstractCouchbaseConfiguration}. + * + * @throws Exception on Bean construction failure. + */ + @Bean(name = BeanNames.COUCHBASE_TEMPLATE) + public CouchbaseTemplate couchbaseTemplate() throws Exception { + CouchbaseTemplate template = new CouchbaseTemplate(couchbaseConfigurer().couchbaseClusterInfo(), + couchbaseConfigurer().couchbaseClient(), mappingCouchbaseConverter(), translationService()); + template.setDefaultConsistency(getDefaultConsistency()); + return template; + } + + /** + * Creates the {@link RepositoryOperationsMapping} bean which will be used by the framework to choose which + * {@link CouchbaseOperations} should back which {@link CouchbaseRepository}. + * Override {@link #configureRepositoryOperationsMapping(RepositoryOperationsMapping)} in order to customize this. + * + * @throws Exception + */ + @Bean(name = BeanNames.COUCHBASE_OPERATIONS_MAPPING) + public RepositoryOperationsMapping repositoryOperationsMapping(CouchbaseTemplate couchbaseTemplate) throws Exception { + //create a base mapping that associates all repositories to the default template + RepositoryOperationsMapping baseMapping = new RepositoryOperationsMapping(couchbaseTemplate); + //let the user tune it + configureRepositoryOperationsMapping(baseMapping); + return baseMapping; + } + + /** + * In order to customize the mapping between repositories/entity types to couchbase templates, + * use the provided mapping's api (eg. in order to have different buckets backing different repositories). + * + * @param mapping the default mapping (will associate all repositories to the default template). + */ + protected void configureRepositoryOperationsMapping(RepositoryOperationsMapping mapping) { + //NO_OP + } + + /** + * Determines the name of the field that will store the type information for complex types when + * using the {@link #mappingCouchbaseConverter()}. + * Defaults to {@value MappingCouchbaseConverter#TYPEKEY_DEFAULT}. + * + * @see MappingCouchbaseConverter#TYPEKEY_DEFAULT + * @see MappingCouchbaseConverter#TYPEKEY_SYNCGATEWAY_COMPATIBLE + */ + public String typeKey() { + return MappingCouchbaseConverter.TYPEKEY_DEFAULT; + } + + /** + * Creates a {@link MappingCouchbaseConverter} using the configured {@link #couchbaseMappingContext}. + * + * @throws Exception on Bean construction failure. + */ + @Bean(name = BeanNames.COUCHBASE_MAPPING_CONVERTER) + public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception { + MappingCouchbaseConverter converter = new MappingCouchbaseConverter(couchbaseMappingContext(), typeKey()); + converter.setCustomConversions(customConversions()); + return converter; + } + + /** + * Creates a {@link TranslationService}. + * + * @return TranslationService, defaulting to JacksonTranslationService. + */ + @Bean(name = BeanNames.COUCHBASE_TRANSLATION_SERVICE) + public TranslationService translationService() { + final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService(); + jacksonTranslationService.afterPropertiesSet(); + return jacksonTranslationService; + } + + /** + * Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package. + * + * @throws Exception on Bean construction failure. + */ + @Bean(name = BeanNames.COUCHBASE_MAPPING_CONTEXT) + public CouchbaseMappingContext couchbaseMappingContext() throws Exception { + CouchbaseMappingContext mappingContext = new CouchbaseMappingContext(); + mappingContext.setInitialEntitySet(getInitialEntitySet()); + mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); + mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); + return mappingContext; + } + + /** + * Register custom Converters in a {@link CustomConversions} object if required. These + * {@link CustomConversions} will be registered with the {@link #mappingCouchbaseConverter()} and + * {@link #couchbaseMappingContext()}. Returns an empty {@link CustomConversions} instance by default. + * + * @return must not be {@literal null}. + */ + @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) + public CustomConversions customConversions() { + return new CustomConversions(Collections.emptyList()); + } + + /** + * Register an {@link IndexManager} bean that will be used to process {@link ViewIndexed}, + * {@link N1qlPrimaryIndexed} and {@link N1qlSecondaryIndexed} annotations on repositories + * to automatically create indexes. By default, since such automatic creations are discouraged in + * production envrironment, the configuration will assume the worst and will ignore these annotations. + *

+ * If you are sure this configuration used in a context where such automatic creations are desired (eg. + * you want automatic index creation in Dev, just not in Prod, and this configuration is the Dev one), + * override the bean and use the {@link IndexManager#IndexManager()} constructor (or + * {@link IndexManager#IndexManager(boolean, boolean, boolean)} constructor with appropriate flags set to true to + * activate). + */ + @Bean(name = BeanNames.COUCHBASE_INDEX_MANAGER) + public IndexManager indexManager() { + return new IndexManager(false, false, false); //this ignores view, N1QL primary and secondary annotations + } + + /** + * Scans the mapping base package for classes annotated with {@link Document}. + * + * @throws ClassNotFoundException if initial entity sets could not be loaded. + */ + protected Set> getInitialEntitySet() throws ClassNotFoundException { + String basePackage = getMappingBasePackage(); + Set> initialEntitySet = new HashSet>(); + + if (StringUtils.hasText(basePackage)) { + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), AbstractCouchbaseConfiguration.class.getClassLoader())); + } + } + + return initialEntitySet; + } + + /** + * Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration + * class (the concrete class, not this one here) by default. + *

+ *

So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package + * will be considered {@code com.acme} unless the method is overridden to implement alternate behavior.

+ * + * @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for + * entities. + */ + protected String getMappingBasePackage() { + return getClass().getPackage().getName(); + } + + /** + * Set to true if field names should be abbreviated with the {@link CamelCaseAbbreviatingFieldNamingStrategy}. + * + * @return true if field names should be abbreviated, default is false. + */ + protected boolean abbreviateFieldNames() { + return false; + } + + /** + * Configures a {@link FieldNamingStrategy} on the {@link CouchbaseMappingContext} instance created. + * + * @return the naming strategy. + */ + protected FieldNamingStrategy fieldNamingStrategy() { + return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy() : PropertyNameFieldNamingStrategy.INSTANCE; + } + + /** + * Configures the default consistency for generated {@link ViewQuery view queries} + * and {@link N1qlQuery N1QL queries} in repositories. + * + * @return the {@link Consistency consistency} to apply by default on generated queries. + */ + protected Consistency getDefaultConsistency() { + return Consistency.DEFAULT_CONSISTENCY; + } +} diff --git a/src/main/java/org/springframework/data/couchbase/config/CouchbaseConfigurer.java b/src/main/java/org/springframework/data/couchbase/config/CouchbaseConfigurer.java new file mode 100644 index 00000000..4d238f9b --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/CouchbaseConfigurer.java @@ -0,0 +1,50 @@ +package org.springframework.data.couchbase.config; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.env.CouchbaseEnvironment; + +import org.springframework.data.couchbase.core.CouchbaseOperations; + +/** + * Strategy interface for users to provide as a factory for custom components needed + * by the Couchbase integration. + * + * This allows to centralize instantiation of Couchbase SDK core elements. + * + * @author Stephane Nicoll + */ +public interface CouchbaseConfigurer { + + /** + * Set up the underlying main {@link CouchbaseEnvironment}, allowing tuning of the Couchbase SDK. + * + * @throws Exception in case of error during the CouchbaseEnvironment instantiation. + */ + CouchbaseEnvironment couchbaseEnvironment() throws Exception; + + /** + * Set up the underlying main Couchbase {@link Cluster} reference to be used by the Spring Data framework + * when storing into Couchbase. + * + * @throws Exception in case of error during the Cluster instantiation. + */ + Cluster couchbaseCluster() throws Exception; + + /** + * Set up the underlying main {@link ClusterInfo}, allowing to check feature availability and cluster configuration. + * + * @throws Exception in case of error during the ClusterInfo instantiation. + */ + ClusterInfo couchbaseClusterInfo() throws Exception; + + /** + * Set up the underlying main {@link Bucket}, the primary Couchbase SDK entry point to be used by the Spring Data + * framework for the {@link CouchbaseOperations}. + * + * @throws Exception in case of error during the bucket instantiation. + */ + Bucket couchbaseClient() throws Exception; + +}