Browse Source
Prior to this commit, values from MongoProperties would always overwrite matching fields in MongoClientSettings. This commit preserves all values in MongoClientSettings if the client app provides the MongoClientSettings bean, and only overwrites from MongoProperties if no MongoClientSettings bean is provided. Fixes gh-22321pull/22412/head
11 changed files with 456 additions and 322 deletions
@ -0,0 +1,143 @@
@@ -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> 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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,210 @@
@@ -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<ServerAddress> 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<ServerAddress> 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<ServerAddress> 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<ServerAddress> allAddresses = getAllAddresses(settings); |
||||
assertThat(allAddresses).hasSize(1); |
||||
assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); |
||||
} |
||||
|
||||
@Test |
||||
void noCustomAddressAndNoUriUsesDefaultUri() { |
||||
MongoClientSettings settings = customizeSettings(); |
||||
List<ServerAddress> 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<ServerAddress> 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<ServerAddress> 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<ServerAddress> 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); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue