diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java index 12f26f2caf..9b1f2acfe5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java @@ -49,12 +49,27 @@ public class MongoAutoConfiguration { @Bean @ConditionalOnMissingBean(MongoClient.class) - public MongoClient mongo(MongoProperties properties, Environment environment, - ObjectProvider builderCustomizers, - ObjectProvider settings) { - return new MongoClientFactory(properties, environment, - builderCustomizers.orderedStream().collect(Collectors.toList())) - .createMongoClient(settings.getIfAvailable()); + public MongoClient mongo(ObjectProvider builderCustomizers, + MongoClientSettings settings) { + return new MongoClientFactory(builderCustomizers.orderedStream().collect(Collectors.toList())) + .createMongoClient(settings); + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingBean(MongoClientSettings.class) + static class MongoClientSettingsConfiguration { + + @Bean + MongoClientSettings mongoClientSettings() { + return MongoClientSettings.builder().build(); + } + + @Bean + MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(MongoProperties properties, + Environment environment) { + return new MongoPropertiesClientSettingsBuilderCustomizer(properties, environment); + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java index 60c775d0cf..97f2794ad2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java @@ -43,12 +43,11 @@ public class MongoClientFactory extends MongoClientFactorySupport { * Construct a factory for creating a blocking {@link MongoClient}. * @param properties configuration properties * @param environment a Spring {@link Environment} containing configuration properties - * @deprecated since 2.3.0 in favor of - * {@link #MongoClientFactory(MongoProperties, Environment, List)} + * @deprecated since 2.3.0 in favor of {@link #MongoClientFactory(List)} */ @Deprecated public MongoClientFactory(MongoProperties properties, Environment environment) { - this(properties, environment, null); + this(null); } /** @@ -56,10 +55,20 @@ public class MongoClientFactory extends MongoClientFactorySupport { * @param properties configuration properties * @param environment a Spring {@link Environment} containing configuration properties * @param builderCustomizers a list of configuration settings customizers + * @deprecated since 2.4.0 in favor of {@link #MongoClientFactory(List)} */ + @Deprecated public MongoClientFactory(MongoProperties properties, Environment environment, List builderCustomizers) { - super(properties, environment, builderCustomizers, MongoClients::create); + this(builderCustomizers); + } + + /** + * Construct a factory for creating a blocking {@link MongoClient}. + * @param builderCustomizers a list of configuration settings customizers + */ + public MongoClientFactory(List builderCustomizers) { + super(builderCustomizers, MongoClients::create); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupport.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupport.java index 30330b83fb..294740b536 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupport.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupport.java @@ -20,15 +20,11 @@ import java.util.Collections; import java.util.List; import java.util.function.BiFunction; -import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; import com.mongodb.MongoClientSettings.Builder; -import com.mongodb.MongoCredential; import com.mongodb.MongoDriverInformation; -import com.mongodb.ServerAddress; import org.springframework.core.env.Environment; -import org.springframework.util.Assert; /** * Base class for setup that is common to MongoDB client factories. @@ -40,120 +36,35 @@ import org.springframework.util.Assert; */ public abstract class MongoClientFactorySupport { - private final MongoProperties properties; - - private final Environment environment; - private final List builderCustomizers; private final BiFunction clientCreator; + @Deprecated protected MongoClientFactorySupport(MongoProperties properties, Environment environment, List builderCustomizers, BiFunction clientCreator) { - this.properties = properties; - this.environment = environment; + this(builderCustomizers, clientCreator); + } + + protected MongoClientFactorySupport(List builderCustomizers, + BiFunction clientCreator) { this.builderCustomizers = (builderCustomizers != null) ? builderCustomizers : Collections.emptyList(); this.clientCreator = clientCreator; } public T createMongoClient(MongoClientSettings settings) { - MongoClientSettings targetSettings = computeClientSettings(settings); - return this.clientCreator.apply(targetSettings, driverInformation()); + Builder targetSettings = MongoClientSettings.builder(settings); + customize(targetSettings); + return this.clientCreator.apply(targetSettings.build(), driverInformation()); } - private MongoClientSettings computeClientSettings(MongoClientSettings settings) { - Builder settingsBuilder = (settings != null) ? MongoClientSettings.builder(settings) - : MongoClientSettings.builder(); - validateConfiguration(); - applyUuidRepresentation(settingsBuilder); - applyHostAndPort(settingsBuilder); - applyCredentials(settingsBuilder); - applyReplicaSet(settingsBuilder); - customize(settingsBuilder); - return settingsBuilder.build(); - } - - private void validateConfiguration() { - if (hasCustomAddress() || hasCustomCredentials() || hasReplicaSet()) { - Assert.state(this.properties.getUri() == null, - "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); - } - } - - private void applyUuidRepresentation(Builder settingsBuilder) { - settingsBuilder.uuidRepresentation(this.properties.getUuidRepresentation()); - } - - private void applyHostAndPort(MongoClientSettings.Builder settings) { - if (isEmbedded()) { - settings.applyConnectionString(new ConnectionString("mongodb://localhost:" + getEmbeddedPort())); - return; - } - - if (hasCustomAddress()) { - String host = getOrDefault(this.properties.getHost(), "localhost"); - int port = getOrDefault(this.properties.getPort(), MongoProperties.DEFAULT_PORT); - ServerAddress serverAddress = new ServerAddress(host, port); - settings.applyToClusterSettings((cluster) -> cluster.hosts(Collections.singletonList(serverAddress))); - return; - } - - settings.applyConnectionString(new ConnectionString(this.properties.determineUri())); - } - - private void applyCredentials(Builder builder) { - if (hasCustomCredentials()) { - String database = (this.properties.getAuthenticationDatabase() != null) - ? this.properties.getAuthenticationDatabase() : this.properties.getMongoClientDatabase(); - builder.credential((MongoCredential.createCredential(this.properties.getUsername(), database, - this.properties.getPassword()))); - } - } - - private void applyReplicaSet(Builder builder) { - if (hasReplicaSet()) { - builder.applyToClusterSettings( - (cluster) -> cluster.requiredReplicaSetName(this.properties.getReplicaSetName())); - } - } - - private void customize(MongoClientSettings.Builder builder) { + private void customize(Builder builder) { for (MongoClientSettingsBuilderCustomizer customizer : this.builderCustomizers) { customizer.customize(builder); } } - private V getOrDefault(V value, V defaultValue) { - return (value != null) ? value : defaultValue; - } - - private Integer getEmbeddedPort() { - if (this.environment != null) { - String localPort = this.environment.getProperty("local.mongo.port"); - if (localPort != null) { - return Integer.valueOf(localPort); - } - } - return null; - } - - private boolean isEmbedded() { - return getEmbeddedPort() != null; - } - - private boolean hasCustomCredentials() { - return this.properties.getUsername() != null && this.properties.getPassword() != null; - } - - private boolean hasReplicaSet() { - return this.properties.getReplicaSetName() != null; - } - - private boolean hasCustomAddress() { - return this.properties.getHost() != null || this.properties.getPort() != null; - } - private MongoDriverInformation driverInformation() { return MongoDriverInformation.builder(MongoDriverInformation.builder().build()).driverName("spring-boot") .build(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java new file mode 100644 index 0000000000..2326e00af3 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.mongo; + +import java.util.Collections; + +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; + +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * A {@link MongoClientSettingsBuilderCustomizer} that applies properties from a + * {@link MongoProperties} to a {@link MongoClientSettings}. + * + * @author Scott Frederick + * @since 2.4.0 + */ +public class MongoPropertiesClientSettingsBuilderCustomizer implements MongoClientSettingsBuilderCustomizer, Ordered { + + private final MongoProperties properties; + + private final Environment environment; + + private int order = 0; + + public MongoPropertiesClientSettingsBuilderCustomizer(MongoProperties properties, Environment environment) { + this.properties = properties; + this.environment = environment; + } + + @Override + public void customize(MongoClientSettings.Builder settingsBuilder) { + validateConfiguration(); + applyUuidRepresentation(settingsBuilder); + applyHostAndPort(settingsBuilder); + applyCredentials(settingsBuilder); + applyReplicaSet(settingsBuilder); + } + + private void validateConfiguration() { + if (hasCustomAddress() || hasCustomCredentials() || hasReplicaSet()) { + Assert.state(this.properties.getUri() == null, + "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); + } + } + + private void applyUuidRepresentation(MongoClientSettings.Builder settingsBuilder) { + settingsBuilder.uuidRepresentation(this.properties.getUuidRepresentation()); + } + + private void applyHostAndPort(MongoClientSettings.Builder settings) { + if (getEmbeddedPort() != null) { + settings.applyConnectionString(new ConnectionString("mongodb://localhost:" + getEmbeddedPort())); + return; + } + + if (hasCustomAddress()) { + String host = getOrDefault(this.properties.getHost(), "localhost"); + int port = getOrDefault(this.properties.getPort(), MongoProperties.DEFAULT_PORT); + ServerAddress serverAddress = new ServerAddress(host, port); + settings.applyToClusterSettings((cluster) -> cluster.hosts(Collections.singletonList(serverAddress))); + return; + } + + settings.applyConnectionString(new ConnectionString(this.properties.determineUri())); + } + + private void applyCredentials(MongoClientSettings.Builder builder) { + if (hasCustomCredentials()) { + String database = (this.properties.getAuthenticationDatabase() != null) + ? this.properties.getAuthenticationDatabase() : this.properties.getMongoClientDatabase(); + builder.credential((MongoCredential.createCredential(this.properties.getUsername(), database, + this.properties.getPassword()))); + } + } + + private void applyReplicaSet(MongoClientSettings.Builder builder) { + if (hasReplicaSet()) { + builder.applyToClusterSettings( + (cluster) -> cluster.requiredReplicaSetName(this.properties.getReplicaSetName())); + } + } + + private V getOrDefault(V value, V defaultValue) { + return (value != null) ? value : defaultValue; + } + + private Integer getEmbeddedPort() { + if (this.environment != null) { + String localPort = this.environment.getProperty("local.mongo.port"); + if (localPort != null) { + return Integer.valueOf(localPort); + } + } + return null; + } + + private boolean hasCustomCredentials() { + return this.properties.getUsername() != null && this.properties.getPassword() != null; + } + + private boolean hasCustomAddress() { + return this.properties.getHost() != null || this.properties.getPort() != null; + } + + private boolean hasReplicaSet() { + return this.properties.getReplicaSetName() != null; + } + + @Override + public int getOrder() { + return this.order; + } + + /** + * Set the order value of this object. + * @param order the new order value + * @see #getOrder() + */ + public void setOrder(int order) { + this.order = order; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java index 0e87bd1a7c..c2b8fdaf85 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -44,6 +44,7 @@ import org.springframework.core.env.Environment; * * @author Mark Paluch * @author Stephane Nicoll + * @author Scott Frederick * @since 2.0.0 */ @Configuration(proxyBeanMethods = false) @@ -53,12 +54,28 @@ public class MongoReactiveAutoConfiguration { @Bean @ConditionalOnMissingBean - public MongoClient reactiveStreamsMongoClient(MongoProperties properties, Environment environment, - ObjectProvider builderCustomizers, - ObjectProvider settings) { - ReactiveMongoClientFactory factory = new ReactiveMongoClientFactory(properties, environment, + public MongoClient reactiveStreamsMongoClient( + ObjectProvider builderCustomizers, MongoClientSettings settings) { + ReactiveMongoClientFactory factory = new ReactiveMongoClientFactory( builderCustomizers.orderedStream().collect(Collectors.toList())); - return factory.createMongoClient(settings.getIfAvailable()); + return factory.createMongoClient(settings); + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingBean(MongoClientSettings.class) + static class MongoClientSettingsConfiguration { + + @Bean + MongoClientSettings mongoClientSettings() { + return MongoClientSettings.builder().build(); + } + + @Bean + MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(MongoProperties properties, + Environment environment) { + return new MongoPropertiesClientSettingsBuilderCustomizer(properties, environment); + } + } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java index 3c04855451..560579c75a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java @@ -33,9 +33,25 @@ import org.springframework.core.env.Environment; */ public class ReactiveMongoClientFactory extends MongoClientFactorySupport { + /** + * Construct a factory for creating a {@link MongoClient}. + * @param properties configuration properties + * @param environment a Spring {@link Environment} containing configuration properties + * @param builderCustomizers a list of configuration settings customizers + * @deprecated since 2.4.0 in favor of {@link #ReactiveMongoClientFactory(List)} + */ + @Deprecated public ReactiveMongoClientFactory(MongoProperties properties, Environment environment, List builderCustomizers) { super(properties, environment, builderCustomizers, MongoClients::create); } + /** + * Construct a factory for creating a {@link MongoClient}. + * @param builderCustomizers a list of configuration settings customizers + */ + public ReactiveMongoClientFactory(List builderCustomizers) { + super(builderCustomizers, MongoClients::create); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupportTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupportTests.java index dbce629aaf..6e697ea881 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupportTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactorySupportTests.java @@ -21,20 +21,14 @@ import java.util.List; import java.util.concurrent.TimeUnit; import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; -import org.bson.UuidRepresentation; import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -52,10 +46,6 @@ import static org.mockito.Mockito.verify; */ abstract class MongoClientFactorySupportTests { - private final MongoProperties properties = new MongoProperties(); - - private final MockEnvironment environment = new MockEnvironment(); - @Test void canBindCharArrayPassword() { // gh-1572 @@ -103,124 +93,6 @@ abstract class MongoClientFactorySupportTests { assertThat(wrapped.getSslSettings().isEnabled()).isEqualTo(settings.getSslSettings().isEnabled()); } - @Test - void portCanBeCustomized() { - this.properties.setPort(12345); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "localhost", 12345); - } - - @Test - void hostCanBeCustomized() { - this.properties.setHost("mongo.example.com"); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "mongo.example.com", 27017); - } - - @Test - void credentialsCanBeCustomized() { - this.properties.setUsername("user"); - this.properties.setPassword("secret".toCharArray()); - T client = createMongoClient(); - assertMongoCredential(getClientSettings(client).getCredential(), "user", "secret", "test"); - } - - @Test - void replicaSetCanBeCustomized() { - this.properties.setReplicaSetName("test"); - T client = createMongoClient(); - assertThat(getClientSettings(client).getClusterSettings().getRequiredReplicaSetName()).isEqualTo("test"); - } - - @Test - void databaseCanBeCustomized() { - this.properties.setDatabase("foo"); - this.properties.setUsername("user"); - this.properties.setPassword("secret".toCharArray()); - T client = createMongoClient(); - assertMongoCredential(getClientSettings(client).getCredential(), "user", "secret", "foo"); - } - - @Test - void uuidRepresentationDefaultToJavaLegacy() { - T client = createMongoClient(); - assertThat(getClientSettings(client).getUuidRepresentation()).isEqualTo(UuidRepresentation.JAVA_LEGACY); - } - - @Test - void uuidRepresentationCanBeCustomized() { - this.properties.setUuidRepresentation(UuidRepresentation.STANDARD); - T client = createMongoClient(); - assertThat(getClientSettings(client).getUuidRepresentation()).isEqualTo(UuidRepresentation.STANDARD); - } - - @Test - void authenticationDatabaseCanBeCustomized() { - this.properties.setAuthenticationDatabase("foo"); - this.properties.setUsername("user"); - this.properties.setPassword("secret".toCharArray()); - T client = createMongoClient(); - assertMongoCredential(getClientSettings(client).getCredential(), "user", "secret", "foo"); - } - - @Test - void uriCanBeCustomized() { - this.properties.setUri("mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test"); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(2); - assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); - assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); - assertMongoCredential(getClientSettings(client).getCredential(), "user", "secret", "test"); - } - - @Test - void uriIsIgnoredInEmbeddedMode() { - this.properties.setUri("mongodb://mongo.example.com:1234/mydb"); - this.environment.setProperty("local.mongo.port", "4000"); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "localhost", 4000); - } - - @Test - void retryWritesIsPropagatedFromUri() { - this.properties.setUri("mongodb://localhost/test?retryWrites=true"); - T client = createMongoClient(); - assertThat(getClientSettings(client).getRetryWrites()).isTrue(); - } - - @Test - void uriCannotBeSetWithCredentials() { - this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); - this.properties.setUsername("user"); - this.properties.setPassword("secret".toCharArray()); - assertThatIllegalStateException().isThrownBy(this::createMongoClient).withMessageContaining( - "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); - } - - @Test - void uriCannotBeSetWithReplicaSetName() { - this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); - this.properties.setReplicaSetName("test"); - assertThatIllegalStateException().isThrownBy(this::createMongoClient).withMessageContaining( - "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); - } - - @Test - void uriCannotBeSetWithHostPort() { - this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); - this.properties.setHost("localhost"); - this.properties.setPort(4567); - assertThatIllegalStateException().isThrownBy(this::createMongoClient).withMessageContaining( - "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); - } - @Test void customizerIsInvoked() { MongoClientSettingsBuilderCustomizer customizer = mock(MongoClientSettingsBuilderCustomizer.class); @@ -228,49 +100,6 @@ abstract class MongoClientFactorySupportTests { verify(customizer).customize(any(MongoClientSettings.Builder.class)); } - @Test - void customizerIsInvokedWhenHostIsSet() { - this.properties.setHost("localhost"); - MongoClientSettingsBuilderCustomizer customizer = mock(MongoClientSettingsBuilderCustomizer.class); - createMongoClient(customizer); - verify(customizer).customize(any(MongoClientSettings.Builder.class)); - } - - @Test - void customizerIsInvokedForEmbeddedMongo() { - this.environment.setProperty("local.mongo.port", "27017"); - MongoClientSettingsBuilderCustomizer customizer = mock(MongoClientSettingsBuilderCustomizer.class); - createMongoClient(customizer); - verify(customizer).customize(any(MongoClientSettings.Builder.class)); - } - - @Test - void onlyHostAndPortSetShouldUseThat() { - this.properties.setHost("localhost"); - this.properties.setPort(27017); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "localhost", 27017); - } - - @Test - void onlyUriSetShouldUseThat() { - this.properties.setUri("mongodb://mongo1.example.com:12345"); - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); - } - - @Test - void noCustomAddressAndNoUriUsesDefaultUri() { - T client = createMongoClient(); - List allAddresses = getAllAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "localhost", 27017); - } - @Test void canBindAutoIndexCreation() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -281,40 +110,24 @@ abstract class MongoClientFactorySupportTests { assertThat(properties.isAutoIndexCreation()).isTrue(); } - private List getAllAddresses(T client) { - return getClientSettings(client).getClusterSettings().getHosts(); - } - protected T createMongoClient() { - return createMongoClient(this.properties, this.environment, null, null); + return createMongoClient(null, MongoClientSettings.builder().build()); } protected T createMongoClient(MongoClientSettings settings) { - return createMongoClient(this.properties, this.environment, null, settings); + return createMongoClient(null, settings); } protected void createMongoClient(MongoClientSettingsBuilderCustomizer... customizers) { - createMongoClient(this.properties, this.environment, (customizers != null) ? Arrays.asList(customizers) : null, - null); + createMongoClient((customizers != null) ? Arrays.asList(customizers) : null, + MongoClientSettings.builder().build()); } - protected abstract T createMongoClient(MongoProperties properties, Environment environment, - List customizers, MongoClientSettings settings); + protected abstract T createMongoClient(List customizers, + MongoClientSettings settings); protected abstract MongoClientSettings getClientSettings(T client); - protected void assertServerAddress(ServerAddress serverAddress, String expectedHost, int expectedPort) { - assertThat(serverAddress.getHost()).isEqualTo(expectedHost); - assertThat(serverAddress.getPort()).isEqualTo(expectedPort); - } - - protected void assertMongoCredential(MongoCredential credentials, String expectedUsername, String expectedPassword, - String expectedSource) { - assertThat(credentials.getUserName()).isEqualTo(expectedUsername); - assertThat(credentials.getPassword()).isEqualTo(expectedPassword.toCharArray()); - assertThat(credentials.getSource()).isEqualTo(expectedSource); - } - @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(MongoProperties.class) static class Config { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java index baafeedce1..7fbddedbcd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java @@ -21,7 +21,6 @@ import java.util.List; import com.mongodb.MongoClientSettings; import com.mongodb.client.MongoClient; -import org.springframework.core.env.Environment; import org.springframework.test.util.ReflectionTestUtils; /** @@ -36,9 +35,9 @@ import org.springframework.test.util.ReflectionTestUtils; class MongoClientFactoryTests extends MongoClientFactorySupportTests { @Override - protected MongoClient createMongoClient(MongoProperties properties, Environment environment, - List customizers, MongoClientSettings settings) { - return new MongoClientFactory(properties, environment, customizers).createMongoClient(settings); + protected MongoClient createMongoClient(List customizers, + MongoClientSettings settings) { + return new MongoClientFactory(customizers).createMongoClient(settings); } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTest.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTest.java new file mode 100644 index 0000000000..63189e9ea0 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTest.java @@ -0,0 +1,210 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.mongo; + +import java.util.List; + +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import org.bson.UuidRepresentation; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link MongoPropertiesClientSettingsBuilderCustomizer}. + * + * @author Scott Frederick + */ +class MongoPropertiesClientSettingsBuilderCustomizerTest { + + private final MongoProperties properties = new MongoProperties(); + + private final MockEnvironment environment = new MockEnvironment(); + + @Test + void portCanBeCustomized() { + this.properties.setPort(12345); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 12345); + } + + @Test + void hostCanBeCustomized() { + this.properties.setHost("mongo.example.com"); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "mongo.example.com", 27017); + } + + @Test + void credentialsCanBeCustomized() { + this.properties.setUsername("user"); + this.properties.setPassword("secret".toCharArray()); + MongoClientSettings settings = customizeSettings(); + assertMongoCredential(settings.getCredential(), "user", "secret", "test"); + } + + @Test + void replicaSetCanBeCustomized() { + this.properties.setReplicaSetName("test"); + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isEqualTo("test"); + } + + @Test + void databaseCanBeCustomized() { + this.properties.setDatabase("foo"); + this.properties.setUsername("user"); + this.properties.setPassword("secret".toCharArray()); + MongoClientSettings settings = customizeSettings(); + assertMongoCredential(settings.getCredential(), "user", "secret", "foo"); + } + + @Test + void uuidRepresentationDefaultToJavaLegacy() { + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getUuidRepresentation()).isEqualTo(UuidRepresentation.JAVA_LEGACY); + } + + @Test + void uuidRepresentationCanBeCustomized() { + this.properties.setUuidRepresentation(UuidRepresentation.STANDARD); + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getUuidRepresentation()).isEqualTo(UuidRepresentation.STANDARD); + } + + @Test + void authenticationDatabaseCanBeCustomized() { + this.properties.setAuthenticationDatabase("foo"); + this.properties.setUsername("user"); + this.properties.setPassword("secret".toCharArray()); + MongoClientSettings settings = customizeSettings(); + assertMongoCredential(settings.getCredential(), "user", "secret", "foo"); + } + + @Test + void onlyHostAndPortSetShouldUseThat() { + this.properties.setHost("localhost"); + this.properties.setPort(27017); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 27017); + } + + @Test + void onlyUriSetShouldUseThat() { + this.properties.setUri("mongodb://mongo1.example.com:12345"); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); + } + + @Test + void noCustomAddressAndNoUriUsesDefaultUri() { + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 27017); + } + + @Test + void uriCanBeCustomized() { + this.properties.setUri("mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test"); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(2); + assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); + assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); + assertMongoCredential(settings.getCredential(), "user", "secret", "test"); + } + + @Test + void uriIsIgnoredInEmbeddedMode() { + this.properties.setUri("mongodb://mongo.example.com:1234/mydb"); + this.environment.setProperty("local.mongo.port", "4000"); + MongoClientSettings settings = customizeSettings(); + List allAddresses = getAllAddresses(settings); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 4000); + } + + @Test + void uriCannotBeSetWithCredentials() { + this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); + this.properties.setUsername("user"); + this.properties.setPassword("secret".toCharArray()); + assertThatIllegalStateException().isThrownBy(this::customizeSettings).withMessageContaining( + "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); + } + + @Test + void uriCannotBeSetWithReplicaSetName() { + this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); + this.properties.setReplicaSetName("test"); + assertThatIllegalStateException().isThrownBy(this::customizeSettings).withMessageContaining( + "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); + } + + @Test + void uriCannotBeSetWithHostPort() { + this.properties.setUri("mongodb://127.0.0.1:1234/mydb"); + this.properties.setHost("localhost"); + this.properties.setPort(4567); + assertThatIllegalStateException().isThrownBy(this::customizeSettings).withMessageContaining( + "Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified"); + } + + @Test + void retryWritesIsPropagatedFromUri() { + this.properties.setUri("mongodb://localhost/test?retryWrites=false"); + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getRetryWrites()).isFalse(); + } + + private MongoClientSettings customizeSettings() { + MongoClientSettings.Builder settings = MongoClientSettings.builder(); + new MongoPropertiesClientSettingsBuilderCustomizer(this.properties, this.environment).customize(settings); + return settings.build(); + } + + private List getAllAddresses(MongoClientSettings settings) { + return settings.getClusterSettings().getHosts(); + } + + protected void assertServerAddress(ServerAddress serverAddress, String expectedHost, int expectedPort) { + assertThat(serverAddress.getHost()).isEqualTo(expectedHost); + assertThat(serverAddress.getPort()).isEqualTo(expectedPort); + } + + protected void assertMongoCredential(MongoCredential credentials, String expectedUsername, String expectedPassword, + String expectedSource) { + assertThat(credentials.getUserName()).isEqualTo(expectedUsername); + assertThat(credentials.getPassword()).isEqualTo(expectedPassword.toCharArray()); + assertThat(credentials.getSource()).isEqualTo(expectedSource); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java index 7cac14aa41..337cde3972 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java @@ -22,7 +22,6 @@ import com.mongodb.MongoClientSettings; import com.mongodb.internal.async.client.AsyncMongoClient; import com.mongodb.reactivestreams.client.MongoClient; -import org.springframework.core.env.Environment; import org.springframework.test.util.ReflectionTestUtils; /** @@ -35,9 +34,9 @@ import org.springframework.test.util.ReflectionTestUtils; class ReactiveMongoClientFactoryTests extends MongoClientFactorySupportTests { @Override - protected MongoClient createMongoClient(MongoProperties properties, Environment environment, - List customizers, MongoClientSettings settings) { - return new ReactiveMongoClientFactory(properties, environment, customizers).createMongoClient(settings); + protected MongoClient createMongoClient(List customizers, + MongoClientSettings settings) { + return new ReactiveMongoClientFactory(customizers).createMongoClient(settings); } @Override diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index 39b06def51..9a5803dc0d 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -4365,6 +4365,14 @@ The following example shows how to connect to a MongoDB database: } ---- +If you have defined your own `MongoClient`, it will be used to auto-configure a suitable `MongoDatabaseFactory`. + +The auto-configured `MongoClient` is created using a `MongoClientSettings` bean. +If you have defined your own `MongoClientSettings`, it will be used without modification and the `spring.data.mongodb` properties will be ignored. +Otherwise a `MongoClientSettings` will be auto-configured and will have the `spring.data.mongodb` properties applied to it. +In either case, you can declare one or more `MongoClientSettingsBuilderCustomizer` beans to fine-tune the `MongoClientSettings` configuration. +Each will be called in order with the `MongoClientSettings.Builder` that is used to build the `MongoClientSettings`. + You can set the configprop:spring.data.mongodb.uri[] property to change the URL and configure additional settings such as the _replica set_, as shown in the following example: [source,properties,indent=0,configprops] @@ -4384,12 +4392,6 @@ For example, you might declare the following settings in your `application.prope spring.data.mongodb.password=secret ---- -If you have defined your own `MongoClient`, it will be used to auto-configure a suitable `MongoDatabaseFactory`. - -The auto-configured `MongoClient` is created using `MongoClientSettings`. -To fine-tune its configuration, declare one or more `MongoClientSettingsBuilderCustomizer` beans. -Each will be called in order with the `MongoClientSettings.Builder` that is used to build the `MongoClientSettings`. - TIP: If `spring.data.mongodb.port` is not specified, the default of `27017` is used. You could delete this line from the example shown earlier.