Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package org.springframework.boot.mongodb.autoconfigure;

import com.mongodb.ConnectionString;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import org.jspecify.annotations.Nullable;

import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
Expand All @@ -28,6 +31,7 @@
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @author Jay Choi
* @since 4.0.0
*/
public interface MongoConnectionDetails extends ConnectionDetails {
Expand All @@ -46,4 +50,28 @@ public interface MongoConnectionDetails extends ConnectionDetails {
return null;
}

/**
* The {@link ReadConcern} for MongoDB.
* @return the read concern option
*/
default @Nullable ReadConcern getReadConcern() {
return null;
}

/**
* The {@link WriteConcern} for MongoDB.
* @return the write concern options
*/
default @Nullable WriteConcern getWriteConcern() {
return null;
}

/**
* The {@link ReadPreference} for MongoDB.
* @return the read preference options
*/
default @Nullable ReadPreference getReadPreference() {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package org.springframework.boot.mongodb.autoconfigure;

import java.time.Duration;
import java.util.List;
import java.util.Map;

import com.mongodb.ConnectionString;
import org.bson.UuidRepresentation;
Expand All @@ -37,6 +39,7 @@
* @author Mark Paluch
* @author Artsiom Yudovin
* @author Safeer Ansari
* @author Jay Choi
* @since 4.0.0
*/
@ConfigurationProperties("spring.mongodb")
Expand Down Expand Up @@ -104,10 +107,20 @@ public class MongoProperties {
*/
private @Nullable String replicaSetName;

/**
* Read concern level for read operations. Supported values are 'local', 'available',
* 'majority', 'linearizable', and 'snapshot'. Overrides the value in 'uri' if set.
*/
private @Nullable String readConcern;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it's not an enum?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I was working with multiple properties and didn't think of it. I agree that using an enum would be better. In the same vein, I'm thinking of using an enum for ReadPreference.mode as well.


private final Representation representation = new Representation();

private final Ssl ssl = new Ssl();

private final WriteConcern writeConcern = new WriteConcern();

private final ReadPreference readPreference = new ReadPreference();

public void setProtocol(String protocol) {
this.protocol = protocol;
}
Expand Down Expand Up @@ -199,6 +212,14 @@ public void setAdditionalHosts(@Nullable List<String> additionalHosts) {
this.additionalHosts = additionalHosts;
}

public @Nullable String getReadConcern() {
return this.readConcern;
}

public void setReadConcern(@Nullable String readConcern) {
this.readConcern = readConcern;
}

public Representation getRepresentation() {
return this.representation;
}
Expand All @@ -207,6 +228,14 @@ public Ssl getSsl() {
return this.ssl;
}

public WriteConcern getWriteConcern() {
return this.writeConcern;
}

public ReadPreference getReadPreference() {
return this.readPreference;
}

public static class Representation {

/**
Expand Down Expand Up @@ -255,4 +284,101 @@ public void setBundle(@Nullable String bundle) {

}

public static class WriteConcern {

/**
* The w value. Can be 'majority' or a number representing the number of nodes
* that must acknowledge the write. Overrides the write concern in 'uri' if any
* write-concern property is set.
*/
private @Nullable String w;

/**
* Whether to block until write operations have been committed to the journal.
* Overrides the write concern in 'uri' if any write-concern property is set.
*/
private @Nullable Boolean journal;

/**
* Timeout for secondaries to acknowledge the write before failing. Overrides the
* write concern in 'uri' if any write-concern property is set.
*/
private @Nullable Duration wTimeout;

public @Nullable String getW() {
return this.w;
}

public void setW(@Nullable String w) {
this.w = w;
}

public @Nullable Boolean getJournal() {
return this.journal;
}

public void setJournal(@Nullable Boolean journal) {
this.journal = journal;
}

public @Nullable Duration getWTimeout() {
return this.wTimeout;
}

public void setWTimeout(@Nullable Duration wTimeout) {
this.wTimeout = wTimeout;
}

}

public static class ReadPreference {

/**
* Read preference mode for read operations. Supported values are 'primary',
* 'primaryPreferred', 'secondary', 'secondaryPreferred', and 'nearest'. Overrides
* the read preference in 'uri' if any read-preference property is set.
*/
private @Nullable String mode;

/**
* List of tag sets for read preference. Each entry in the list represents a tag
* set with fallback priority. An empty map represents a wildcard tag set that
* matches any replica set member. Overrides the read preference in 'uri' if any
* read-preference property is set.
*/
private @Nullable List<Map<String, String>> tags;

/**
* Maximum staleness duration for read preference. The minimum value is 90
* seconds. Overrides the read preference in 'uri' if any read-preference property
* is set.
*/
private @Nullable Duration maxStaleness;

public @Nullable String getMode() {
return this.mode;
}

public void setMode(@Nullable String mode) {
this.mode = mode;
}

public @Nullable List<Map<String, String>> getTags() {
return this.tags;
}

public void setTags(@Nullable List<Map<String, String>> tags) {
this.tags = tags;
}

public @Nullable Duration getMaxStaleness() {
return this.maxStaleness;
}

public void setMaxStaleness(@Nullable Duration maxStaleness) {
this.maxStaleness = maxStaleness;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.mongodb.ConnectionString;
import com.mongodb.ReadConcern;
import com.mongodb.ReadConcernLevel;
import com.mongodb.ReadPreference;
import com.mongodb.Tag;
import com.mongodb.TagSet;
import com.mongodb.WriteConcern;
import org.jspecify.annotations.Nullable;

import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.boot.mongodb.autoconfigure.MongoProperties.Ssl;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
Expand All @@ -37,6 +47,7 @@
* @author Andy Wilkinson
* @author Phillip Webb
* @author Scott Frederick
* @author Jay Choi
* @since 4.0.0
*/
public class PropertiesMongoConnectionDetails implements MongoConnectionDetails {
Expand Down Expand Up @@ -113,6 +124,67 @@ private char[] encode(char[] input) {
return SslBundle.systemDefault();
}

@Override
public @Nullable ReadConcern getReadConcern() {
String readConcernLevel = this.properties.getReadConcern();
if (readConcernLevel == null) {
return null;
}
return new ReadConcern(ReadConcernLevel.fromString(readConcernLevel));
}

@Override
public @Nullable WriteConcern getWriteConcern() {
String w = this.properties.getWriteConcern().getW();
Duration wTimeout = this.properties.getWriteConcern().getWTimeout();
Boolean journal = this.properties.getWriteConcern().getJournal();
WriteConcern writeConcern = null;
if (w != null || wTimeout != null || journal != null) {
if (w == null) {
writeConcern = WriteConcern.ACKNOWLEDGED;
}
else {
try {
writeConcern = new WriteConcern(Integer.parseInt(w));
}
catch (NumberFormatException ex) {
writeConcern = new WriteConcern(w);
}
}
if (wTimeout != null) {
writeConcern = writeConcern.withWTimeout(wTimeout.toMillis(), TimeUnit.MILLISECONDS);
}
if (journal != null) {
writeConcern = writeConcern.withJournal(journal);
}
}
return writeConcern;
}

@Override
public @Nullable ReadPreference getReadPreference() {
String mode = this.properties.getReadPreference().getMode();
List<Map<String, String>> tags = this.properties.getReadPreference().getTags();
Duration maxStaleness = this.properties.getReadPreference().getMaxStaleness();
List<TagSet> tagSetList = getTagSets(tags);
if (mode != null) {
if (tagSetList.isEmpty() && maxStaleness == null) {
return ReadPreference.valueOf(mode);
}
else if (maxStaleness == null) {
return ReadPreference.valueOf(mode, tagSetList);
}
else {
return ReadPreference.valueOf(mode, tagSetList, maxStaleness.toSeconds(), TimeUnit.SECONDS);
}
}
else if (!tagSetList.isEmpty() || maxStaleness != null) {
throw new InvalidConfigurationPropertyValueException("spring.mongodb.read-preference.mode", null,
"Read preference mode must be specified if tags or max-staleness is specified");
}
return null;
}

private List<String> getOptions() {
List<String> options = new ArrayList<>();
if (StringUtils.hasText(this.properties.getReplicaSetName())) {
Expand All @@ -124,4 +196,21 @@ private List<String> getOptions() {
return options;
}

private List<TagSet> getTagSets(@Nullable List<Map<String, String>> tags) {
List<TagSet> tagSetList = new ArrayList<>();
if (tags != null && !tags.isEmpty()) {
for (Map<String, String> tag : tags) {
if (tag == null || tag.isEmpty()) {
tagSetList.add(new TagSet());
}
else {
List<Tag> tagList = new ArrayList<>();
tag.forEach((key, value) -> tagList.add(new Tag(key, value)));
tagSetList.add(new TagSet(tagList));
}
}
}
return tagSetList;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package org.springframework.boot.mongodb.autoconfigure;

import com.mongodb.MongoClientSettings;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.connection.SslSettings;
import org.bson.UuidRepresentation;

Expand All @@ -31,6 +34,7 @@
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @author Jay Choi
* @since 4.0.0
*/
public class StandardMongoClientSettingsBuilderCustomizer implements MongoClientSettingsBuilderCustomizer, Ordered {
Expand All @@ -52,6 +56,18 @@ public void customize(MongoClientSettings.Builder settingsBuilder) {
settingsBuilder.uuidRepresentation(this.uuidRepresentation);
settingsBuilder.applyConnectionString(this.connectionDetails.getConnectionString());
settingsBuilder.applyToSslSettings(this::configureSslIfNeeded);
ReadConcern readConcern = this.connectionDetails.getReadConcern();
if (readConcern != null) {
settingsBuilder.readConcern(readConcern);
}
WriteConcern writeConcern = this.connectionDetails.getWriteConcern();
if (writeConcern != null) {
settingsBuilder.writeConcern(writeConcern);
}
ReadPreference readPreference = this.connectionDetails.getReadPreference();
if (readPreference != null) {
settingsBuilder.readPreference(readPreference);
}
}

private void configureSslIfNeeded(SslSettings.Builder settings) {
Expand Down
Loading