From 80543250a2a4fa2f35a83b6f4dcbac958db61a1f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 29 Nov 2017 11:07:10 +0000 Subject: [PATCH] Rework properties for enabling Spring Data repositories Rather than using two properties to enable or disable reactive and imperative repositories for a particular store, this commit introduces a new repository type condition that's backed by a single spring.data..repositories.type property. The type can be auto (automatically enables whatever's available), imperative (enables imperative repositories), none (enables nothing), or reactive (enables reactive repositories). The default is auto. Repositories do not have a reactive option (such as JPA) continue to have a spring.data..repositories.enabled property that takes a boolean value. Closes gh-11134 --- .../data/ConditionalOnRepositoryType.java | 52 +++++++ .../data/OnRepositoryTypeCondition.java | 63 +++++++++ .../autoconfigure/data/RepositoryType.java | 47 +++++++ ...ReactiveRepositoriesAutoConfiguration.java | 5 +- ...assandraRepositoriesAutoConfiguration.java | 5 +- ...ReactiveRepositoriesAutoConfiguration.java | 5 +- ...ouchbaseRepositoriesAutoConfiguration.java | 5 +- ...ReactiveRepositoriesAutoConfiguration.java | 5 +- .../MongoRepositoriesAutoConfiguration.java | 5 +- ...itional-spring-configuration-metadata.json | 42 ++++-- .../ConditionalOnRepositoryTypeTests.java | 131 ++++++++++++++++++ ...iveRepositoriesAutoConfigurationTests.java | 83 ++++++----- ...draRepositoriesAutoConfigurationTests.java | 78 ++++++----- ...veRepositoriesAutoConfigurationTests.java} | 8 +- ...iveRepositoriesAutoConfigurationTests.java | 11 +- ...aseRepositoriesAutoConfigurationTests.java | 10 +- ...ngoRepositoriesAutoConfigurationTests.java | 2 +- ...iveRepositoriesAutoConfigurationTests.java | 83 +++++------ ...ngoRepositoriesAutoConfigurationTests.java | 81 +++++------ .../appendix-application-properties.adoc | 9 +- .../src/main/asciidoc/howto.adoc | 9 +- 21 files changed, 543 insertions(+), 196 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryType.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/RepositoryType.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryTypeTests.java rename spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/{CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests.java => CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java} (92%) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryType.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryType.java new file mode 100644 index 0000000000..dbe5adc88f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryType.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2017 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.data; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; + +/** + * {@link Conditional} that only matches when a particular type of Spring Data repository + * has been enabled. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(OnRepositoryTypeCondition.class) +public @interface ConditionalOnRepositoryType { + + /** + * The name of the store that backs the repositories. + * @return the store + */ + String store(); + + /** + * The required repository type. + * @return the required repository type + */ + RepositoryType type(); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java new file mode 100644 index 0000000000..bddc49ad41 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2017 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.data; + +import java.util.Locale; +import java.util.Map; + +import org.springframework.boot.autoconfigure.condition.ConditionMessage; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * {@link SpringBootCondition} for controlling what type of Spring Data repositories are + * auto-configured. + * + * @author Andy Wilkinson + */ +class OnRepositoryTypeCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + Map attributes = metadata.getAnnotationAttributes( + ConditionalOnRepositoryType.class.getName(), true); + RepositoryType configuredType = getTypeProperty(context.getEnvironment(), + (String) attributes.get("store")); + RepositoryType requiredType = (RepositoryType) attributes.get("type"); + ConditionMessage.Builder message = ConditionMessage + .forCondition(ConditionalOnRepositoryType.class); + if (configuredType == requiredType || configuredType == RepositoryType.AUTO) { + return ConditionOutcome.match(message.because("configured type of '" + + configuredType.name() + "' matched required type")); + } + return ConditionOutcome + .noMatch(message.because("configured type (" + configuredType.name() + + ") did not match required type (" + requiredType.name() + ")")); + } + + private RepositoryType getTypeProperty(Environment environment, String store) { + return RepositoryType.valueOf(environment + .getProperty(String.format("spring.data.%s.repositories.type", store), + "auto") + .toUpperCase(Locale.ENGLISH)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/RepositoryType.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/RepositoryType.java new file mode 100644 index 0000000000..2c1ccaea9f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/RepositoryType.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2017 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.data; + +/** + * Type of Spring Data repositories to enable. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public enum RepositoryType { + + /** + * Enables all repository types automatically based on their availability. + */ + AUTO, + + /** + * Enables imperative repositories. + */ + IMPERATIVE, + + /** + * Enables no repositories. + */ + NONE, + + /** + * Enables reactive repositories. + */ + REACTIVE; + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfiguration.java index d75d44a4c8..fb6c88628c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfiguration.java @@ -20,7 +20,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.cassandra.ReactiveSession; @@ -39,7 +40,7 @@ import org.springframework.data.cassandra.repository.support.ReactiveCassandraRe */ @Configuration @ConditionalOnClass({ ReactiveSession.class, ReactiveCassandraRepository.class }) -@ConditionalOnProperty(prefix = "spring.data.cassandra.reactiverepositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "cassandra", type = RepositoryType.REACTIVE) @ConditionalOnMissingBean(ReactiveCassandraRepositoryFactoryBean.class) @Import(CassandraReactiveRepositoriesAutoConfigureRegistrar.class) @AutoConfigureAfter(CassandraReactiveDataAutoConfiguration.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfiguration.java index 1b570472db..7dce53d1e4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfiguration.java @@ -21,7 +21,8 @@ import com.datastax.driver.core.Session; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.cassandra.repository.CassandraRepository; @@ -38,7 +39,7 @@ import org.springframework.data.cassandra.repository.support.CassandraRepository */ @Configuration @ConditionalOnClass({ Session.class, CassandraRepository.class }) -@ConditionalOnProperty(prefix = "spring.data.cassandra.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "cassandra", type = RepositoryType.IMPERATIVE) @ConditionalOnMissingBean(CassandraRepositoryFactoryBean.class) @Import(CassandraRepositoriesAutoConfigureRegistrar.class) public class CassandraRepositoriesAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfiguration.java index 8b716de4f4..e907128852 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfiguration.java @@ -24,7 +24,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository; @@ -40,7 +41,7 @@ import org.springframework.data.couchbase.repository.support.ReactiveCouchbaseRe */ @Configuration @ConditionalOnClass({ Bucket.class, ReactiveCouchbaseRepository.class, Flux.class }) -@ConditionalOnProperty(prefix = "spring.data.couchbase.reactiverepositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "couchbase", type = RepositoryType.REACTIVE) @ConditionalOnBean(ReactiveRepositoryOperationsMapping.class) @ConditionalOnMissingBean(ReactiveCouchbaseRepositoryFactoryBean.class) @Import(CouchbaseReactiveRepositoriesRegistrar.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfiguration.java index a5d68cba26..451538a2be 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfiguration.java @@ -22,7 +22,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.couchbase.repository.CouchbaseRepository; @@ -40,7 +41,7 @@ import org.springframework.data.couchbase.repository.support.CouchbaseRepository @Configuration @ConditionalOnClass({ Bucket.class, CouchbaseRepository.class }) @ConditionalOnBean(RepositoryOperationsMapping.class) -@ConditionalOnProperty(prefix = "spring.data.couchbase.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "couchbase", type = RepositoryType.IMPERATIVE) @ConditionalOnMissingBean(CouchbaseRepositoryFactoryBean.class) @Import(CouchbaseRepositoriesRegistrar.class) public class CouchbaseRepositoriesAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfiguration.java index 51206fa7a6..b96f471488 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfiguration.java @@ -22,7 +22,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; @@ -51,7 +52,7 @@ import org.springframework.data.mongodb.repository.support.ReactiveMongoReposito @ConditionalOnClass({ MongoClient.class, ReactiveMongoRepository.class }) @ConditionalOnMissingBean({ ReactiveMongoRepositoryFactoryBean.class, ReactiveMongoRepositoryConfigurationExtension.class }) -@ConditionalOnProperty(prefix = "spring.data.mongodb.reactiverepositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "mongodb", type = RepositoryType.REACTIVE) @Import(MongoReactiveRepositoriesAutoConfigureRegistrar.class) @AutoConfigureAfter(MongoReactiveDataAutoConfiguration.class) public class MongoReactiveRepositoriesAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java index acb9943d4d..3028dd03de 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java @@ -22,7 +22,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; +import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.mongodb.repository.MongoRepository; @@ -55,7 +56,7 @@ import org.springframework.data.mongodb.repository.support.MongoRepositoryFactor @ConditionalOnClass({ Mongo.class, MongoRepository.class }) @ConditionalOnMissingBean({ MongoRepositoryFactoryBean.class, MongoRepositoryConfigurationExtension.class }) -@ConditionalOnProperty(prefix = "spring.data.mongodb.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnRepositoryType(store = "mongodb", type = RepositoryType.IMPERATIVE) @Import(MongoRepositoriesAutoConfigureRegistrar.class) @AutoConfigureAfter(MongoDataAutoConfiguration.class) public class MongoRepositoriesAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 1a47f32070..240783324a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -95,32 +95,40 @@ "defaultValue": "none" }, { - "name": "spring.data.cassandra.reactiverepositories.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable Cassandra reactive repositories.", - "defaultValue": true + "name": "spring.data.cassandra.repositories.type", + "type": "org.springframework.boot.autoconfigure.data.RepositoryType", + "description": "Type of Cassandra repositories to enable.", + "defaultValue": "auto" }, { "name": "spring.data.cassandra.repositories.enabled", "type": "java.lang.Boolean", "description": "Whether to enable Cassandra repositories.", - "defaultValue": true + "defaultValue": true, + "deprecation": { + "replacement": "spring.data.cassandra.repositories.type", + "level": "error" + } }, { "name": "spring.data.couchbase.consistency", "defaultValue": "read-your-own-writes" }, { - "name": "spring.data.couchbase.reactiverepositories.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable Couchbase reactive repositories.", - "defaultValue": true + "name": "spring.data.couchbase.repositories.type", + "type": "org.springframework.boot.autoconfigure.data.RepositoryType", + "description": "Type of Couchbase repositories to enable.", + "defaultValue": "auto" }, { "name": "spring.data.couchbase.repositories.enabled", "type": "java.lang.Boolean", "description": "Whether to enable Couchbase repositories.", - "defaultValue": true + "defaultValue": true, + "deprecation": { + "replacement": "spring.data.couchbase.repositories.type", + "level": "error" + } }, { "name": "spring.data.elasticsearch.repositories.enabled", @@ -141,16 +149,20 @@ "defaultValue": true }, { - "name": "spring.data.mongodb.reactiverepositories.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable Mongo reactive repositories.", - "defaultValue": true + "name": "spring.data.mongodb.repositories.type", + "type": "org.springframework.boot.autoconfigure.data.RepositoryType", + "description": "Type of Mongo repositories to enable.", + "defaultValue": "auto" }, { "name": "spring.data.mongodb.repositories.enabled", "type": "java.lang.Boolean", "description": "Whether to enable Mongo repositories.", - "defaultValue": true + "defaultValue": true, + "deprecation": { + "replacement": "spring.data.mongodb.repositories.type", + "level": "error" + } }, { "name": "spring.data.neo4j.open-in-view", diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryTypeTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryTypeTests.java new file mode 100644 index 0000000000..ac9ffaa3b9 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ConditionalOnRepositoryTypeTests.java @@ -0,0 +1,131 @@ +/* + * Copyright 2012-2017 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.data; + +import org.junit.Test; + +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ConditionalOnRepositoryType}. + * + * @author Andy Wilkinson + */ +public class ConditionalOnRepositoryTypeTests { + + private final ApplicationContextRunner runner = new ApplicationContextRunner(); + + @Test + public void imperativeRepositoryMatchesWithNoConfiguredType() { + this.runner.withUserConfiguration(ImperativeRepository.class) + .run((context) -> assertThat(context) + .hasSingleBean(ImperativeRepository.class)); + } + + @Test + public void reactiveRepositoryMatchesWithNoConfiguredType() { + this.runner.withUserConfiguration(ReactiveRepository.class).run( + (context) -> assertThat(context).hasSingleBean(ReactiveRepository.class)); + } + + @Test + public void imperativeRepositoryMatchesWithAutoConfiguredType() { + this.runner.withUserConfiguration(ImperativeRepository.class) + .withPropertyValues("spring.data.test.repositories.type:auto") + .run((context) -> assertThat(context) + .hasSingleBean(ImperativeRepository.class)); + } + + @Test + public void reactiveRepositoryMatchesWithAutoConfiguredType() { + this.runner.withUserConfiguration(ReactiveRepository.class) + .withPropertyValues("spring.data.test.repositories.type:auto") + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveRepository.class)); + } + + @Test + public void imperativeRepositoryMatchesWithImperativeConfiguredType() { + this.runner.withUserConfiguration(ImperativeRepository.class) + .withPropertyValues("spring.data.test.repositories.type:imperative") + .run((context) -> assertThat(context) + .hasSingleBean(ImperativeRepository.class)); + } + + @Test + public void reactiveRepositoryMatchesWithReactiveConfiguredType() { + this.runner.withUserConfiguration(ReactiveRepository.class) + .withPropertyValues("spring.data.test.repositories.type:reactive") + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveRepository.class)); + } + + @Test + public void imperativeRepositoryDoesNotMatchWithReactiveConfiguredType() { + this.runner.withUserConfiguration(ImperativeRepository.class) + .withPropertyValues("spring.data.test.repositories.type:reactive") + .run((context) -> assertThat(context) + .doesNotHaveBean(ImperativeRepository.class)); + } + + @Test + public void reactiveRepositoryDoesNotMatchWithImperativeConfiguredType() { + this.runner.withUserConfiguration(ReactiveRepository.class) + .withPropertyValues("spring.data.test.repositories.type:imperative") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveRepository.class)); + } + + @Test + public void imperativeRepositoryDoesNotMatchWithNoneConfiguredType() { + this.runner.withUserConfiguration(ImperativeRepository.class) + .withPropertyValues("spring.data.test.repositories.type:none") + .run((context) -> assertThat(context) + .doesNotHaveBean(ImperativeRepository.class)); + } + + @Test + public void reactiveRepositoryDoesNotMatchWithNoneConfiguredType() { + this.runner.withUserConfiguration(ReactiveRepository.class) + .withPropertyValues("spring.data.test.repositories.type:none") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveRepository.class)); + } + + @Test + public void failsFastWhenConfiguredTypeIsUnknown() { + this.runner.withUserConfiguration(ReactiveRepository.class) + .withPropertyValues("spring.data.test.repositories.type:abcde") + .run((context) -> assertThat(context).hasFailed()); + } + + @Configuration + @ConditionalOnRepositoryType(store = "test", type = RepositoryType.IMPERATIVE) + protected static class ImperativeRepository { + + } + + @Configuration + @ConditionalOnRepositoryType(store = "test", type = RepositoryType.REACTIVE) + protected static class ReactiveRepository { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfigurationTests.java index c8d0100b38..6e87cb2514 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveRepositoriesAutoConfigurationTests.java @@ -20,9 +20,9 @@ import java.util.Set; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; -import org.junit.After; import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -30,7 +30,8 @@ import org.springframework.boot.autoconfigure.data.alt.cassandra.ReactiveCityCas import org.springframework.boot.autoconfigure.data.cassandra.city.City; import org.springframework.boot.autoconfigure.data.cassandra.city.ReactiveCityRepository; import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; @@ -50,62 +51,72 @@ import static org.mockito.Mockito.mock; * @author EddĂș MelĂ©ndez * @author Stephane Nicoll * @author Mark Paluch + * @author Andy Wilkinson */ public class CassandraReactiveRepositoriesAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } + private final ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(CassandraAutoConfiguration.class, + CassandraRepositoriesAutoConfiguration.class, + CassandraDataAutoConfiguration.class, + CassandraReactiveDataAutoConfiguration.class, + CassandraReactiveRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class)); @Test public void testDefaultRepositoryConfiguration() { - load(TestConfiguration.class); - assertThat(this.context.getBean(ReactiveCityRepository.class)).isNotNull(); - assertThat(this.context.getBean(Cluster.class)).isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1); + this.runner.withUserConfiguration(TestConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(ReactiveCityRepository.class); + assertThat(context).hasSingleBean(Cluster.class); + assertThat(getInitialEntitySet(context)).hasSize(1); + }); } @Test public void testNoRepositoryConfiguration() { - load(TestExcludeConfiguration.class, EmptyConfiguration.class); - assertThat(this.context.getBean(Cluster.class)).isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); + this.runner.withUserConfiguration(TestExcludeConfiguration.class, + EmptyConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Cluster.class); + assertThat(getInitialEntitySet(context)).hasSize(1) + .containsOnly(City.class); + }); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { - load(TestExcludeConfiguration.class, CustomizedConfiguration.class); - assertThat(this.context.getBean(ReactiveCityCassandraRepository.class)) - .isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); + this.runner.withUserConfiguration(TestExcludeConfiguration.class, + CustomizedConfiguration.class).run((context) -> { + assertThat(context) + .hasSingleBean(ReactiveCityCassandraRepository.class); + assertThat(getInitialEntitySet(context)).hasSize(1) + .containsOnly(City.class); + }); + } + + @Test + public void enablingImperativeRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.cassandra.repositories.type=imperative") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityRepository.class)); + } + + @Test + public void enablingNoRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.cassandra.repositories.type=none") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityRepository.class)); } @SuppressWarnings("unchecked") - private Set> getInitialEntitySet() { - CassandraMappingContext mappingContext = this.context + private Set> getInitialEntitySet(ApplicationContext context) { + CassandraMappingContext mappingContext = context .getBean(CassandraMappingContext.class); return (Set>) ReflectionTestUtils.getField(mappingContext, "initialEntitySet"); } - private void load(Class... configurations) { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(configurations); - ctx.register(CassandraAutoConfiguration.class, - CassandraRepositoriesAutoConfiguration.class, - CassandraDataAutoConfiguration.class, - CassandraReactiveDataAutoConfiguration.class, - CassandraReactiveRepositoriesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); - ctx.refresh(); - this.context = ctx; - } - @Configuration @TestAutoConfigurationPackage(City.class) static class TestConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java index 2a844267ad..b666656f43 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java @@ -20,10 +20,9 @@ import java.util.Set; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; -import org.junit.After; -import org.junit.Before; import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -31,7 +30,8 @@ import org.springframework.boot.autoconfigure.data.alt.cassandra.CityCassandraRe import org.springframework.boot.autoconfigure.data.cassandra.city.City; import org.springframework.boot.autoconfigure.data.cassandra.city.CityRepository; import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -52,57 +52,65 @@ import static org.mockito.Mockito.mock; */ public class CassandraRepositoriesAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @Before - public void setUp() { - this.context = new AnnotationConfigApplicationContext(); - } - - @After - public void close() { - this.context.close(); - } + private final ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(CassandraAutoConfiguration.class, + CassandraRepositoriesAutoConfiguration.class, + CassandraDataAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class)); @Test public void testDefaultRepositoryConfiguration() { - addConfigurations(TestConfiguration.class); - assertThat(this.context.getBean(CityRepository.class)).isNotNull(); - assertThat(this.context.getBean(Cluster.class)).isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1); + this.runner.withUserConfiguration(TestConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(CityRepository.class); + assertThat(context).hasSingleBean(Cluster.class); + assertThat(getInitialEntitySet(context)).hasSize(1); + }); } @Test public void testNoRepositoryConfiguration() { - addConfigurations(TestExcludeConfiguration.class, EmptyConfiguration.class); - assertThat(this.context.getBean(Cluster.class)).isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); + this.runner.withUserConfiguration(TestExcludeConfiguration.class, + EmptyConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Cluster.class); + assertThat(getInitialEntitySet(context)).hasSize(1) + .containsOnly(City.class); + }); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { - addConfigurations(TestExcludeConfiguration.class, CustomizedConfiguration.class); - assertThat(this.context.getBean(CityCassandraRepository.class)).isNotNull(); - assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); + this.runner.withUserConfiguration(TestExcludeConfiguration.class, + CustomizedConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(CityCassandraRepository.class); + assertThat(getInitialEntitySet(context)).hasSize(1) + .containsOnly(City.class); + }); + } + + @Test + public void enablingReactiveRepositoriesDisablesImperativeRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.cassandra.repositories.type=reactive") + .run((context) -> assertThat(context) + .doesNotHaveBean(CityCassandraRepository.class)); + } + + @Test + public void enablingNoRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.cassandra.repositories.type=none") + .run((context) -> assertThat(context) + .doesNotHaveBean(CityCassandraRepository.class)); } @SuppressWarnings("unchecked") - private Set> getInitialEntitySet() { - CassandraMappingContext mappingContext = this.context + private Set> getInitialEntitySet(AssertableApplicationContext context) { + CassandraMappingContext mappingContext = context .getBean(CassandraMappingContext.class); return (Set>) ReflectionTestUtils.getField(mappingContext, "initialEntitySet"); } - private void addConfigurations(Class... configurations) { - this.context.register(configurations); - this.context.register(CassandraAutoConfiguration.class, - CassandraRepositoriesAutoConfiguration.class, - CassandraDataAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); - this.context.refresh(); - } - @Configuration @TestAutoConfigurationPackage(City.class) static class TestConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java similarity index 92% rename from spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests.java rename to spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java index 5ae970260f..b1d8867555 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java @@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ -public class CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests { +public class CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests { private AnnotationConfigApplicationContext context; @@ -55,12 +55,12 @@ public class CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests { } @Test - public void shouldCreateInstancesForReactiveAndBlockingRepositories() + public void shouldCreateInstancesForReactiveAndImperativeRepositories() throws Exception { this.context = new AnnotationConfigApplicationContext(); TestPropertyValues.of("spring.datasource.initialization-mode:never") .applyTo(this.context); - this.context.register(BlockingAndReactiveConfiguration.class, + this.context.register(ImperativeAndReactiveConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); @@ -71,7 +71,7 @@ public class CouchbaseReactiveAndBlockingRepositoriesAutoConfigurationTests { @TestAutoConfigurationPackage(CouchbaseAutoConfigurationTests.class) @EnableCouchbaseRepositories(basePackageClasses = CityRepository.class) @EnableReactiveCouchbaseRepositories(basePackageClasses = ReactiveCityRepository.class) - protected static class BlockingAndReactiveConfiguration { + protected static class ImperativeAndReactiveConfiguration { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfigurationTests.java index fe0a4e89ba..25614d51e4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveRepositoriesAutoConfigurationTests.java @@ -65,10 +65,15 @@ public class CouchbaseReactiveRepositoriesAutoConfigurationTests { } @Test - public void disableReactiveRepository() { + public void imperativeRepositories() { load(DefaultConfiguration.class, - "spring.data.couchbase.reactiverepositories.enabled=false", - "spring.data.couchbase.repositories.enabled=false"); + "spring.data.couchbase.repositories.type=imperative"); + assertThat(this.context.getBeansOfType(ReactiveCityRepository.class)).hasSize(0); + } + + @Test + public void disabledRepositories() { + load(DefaultConfiguration.class, "spring.data.couchbase.repositories.type=none"); assertThat(this.context.getBeansOfType(ReactiveCityRepository.class)).hasSize(0); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfigurationTests.java index 0d419baa4a..7745935d72 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseRepositoriesAutoConfigurationTests.java @@ -63,9 +63,15 @@ public class CouchbaseRepositoriesAutoConfigurationTests { } @Test - public void disableRepository() { + public void reactiveRepositories() { load(DefaultConfiguration.class, - "spring.data.couchbase.repositories.enabled=false"); + "spring.data.couchbase.repositories.type=reactive"); + assertThat(this.context.getBeansOfType(CityRepository.class)).hasSize(0); + } + + @Test + public void disabledRepositories() { + load(DefaultConfiguration.class, "spring.data.couchbase.repositories.type=none"); assertThat(this.context.getBeansOfType(CityRepository.class)).hasSize(0); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java index 3286fdb088..16105f7068 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java @@ -106,7 +106,7 @@ public class MixedMongoRepositoriesAutoConfigurationTests { this.context = new AnnotationConfigApplicationContext(); TestPropertyValues .of("spring.datasource.initialization-mode:never", - "spring.data.mongodb.repositories.enabled:false") + "spring.data.mongodb.repositories.type:none") .applyTo(this.context); this.context.register(OverlapConfiguration.class, BaseConfiguration.class); this.context.refresh(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfigurationTests.java index 3a9037ec20..9020202b6a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveRepositoriesAutoConfigurationTests.java @@ -19,10 +19,9 @@ package org.springframework.boot.autoconfigure.data.mongo; import java.util.Set; import com.mongodb.reactivestreams.client.MongoClient; -import org.junit.After; import org.junit.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; @@ -32,7 +31,7 @@ import org.springframework.boot.autoconfigure.data.mongo.city.City; import org.springframework.boot.autoconfigure.data.mongo.city.ReactiveCityRepository; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @@ -45,61 +44,67 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link MongoReactiveRepositoriesAutoConfiguration}. * * @author Mark Paluch + * @author Andy Wilkinson */ public class MongoReactiveRepositoriesAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } + private final ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, + MongoReactiveAutoConfiguration.class, + MongoReactiveDataAutoConfiguration.class, + MongoReactiveRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class)); @Test public void testDefaultRepositoryConfiguration() throws Exception { - prepareApplicationContext(TestConfiguration.class); - assertThat(this.context.getBean(ReactiveCityRepository.class)).isNotNull(); - MongoClient client = this.context.getBean(MongoClient.class); - assertThat(client).isInstanceOf(MongoClient.class); - MongoMappingContext mappingContext = this.context - .getBean(MongoMappingContext.class); - @SuppressWarnings("unchecked") - Set> entities = (Set>) ReflectionTestUtils - .getField(mappingContext, "initialEntitySet"); - assertThat(entities).hasSize(1); + this.runner.withUserConfiguration(TestConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(ReactiveCityRepository.class); + MongoClient client = context.getBean(MongoClient.class); + assertThat(client).isInstanceOf(MongoClient.class); + MongoMappingContext mappingContext = context + .getBean(MongoMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1); + }); } @Test public void testNoRepositoryConfiguration() throws Exception { - prepareApplicationContext(EmptyConfiguration.class); - MongoClient client = this.context.getBean(MongoClient.class); - assertThat(client).isInstanceOf(MongoClient.class); + this.runner.withUserConfiguration(EmptyConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(MongoClient.class)); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { - prepareApplicationContext(CustomizedConfiguration.class); - assertThat(this.context.getBeansOfType(ReactiveCityMongoDbRepository.class)) - .isEmpty(); + this.runner.withUserConfiguration(CustomizedConfiguration.class) + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityMongoDbRepository.class)); } - @Test(expected = NoSuchBeanDefinitionException.class) + @Test public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { - prepareApplicationContext(SortOfInvalidCustomConfiguration.class); - this.context.getBean(ReactiveCityRepository.class); + this.runner.withUserConfiguration(SortOfInvalidCustomConfiguration.class) + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityRepository.class)); } - private void prepareApplicationContext(Class... configurationClasses) { - this.context = new AnnotationConfigApplicationContext(); - this.context.register(configurationClasses); - this.context.register(MongoAutoConfiguration.class, - MongoDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class, - MongoReactiveDataAutoConfiguration.class, - MongoReactiveRepositoriesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); - this.context.refresh(); + @Test + public void enablingImperativeRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.mongodb.repositories.type=imperative") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityRepository.class)); + } + + @Test + public void enablingNoRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.mongodb.repositories.type=none") + .run((context) -> assertThat(context) + .doesNotHaveBean(ReactiveCityRepository.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfigurationTests.java index fd169b7d79..0449c69d4e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfigurationTests.java @@ -20,10 +20,9 @@ import java.util.Set; import com.mongodb.Mongo; import com.mongodb.MongoClient; -import org.junit.After; import org.junit.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; @@ -31,7 +30,7 @@ import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; import org.springframework.boot.autoconfigure.data.mongo.city.City; import org.springframework.boot.autoconfigure.data.mongo.city.CityRepository; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @@ -47,58 +46,62 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class MongoRepositoriesAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @After - public void close() { - this.context.close(); - } + private final ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, + MongoRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class)); @Test public void testDefaultRepositoryConfiguration() throws Exception { - prepareApplicationContext(TestConfiguration.class); - - assertThat(this.context.getBean(CityRepository.class)).isNotNull(); - Mongo mongo = this.context.getBean(Mongo.class); - assertThat(mongo).isInstanceOf(MongoClient.class); - MongoMappingContext mappingContext = this.context - .getBean(MongoMappingContext.class); - @SuppressWarnings("unchecked") - Set> entities = (Set>) ReflectionTestUtils - .getField(mappingContext, "initialEntitySet"); - assertThat(entities).hasSize(1); + this.runner.withUserConfiguration(TestConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(CityRepository.class); + Mongo mongo = context.getBean(Mongo.class); + assertThat(mongo).isInstanceOf(MongoClient.class); + MongoMappingContext mappingContext = context + .getBean(MongoMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1); + }); } @Test public void testNoRepositoryConfiguration() throws Exception { - prepareApplicationContext(EmptyConfiguration.class); - - Mongo mongo = this.context.getBean(Mongo.class); - assertThat(mongo).isInstanceOf(MongoClient.class); + this.runner.withUserConfiguration(EmptyConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Mongo.class); + assertThat(context.getBean(Mongo.class)).isInstanceOf(MongoClient.class); + }); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { - prepareApplicationContext(CustomizedConfiguration.class); - - assertThat(this.context.getBean(CityMongoDbRepository.class)).isNotNull(); + this.runner.withUserConfiguration(CustomizedConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(CityMongoDbRepository.class)); } - @Test(expected = NoSuchBeanDefinitionException.class) + @Test public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { - prepareApplicationContext(SortOfInvalidCustomConfiguration.class); - - this.context.getBean(CityRepository.class); + this.runner.withUserConfiguration(SortOfInvalidCustomConfiguration.class).run( + (context) -> assertThat(context).doesNotHaveBean(CityRepository.class)); } - private void prepareApplicationContext(Class... configurationClasses) { - this.context = new AnnotationConfigApplicationContext(); - this.context.register(configurationClasses); - this.context.register(MongoAutoConfiguration.class, - MongoDataAutoConfiguration.class, - MongoRepositoriesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); - this.context.refresh(); + @Test + public void enablingReactiveRepositoriesDisablesImperativeRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.mongodb.repositories.type=reactive") + .run((context) -> assertThat(context) + .doesNotHaveBean(CityRepository.class)); + } + + @Test + public void enablingNoRepositoriesDisablesReactiveRepositories() { + this.runner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.mongodb.repositories.type=none") + .run((context) -> assertThat(context) + .doesNotHaveBean(CityRepository.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 89a5610037..c1dbdb7e1d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -576,10 +576,9 @@ content into your application. Rather, pick only the properties that you need. spring.data.cassandra.pool.idle-timeout=120 # Idle timeout before an idle connection is removed. If a duration suffix is not specified, seconds will be used. spring.data.cassandra.pool.max-queue-size=256 # Maximum number of requests that get queued if no connection is available. spring.data.cassandra.pool.pool-timeout=5000ms # Pool timeout when trying to acquire a connection from a host's pool. - spring.data.cassandra.reactiverepositories.enabled=true # Whether to enable Cassandra reactive repositories. spring.data.cassandra.read-timeout= # Socket option: read time out. spring.data.cassandra.reconnection-policy= # Reconnection policy class. - spring.data.cassandra.repositories.enabled= # Enable Cassandra repositories. + spring.data.cassandra.repositories.type=auto # Type of Cassandra repositories to enable. spring.data.cassandra.retry-policy= # Class name of the retry policy. spring.data.cassandra.serial-consistency-level= # Queries serial consistency level. spring.data.cassandra.schema-action=none # Schema action to take at startup. @@ -589,8 +588,7 @@ content into your application. Rather, pick only the properties that you need. # DATA COUCHBASE ({sc-spring-boot-autoconfigure}/data/couchbase/CouchbaseDataProperties.{sc-ext}[CouchbaseDataProperties]) spring.data.couchbase.auto-index=false # Automatically create views and indexes. spring.data.couchbase.consistency=read-your-own-writes # Consistency to apply by default on generated queries. - spring.data.couchbase.reactiverepositories.enabled=true # Enable Couchbase reactive repositories. - spring.data.couchbase.repositories.enabled=true # Enable Couchbase repositories. + spring.data.couchbase.repositories.type=auto # Type of Couchbase repositories to enable. # ELASTICSEARCH ({sc-spring-boot-autoconfigure}/data/elasticsearch/ElasticsearchProperties.{sc-ext}[ElasticsearchProperties]) spring.data.elasticsearch.cluster-name=elasticsearch # Elasticsearch cluster name. @@ -609,8 +607,7 @@ content into your application. Rather, pick only the properties that you need. spring.data.mongodb.host=localhost # Mongo server host. Cannot be set with URI. spring.data.mongodb.password= # Login password of the mongo server. Cannot be set with URI. spring.data.mongodb.port=27017 # Mongo server port. Cannot be set with URI. - spring.data.mongodb.reactiverepositories.enabled=true # Whether to enable Mongo reactive repositories. - spring.data.mongodb.repositories.enabled=true # Whether to enable Mongo repositories. + spring.data.mongodb.repositories.type=true # Type of Mongo repositories to enable. spring.data.mongodb.uri=mongodb://localhost/test # Mongo database URI. Cannot be set with host, port and credentials. spring.data.mongodb.username= # Login user of the mongo server. Cannot be set with URI. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index fe8b9c092d..26039db1d7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -1902,10 +1902,11 @@ repositories for you. The most explicit way to do that is to use the standard Sp `+@EnableJpaRepositories+` and `+@EnableMongoRepositories+` annotations and provide the location of your `Repository` interfaces. -There are also flags (`+spring.data.*.repositories.enabled+`) that you can use to switch the -auto-configured repositories on and off in external configuration. Doing so is useful, for -instance, in case you want to switch off the Mongo repositories and still use the -auto-configured `MongoTemplate`. +There are also flags (`+spring.data.*.repositories.enabled+` and +`+spring.data.*.repositories.type+`) that you can use to switch the auto-configured +repositories on and off in external configuration. Doing so is useful, for instance, in +case you want to switch off the Mongo repositories and still use the auto-configured +`MongoTemplate`. The same obstacle and the same features exist for other auto-configured Spring Data repository types (Elasticsearch, Solr, and others). To work with them, change the names of the annotations and flags