diff --git a/driver-core/src/main/com/mongodb/ConnectionString.java b/driver-core/src/main/com/mongodb/ConnectionString.java
index 210cdbd48e8..cd8f359f35c 100644
--- a/driver-core/src/main/com/mongodb/ConnectionString.java
+++ b/driver-core/src/main/com/mongodb/ConnectionString.java
@@ -23,6 +23,7 @@
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.dns.DefaultDnsResolver;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.DnsClient;
import org.bson.UuidRepresentation;
import java.io.UnsupportedEncodingException;
@@ -296,6 +297,21 @@ public class ConnectionString {
* @since 3.0
*/
public ConnectionString(final String connectionString) {
+ this(connectionString, null);
+ }
+
+ /**
+ * Creates a ConnectionString from the given string with the given {@link DnsClient}.
+ *
+ *
If setting {@link MongoClientSettings#getDnsClient()} explicitly, care should be taken to call this constructor with the same
+ * {@link DnsClient}.
+ *
+ * @param connectionString the connection string
+ * @param dnsClient the DNS client with which to resolve TXT record for the mongodb+srv protocol
+ * @since 4.10
+ * @see MongoClientSettings#getDnsClient()
+ */
+ public ConnectionString(final String connectionString, @Nullable final DnsClient dnsClient) {
this.connectionString = connectionString;
boolean isMongoDBProtocol = connectionString.startsWith(MONGODB_PREFIX);
isSrvProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
@@ -394,7 +410,7 @@ public ConnectionString(final String connectionString) {
}
String txtRecordsQueryParameters = isSrvProtocol
- ? new DefaultDnsResolver().resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
+ ? new DefaultDnsResolver(dnsClient).resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
String connectionStringQueryParameters = unprocessedConnectionString;
Map> connectionStringOptionsMap = parseOptions(connectionStringQueryParameters);
diff --git a/driver-core/src/main/com/mongodb/MongoClientSettings.java b/driver-core/src/main/com/mongodb/MongoClientSettings.java
index 700d4148c8f..cb68ac4c2f0 100644
--- a/driver-core/src/main/com/mongodb/MongoClientSettings.java
+++ b/driver-core/src/main/com/mongodb/MongoClientSettings.java
@@ -29,6 +29,8 @@
import com.mongodb.connection.StreamFactoryFactory;
import com.mongodb.event.CommandListener;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.DnsClient;
+import com.mongodb.spi.dns.InetAddressResolver;
import org.bson.UuidRepresentation;
import org.bson.codecs.BsonCodecProvider;
import org.bson.codecs.BsonValueCodecProvider;
@@ -107,6 +109,8 @@ public final class MongoClientSettings {
private final boolean heartbeatConnectTimeoutSetExplicitly;
private final ContextProvider contextProvider;
+ private final DnsClient dnsClient;
+ private final InetAddressResolver inetAddressResolver;
/**
* Gets the default codec registry. It includes the following providers:
@@ -160,6 +164,39 @@ public static Builder builder(final MongoClientSettings settings) {
return new Builder(settings);
}
+ /**
+ * Gets the {@link DnsClient} to use for resolving DNS queries.
+ *
+ * If set, it will be used to resolve SRV and TXT records for mongodb+srv connections. Otherwise,
+ * implementations of {@link com.mongodb.spi.dns.DnsClientProvider} will be discovered via {@link java.util.ServiceLoader}.
+ * If no implementations are discovered, then {@code com.sun.jndi.dns.DnsContextFactory} will be used to resolve these records.
+ *
+ *
If applying a connection string to these settings, care must be taken to also pass the same {@link DnsClient} as an argument to
+ * the {@link ConnectionString} constructor.
+ *
+ * @return the DNS client
+ * @since 4.10
+ * @see ConnectionString#ConnectionString(String, DnsClient)
+ */
+ @Nullable
+ public DnsClient getDnsClient() {
+ return dnsClient;
+ }
+
+ /**
+ * Gets the {@link InetAddressResolver} to use for looking up the {@link java.net.InetAddress} instances for each host.
+ *
+ *
If set, it will be used to look up the {@link java.net.InetAddress} for each host, via
+ * {@link InetAddressResolver#lookupByName(String)}. Otherwise, {@link java.net.InetAddress#getAllByName(String)} will be used.
+ *
+ * @return the {@link java.net.InetAddress} resolver
+ * @since 4.10
+ */
+ @Nullable
+ public InetAddressResolver getInetAddressResolver() {
+ return inetAddressResolver;
+ }
+
/**
* A builder for {@code MongoClientSettings} so that {@code MongoClientSettings} can be immutable, and to support easier construction
* through chaining.
@@ -193,6 +230,8 @@ public static final class Builder {
private int heartbeatSocketTimeoutMS;
private ContextProvider contextProvider;
+ private DnsClient dnsClient;
+ private InetAddressResolver inetAddressResolver;
private Builder() {
}
@@ -211,6 +250,8 @@ private Builder(final MongoClientSettings settings) {
credential = settings.getCredential();
uuidRepresentation = settings.getUuidRepresentation();
serverApi = settings.getServerApi();
+ dnsClient = settings.getDnsClient();
+ inetAddressResolver = settings.getInetAddressResolver();
streamFactoryFactory = settings.getStreamFactoryFactory();
autoEncryptionSettings = settings.getAutoEncryptionSettings();
contextProvider = settings.getContextProvider();
@@ -220,6 +261,7 @@ private Builder(final MongoClientSettings settings) {
socketSettingsBuilder.applySettings(settings.getSocketSettings());
connectionPoolSettingsBuilder.applySettings(settings.getConnectionPoolSettings());
sslSettingsBuilder.applySettings(settings.getSslSettings());
+
if (settings.heartbeatConnectTimeoutSetExplicitly) {
heartbeatConnectTimeoutMS = settings.heartbeatSocketSettings.getConnectTimeout(MILLISECONDS);
}
@@ -273,7 +315,7 @@ public Builder applyConnectionString(final ConnectionString connectionString) {
/**
* Applies the {@link LoggerSettings.Builder} block and then sets the loggerSettings.
*
- * @param block the block to apply to the LoggerSettins.
+ * @param block the block to apply to the LoggerSettings.
* @return this
* @see MongoClientSettings#getLoggerSettings()
* @since 4.9
@@ -580,6 +622,45 @@ public Builder contextProvider(@Nullable final ContextProvider contextProvider)
return this;
}
+ /**
+ * Sets the {@link DnsClient} to use for resolving DNS queries.
+ *
+ *
If set, it will be used to resolve SRV and TXT records for mongodb+srv connections. Otherwise,
+ * implementation of {@link com.mongodb.spi.dns.DnsClientProvider} will be discovered via {@link java.util.ServiceLoader}
+ * and used to create an instance of {@link DnsClient}. If no implementation is discovered, then
+ * {@code com.sun.jndi.dns.DnsContextFactory} will be used to resolve these records.
+ *
+ *
If {@linkplain #applyConnectionString(ConnectionString) applying a connection string to these settings}, care must be
+ * taken to also pass the same {@link DnsClient} as an argument to the {@link ConnectionString} constructor.
+ *
+ * @param dnsClient the DNS client
+ * @return the DNS client
+ * @since 4.10
+ * @see ConnectionString#ConnectionString(String, DnsClient)
+ */
+ public Builder dnsClient(@Nullable final DnsClient dnsClient) {
+ this.dnsClient = dnsClient;
+ return this;
+ }
+
+ /**
+ * Sets the {@link InetAddressResolver} to use for looking up the {@link java.net.InetAddress} instances for each host.
+ *
+ *
If set, it will be used to look up the {@link java.net.InetAddress} for each host, via
+ * {@link InetAddressResolver#lookupByName(String)}. Otherwise,
+ * an implementation of {@link com.mongodb.spi.dns.InetAddressResolverProvider} will be discovered via
+ * {@link java.util.ServiceLoader} and used to create an instance of {@link InetAddressResolver}. If no implementation is
+ * discovered, {@link java.net.InetAddress#getAllByName(String)} will be used to lookup the {@link java.net.InetAddress}
+ * instances for a host.
+ *
+ * @param inetAddressResolver the InetAddress provider
+ * @return the {@link java.net.InetAddress} resolver
+ * @since 4.10
+ */
+ public Builder inetAddressResolver(@Nullable final InetAddressResolver inetAddressResolver) {
+ this.inetAddressResolver = inetAddressResolver;
+ return this;
+ }
// Package-private to provide interop with MongoClientOptions
Builder heartbeatConnectTimeoutMS(final int heartbeatConnectTimeoutMS) {
@@ -905,6 +986,8 @@ public boolean equals(final Object o) {
&& uuidRepresentation == that.uuidRepresentation
&& Objects.equals(serverApi, that.serverApi)
&& Objects.equals(autoEncryptionSettings, that.autoEncryptionSettings)
+ && Objects.equals(dnsClient, that.dnsClient)
+ && Objects.equals(inetAddressResolver, that.inetAddressResolver)
&& Objects.equals(contextProvider, that.contextProvider);
}
@@ -913,7 +996,8 @@ public int hashCode() {
return Objects.hash(readPreference, writeConcern, retryWrites, retryReads, readConcern, credential, streamFactoryFactory,
commandListeners, codecRegistry, loggerSettings, clusterSettings, socketSettings, heartbeatSocketSettings,
connectionPoolSettings, serverSettings, sslSettings, applicationName, compressorList, uuidRepresentation, serverApi,
- autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, heartbeatConnectTimeoutSetExplicitly, contextProvider);
+ autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, heartbeatConnectTimeoutSetExplicitly, dnsClient,
+ inetAddressResolver, contextProvider);
}
@Override
@@ -940,6 +1024,8 @@ public String toString() {
+ ", uuidRepresentation=" + uuidRepresentation
+ ", serverApi=" + serverApi
+ ", autoEncryptionSettings=" + autoEncryptionSettings
+ + ", dnsClient=" + dnsClient
+ + ", inetAddressResolver=" + inetAddressResolver
+ ", contextProvider=" + contextProvider
+ '}';
}
@@ -964,6 +1050,8 @@ private MongoClientSettings(final Builder builder) {
compressorList = builder.compressorList;
uuidRepresentation = builder.uuidRepresentation;
serverApi = builder.serverApi;
+ dnsClient = builder.dnsClient;
+ inetAddressResolver = builder.inetAddressResolver;
autoEncryptionSettings = builder.autoEncryptionSettings;
heartbeatSocketSettings = SocketSettings.builder()
.readTimeout(builder.heartbeatSocketTimeoutMS == 0
diff --git a/driver-core/src/main/com/mongodb/MongoSocketException.java b/driver-core/src/main/com/mongodb/MongoSocketException.java
index 2641b39abae..820c2cb769f 100644
--- a/driver-core/src/main/com/mongodb/MongoSocketException.java
+++ b/driver-core/src/main/com/mongodb/MongoSocketException.java
@@ -33,7 +33,7 @@ public class MongoSocketException extends MongoException {
* @param msg the message
* @param e the cause
*/
- MongoSocketException(final String msg, final ServerAddress serverAddress, final Throwable e) {
+ public MongoSocketException(final String msg, final ServerAddress serverAddress, final Throwable e) {
super(-2, msg, e);
this.serverAddress = serverAddress;
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
index 26c19ee7ec7..d7749ce30c5 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
@@ -32,6 +32,8 @@
import com.mongodb.event.ServerListener;
import com.mongodb.event.ServerMonitorListener;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.DnsClient;
+import com.mongodb.spi.dns.InetAddressResolver;
import java.util.List;
@@ -59,7 +61,8 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina
@Nullable final CommandListener commandListener,
@Nullable final String applicationName,
@Nullable final MongoDriverInformation mongoDriverInformation,
- final List compressorList, @Nullable final ServerApi serverApi) {
+ final List compressorList, @Nullable final ServerApi serverApi,
+ @Nullable final DnsClient dnsClient, @Nullable final InetAddressResolver inetAddressResolver) {
ClusterId clusterId = new ClusterId(applicationName);
ClusterSettings clusterSettings;
@@ -87,20 +90,20 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina
.build();
}
- DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = new DefaultDnsSrvRecordMonitorFactory(clusterId, serverSettings);
+ DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = new DefaultDnsSrvRecordMonitorFactory(clusterId, serverSettings, dnsClient);
if (clusterSettings.getMode() == ClusterConnectionMode.LOAD_BALANCED) {
ClusterableServerFactory serverFactory = new LoadBalancedClusterableServerFactory(serverSettings,
connectionPoolSettings, internalConnectionPoolSettings, streamFactory, credential, loggerSettings, commandListener,
applicationName, mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(),
- compressorList, serverApi);
+ compressorList, serverApi, inetAddressResolver);
return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, dnsSrvRecordMonitorFactory);
} else {
ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(serverSettings,
connectionPoolSettings, internalConnectionPoolSettings,
streamFactory, heartbeatStreamFactory, credential, loggerSettings, commandListener, applicationName,
mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList,
- serverApi);
+ serverApi, inetAddressResolver);
if (clusterSettings.getMode() == ClusterConnectionMode.SINGLE) {
return new SingleServerCluster(clusterId, clusterSettings, serverFactory);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
index 2c6d349c58f..c5e66200de1 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
@@ -31,6 +31,7 @@
import com.mongodb.event.ServerListener;
import com.mongodb.internal.inject.SameObjectProvider;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.InetAddressResolver;
import java.util.List;
@@ -54,6 +55,8 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory
private final List compressorList;
@Nullable
private final ServerApi serverApi;
+ @Nullable
+ private final InetAddressResolver inetAddressResolver;
public DefaultClusterableServerFactory(
final ServerSettings serverSettings, final ConnectionPoolSettings connectionPoolSettings,
@@ -63,7 +66,8 @@ public DefaultClusterableServerFactory(
final LoggerSettings loggerSettings,
@Nullable final CommandListener commandListener,
@Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation,
- final List compressorList, @Nullable final ServerApi serverApi) {
+ final List compressorList, @Nullable final ServerApi serverApi,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this.serverSettings = serverSettings;
this.connectionPoolSettings = connectionPoolSettings;
this.internalConnectionPoolSettings = internalConnectionPoolSettings;
@@ -76,6 +80,7 @@ public DefaultClusterableServerFactory(
this.mongoDriverInformation = mongoDriverInformation;
this.compressorList = compressorList;
this.serverApi = serverApi;
+ this.inetAddressResolver = inetAddressResolver;
}
@Override
@@ -86,11 +91,11 @@ public ClusterableServer create(final Cluster cluster, final ServerAddress serve
ServerMonitor serverMonitor = new DefaultServerMonitor(serverId, serverSettings, cluster.getClock(),
// no credentials, compressor list, or command listener for the server monitor factory
new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, applicationName,
- mongoDriverInformation, emptyList(), loggerSettings, null, serverApi),
+ mongoDriverInformation, emptyList(), loggerSettings, null, serverApi, inetAddressResolver),
clusterMode, serverApi, sdamProvider);
ConnectionPool connectionPool = new DefaultConnectionPool(serverId,
new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, applicationName,
- mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi),
+ mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi, inetAddressResolver),
connectionPoolSettings, internalConnectionPoolSettings, sdamProvider);
ServerListener serverListener = singleServerListener(serverSettings);
SdamServerDescriptionManager sdam = new DefaultSdamServerDescriptionManager(cluster, serverId, serverListener, serverMonitor,
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultDnsSrvRecordMonitorFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultDnsSrvRecordMonitorFactory.java
index c584e9547ab..d303acdef99 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultDnsSrvRecordMonitorFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultDnsSrvRecordMonitorFactory.java
@@ -19,6 +19,8 @@
import com.mongodb.connection.ClusterId;
import com.mongodb.connection.ServerSettings;
import com.mongodb.internal.dns.DefaultDnsResolver;
+import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.DnsClient;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -32,15 +34,17 @@ public class DefaultDnsSrvRecordMonitorFactory implements DnsSrvRecordMonitorFac
private final ClusterId clusterId;
private final long noRecordsRescanFrequency;
+ private final DnsClient dnsClient;
- public DefaultDnsSrvRecordMonitorFactory(final ClusterId clusterId, final ServerSettings serverSettings) {
+ public DefaultDnsSrvRecordMonitorFactory(final ClusterId clusterId, final ServerSettings serverSettings, @Nullable final DnsClient dnsClient) {
this.clusterId = clusterId;
this.noRecordsRescanFrequency = serverSettings.getHeartbeatFrequency(MILLISECONDS);
+ this.dnsClient = dnsClient;
}
@Override
public DnsSrvRecordMonitor create(final String hostName, final String srvServiceName, final DnsSrvRecordInitializer dnsSrvRecordInitializer) {
return new DefaultDnsSrvRecordMonitor(hostName, srvServiceName, DEFAULT_RESCAN_FREQUENCY_MILLIS, noRecordsRescanFrequency,
- dnsSrvRecordInitializer, clusterId, new DefaultDnsResolver());
+ dnsSrvRecordInitializer, clusterId, new DefaultDnsResolver(dnsClient));
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
index ac9f70943e6..cf331dee4f4 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
@@ -48,6 +48,7 @@
import com.mongodb.internal.logging.StructuredLogger;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.InetAddressResolver;
import org.bson.BsonBinaryReader;
import org.bson.BsonDocument;
import org.bson.ByteBuf;
@@ -118,6 +119,7 @@ public class InternalStreamConnection implements InternalConnection {
private final ConnectionGenerationSupplier connectionGenerationSupplier;
private final StreamFactory streamFactory;
private final InternalConnectionInitializer connectionInitializer;
+ private final InetAddressResolver inetAddressResolver;
private volatile ConnectionDescription description;
private volatile ServerDescription initialServerDescription;
@@ -149,17 +151,19 @@ static Set getSecuritySensitiveHelloCommands() {
public InternalStreamConnection(final ClusterConnectionMode clusterConnectionMode, final ServerId serverId,
final ConnectionGenerationSupplier connectionGenerationSupplier,
final StreamFactory streamFactory, final List compressorList,
- final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer) {
+ final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this(clusterConnectionMode, false, serverId, connectionGenerationSupplier, streamFactory, compressorList,
- LoggerSettings.builder().build(), commandListener, connectionInitializer);
+ LoggerSettings.builder().build(), commandListener, connectionInitializer, inetAddressResolver);
}
public InternalStreamConnection(final ClusterConnectionMode clusterConnectionMode, final boolean isMonitoringConnection,
- final ServerId serverId,
- final ConnectionGenerationSupplier connectionGenerationSupplier,
- final StreamFactory streamFactory, final List compressorList,
- final LoggerSettings loggerSettings,
- final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer) {
+ final ServerId serverId,
+ final ConnectionGenerationSupplier connectionGenerationSupplier,
+ final StreamFactory streamFactory, final List compressorList,
+ final LoggerSettings loggerSettings,
+ final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this.clusterConnectionMode = clusterConnectionMode;
this.isMonitoringConnection = isMonitoringConnection;
this.serverId = notNull("serverId", serverId);
@@ -176,6 +180,7 @@ public InternalStreamConnection(final ClusterConnectionMode clusterConnectionMod
.type(ServerType.UNKNOWN)
.state(ServerConnectionState.CONNECTING)
.build();
+ this.inetAddressResolver = inetAddressResolver;
if (clusterConnectionMode != ClusterConnectionMode.LOAD_BALANCED) {
generation = connectionGenerationSupplier.getGeneration();
}
@@ -199,7 +204,7 @@ public int getGeneration() {
@Override
public void open() {
isTrue("Open already called", stream == null);
- stream = streamFactory.create(serverId.getAddress());
+ stream = streamFactory.create(getServerAddressWithResolver());
try {
stream.open();
@@ -222,7 +227,7 @@ public void open() {
public void openAsync(final SingleResultCallback callback) {
isTrue("Open already called", stream == null, callback);
try {
- stream = streamFactory.create(serverId.getAddress());
+ stream = streamFactory.create(getServerAddressWithResolver());
stream.openAsync(new AsyncCompletionHandler() {
@Override
public void completed(@Nullable final Void aVoid) {
@@ -261,6 +266,9 @@ public void failed(final Throwable t) {
}
}
+ private ServerAddress getServerAddressWithResolver() {
+ return new ServerAddressWithResolver(serverId.getAddress(), inetAddressResolver);
+ }
private void initAfterHandshakeStart(final InternalConnectionInitializationDescription initializationDescription) {
description = initializationDescription.getConnectionDescription();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java
index 14312edef17..2431a3b800a 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java
@@ -25,6 +25,7 @@
import com.mongodb.connection.StreamFactory;
import com.mongodb.event.CommandListener;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.InetAddressResolver;
import org.bson.BsonDocument;
import java.util.List;
@@ -39,28 +40,31 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory {
private final StreamFactory streamFactory;
private final BsonDocument clientMetadataDocument;
private final List compressorList;
- private LoggerSettings loggerSettings;
+ private final LoggerSettings loggerSettings;
private final CommandListener commandListener;
@Nullable
private final ServerApi serverApi;
+ private final InetAddressResolver inetAddressResolver;
private final MongoCredentialWithCache credential;
InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode,
final StreamFactory streamFactory,
@Nullable final MongoCredentialWithCache credential,
- @Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation,
+ @Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation,
final List compressorList,
- final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) {
+ final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this(clusterConnectionMode, false, streamFactory, credential, applicationName, mongoDriverInformation, compressorList,
- loggerSettings, commandListener, serverApi);
+ loggerSettings, commandListener, serverApi, inetAddressResolver);
}
InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode, final boolean isMonitoringConnection,
final StreamFactory streamFactory,
@Nullable final MongoCredentialWithCache credential,
- @Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation,
+ @Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation,
final List compressorList,
- final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) {
+ final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this.clusterConnectionMode = clusterConnectionMode;
this.isMonitoringConnection = isMonitoringConnection;
this.streamFactory = notNull("streamFactory", streamFactory);
@@ -68,6 +72,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory {
this.loggerSettings = loggerSettings;
this.commandListener = commandListener;
this.serverApi = serverApi;
+ this.inetAddressResolver = inetAddressResolver;
this.clientMetadataDocument = createClientMetadataDocument(applicationName, mongoDriverInformation);
this.credential = credential;
}
@@ -78,7 +83,7 @@ public InternalConnection create(final ServerId serverId, final ConnectionGenera
return new InternalStreamConnection(clusterConnectionMode, isMonitoringConnection, serverId, connectionGenerationSupplier,
streamFactory, compressorList, loggerSettings, commandListener,
new InternalStreamConnectionInitializer(clusterConnectionMode, authenticator, clientMetadataDocument, compressorList,
- serverApi));
+ serverApi), inetAddressResolver);
}
private Authenticator createAuthenticator(final MongoCredentialWithCache credential) {
diff --git a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java
index 2ff282e9867..54d3aca40e0 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java
@@ -31,6 +31,7 @@
import com.mongodb.event.CommandListener;
import com.mongodb.internal.inject.EmptyProvider;
import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.InetAddressResolver;
import java.util.List;
@@ -52,15 +53,17 @@ public class LoadBalancedClusterableServerFactory implements ClusterableServerFa
private final MongoDriverInformation mongoDriverInformation;
private final List compressorList;
private final ServerApi serverApi;
+ private final InetAddressResolver inetAddressResolver;
public LoadBalancedClusterableServerFactory(final ServerSettings serverSettings,
- final ConnectionPoolSettings connectionPoolSettings,
- final InternalConnectionPoolSettings internalConnectionPoolSettings,
- final StreamFactory streamFactory, @Nullable final MongoCredential credential,
- final LoggerSettings loggerSettings,
- @Nullable final CommandListener commandListener,
- @Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation,
- final List compressorList, @Nullable final ServerApi serverApi) {
+ final ConnectionPoolSettings connectionPoolSettings,
+ final InternalConnectionPoolSettings internalConnectionPoolSettings,
+ final StreamFactory streamFactory, @Nullable final MongoCredential credential,
+ final LoggerSettings loggerSettings,
+ @Nullable final CommandListener commandListener,
+ @Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation,
+ final List compressorList, @Nullable final ServerApi serverApi,
+ @Nullable final InetAddressResolver inetAddressResolver) {
this.serverSettings = serverSettings;
this.connectionPoolSettings = connectionPoolSettings;
this.internalConnectionPoolSettings = internalConnectionPoolSettings;
@@ -72,13 +75,14 @@ public LoadBalancedClusterableServerFactory(final ServerSettings serverSettings,
this.mongoDriverInformation = mongoDriverInformation;
this.compressorList = compressorList;
this.serverApi = serverApi;
+ this.inetAddressResolver = inetAddressResolver;
}
@Override
public ClusterableServer create(final Cluster cluster, final ServerAddress serverAddress) {
ConnectionPool connectionPool = new DefaultConnectionPool(new ServerId(cluster.getClusterId(), serverAddress),
new InternalStreamConnectionFactory(ClusterConnectionMode.LOAD_BALANCED, streamFactory, credential, applicationName,
- mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi),
+ mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi, inetAddressResolver),
connectionPoolSettings, internalConnectionPoolSettings, EmptyProvider.instance());
connectionPool.ready();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/ServerAddressWithResolver.java b/driver-core/src/main/com/mongodb/internal/connection/ServerAddressWithResolver.java
new file mode 100644
index 00000000000..0caa448153c
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/connection/ServerAddressWithResolver.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.internal.connection;
+
+import com.mongodb.MongoSocketException;
+import com.mongodb.ServerAddress;
+import com.mongodb.lang.Nullable;
+import com.mongodb.spi.dns.InetAddressResolver;
+import com.mongodb.spi.dns.InetAddressResolverProvider;
+
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+final class ServerAddressWithResolver extends ServerAddress {
+ private static final long serialVersionUID = 1;
+
+ @Nullable
+ private static final InetAddressResolver DEFAULT_INET_ADDRESS_RESOLVER;
+
+ static {
+ DEFAULT_INET_ADDRESS_RESOLVER = StreamSupport.stream(ServiceLoader.load(InetAddressResolverProvider.class).spliterator(), false)
+ .findFirst()
+ .map(InetAddressResolverProvider::create)
+ .orElse(null);
+ }
+
+ @Nullable
+ private final transient InetAddressResolver resolver;
+
+ ServerAddressWithResolver(final ServerAddress serverAddress, @Nullable final InetAddressResolver inetAddressResolver) {
+ super(serverAddress.getHost(), serverAddress.getPort());
+ this.resolver = inetAddressResolver == null ? DEFAULT_INET_ADDRESS_RESOLVER : inetAddressResolver;
+ }
+
+ @Override
+ public InetSocketAddress getSocketAddress() {
+ if (resolver == null) {
+ return super.getSocketAddress();
+ }
+
+ return getSocketAddresses().get(0);
+ }
+
+ @Override
+ public List getSocketAddresses() {
+ if (resolver == null || isIpLiteral()) {
+ return super.getSocketAddresses();
+ }
+ try {
+ return resolver.lookupByName(getHost())
+ .stream()
+ .map(inetAddress -> new InetSocketAddress(inetAddress, getPort())).collect(Collectors.toList());
+ } catch (UnknownHostException e) {
+ throw new MongoSocketException(e.getMessage(), this, e);
+ }
+ }
+
+ // If this returns true, it's either an IP literal or a malformed hostname. But either way, skip lookup via resolver
+ private boolean isIpLiteral() {
+ return getHost().charAt(0) == '[' || Character.digit(getHost().charAt(0), 16) != -1 || (getHost().charAt(0) == ':');
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ ServerAddressWithResolver that = (ServerAddressWithResolver) o;
+ return Objects.equals(resolver, that.resolver);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), resolver);
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/internal/dns/DefaultDnsResolver.java b/driver-core/src/main/com/mongodb/internal/dns/DefaultDnsResolver.java
index acf98487577..d483e220253 100644
--- a/driver-core/src/main/com/mongodb/internal/dns/DefaultDnsResolver.java
+++ b/driver-core/src/main/com/mongodb/internal/dns/DefaultDnsResolver.java
@@ -17,6 +17,7 @@
package com.mongodb.internal.dns;
import com.mongodb.MongoConfigurationException;
+import com.mongodb.lang.Nullable;
import com.mongodb.spi.dns.DnsClient;
import com.mongodb.spi.dns.DnsClientProvider;
import com.mongodb.spi.dns.DnsWithResponseCodeException;
@@ -24,6 +25,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
+import java.util.stream.StreamSupport;
import static java.lang.String.format;
import static java.util.Arrays.asList;
@@ -35,21 +37,23 @@
*/
public final class DefaultDnsResolver implements DnsResolver {
+ private static final DnsClient DEFAULT_DNS_CLIENT;
+
+ static {
+ DEFAULT_DNS_CLIENT = StreamSupport.stream(ServiceLoader.load(DnsClientProvider.class).spliterator(), false)
+ .findFirst()
+ .map(DnsClientProvider::create)
+ .orElse(new JndiDnsClient());
+ }
+
private final DnsClient dnsClient;
public DefaultDnsResolver() {
- ServiceLoader loader = ServiceLoader.load(DnsClientProvider.class);
- DnsClient dnsClientFromServiceLoader = null;
- for (DnsClientProvider dnsClientProvider : loader) {
- dnsClientFromServiceLoader = dnsClientProvider.create();
- break;
- }
+ this(DEFAULT_DNS_CLIENT);
+ }
- if (dnsClientFromServiceLoader == null) {
- dnsClient = new JndiDnsClient();
- } else {
- dnsClient = dnsClientFromServiceLoader;
- }
+ public DefaultDnsResolver(@Nullable final DnsClient dnsClient) {
+ this.dnsClient = dnsClient == null ? DEFAULT_DNS_CLIENT : dnsClient;
}
/*
diff --git a/driver-core/src/main/com/mongodb/spi/dns/DnsClient.java b/driver-core/src/main/com/mongodb/spi/dns/DnsClient.java
index 64fd3424d39..482d48ec0a2 100644
--- a/driver-core/src/main/com/mongodb/spi/dns/DnsClient.java
+++ b/driver-core/src/main/com/mongodb/spi/dns/DnsClient.java
@@ -16,6 +16,9 @@
package com.mongodb.spi.dns;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.annotations.ThreadSafe;
+
import java.util.List;
@@ -24,7 +27,9 @@
*
* @since 4.6
* @see DnsClientProvider
+ * @see MongoClientSettings.Builder#dnsClient(DnsClient)
*/
+@ThreadSafe
public interface DnsClient {
/**
* Gets the resource record values for the given name and type.
diff --git a/driver-core/src/main/com/mongodb/spi/dns/DnsClientProvider.java b/driver-core/src/main/com/mongodb/spi/dns/DnsClientProvider.java
index 3a82835f981..85581ee8eed 100644
--- a/driver-core/src/main/com/mongodb/spi/dns/DnsClientProvider.java
+++ b/driver-core/src/main/com/mongodb/spi/dns/DnsClientProvider.java
@@ -17,15 +17,21 @@
package com.mongodb.spi.dns;
/**
- * A provider interface for {@link DnsClient}.
+ * Service-provider class for {@link DnsClient}.
*
- *
- * The driver discovers implementations of this interface via {@link java.util.ServiceLoader}.
- *
+ * A resolver provider is a factory for custom implementations of
+ * {@linkplain DnsClient a DNS client}. A DNS client defines operations for
+ * looking up DNS records for a given type.
+ *
+ *
The driver discovers implementations of this interface via {@link java.util.ServiceLoader}.
+ *
+ *
If more fine-grained control is required for multi-tenant applications, an
+ * {@linkplain DnsClient a DNS client} can be configured via
+ * {@link com.mongodb.MongoClientSettings.Builder#dnsClient(DnsClient)}.
*
* @since 4.6
* @see java.util.ServiceLoader
- */
+*/
public interface DnsClientProvider {
/**
* Construct a new instance of a {@link DnsClient}.
diff --git a/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolver.java b/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolver.java
new file mode 100644
index 00000000000..8a577579ec4
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.spi.dns;
+
+import com.mongodb.MongoClientSettings;
+import com.mongodb.annotations.ThreadSafe;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+
+/**
+ * This interface defines operations for looking up host names.
+ *
+ *
The default resolver for the driver can be customized by deploying an implementation of {@link InetAddressResolverProvider}.
+ *
+ * @see InetAddressResolverProvider
+ * @see MongoClientSettings.Builder#inetAddressResolver(InetAddressResolver)
+ * @since 4.10
+ */
+@ThreadSafe
+public interface InetAddressResolver {
+ /**
+ * Given the name of a host, returns a list of IP addresses of the requested
+ * address family associated with a provided hostname.
+ *
+ * Implementations are encouraged to implement their own caching policies, as there is
+ * no guarantee that the caller will implement a cache.
+ *
+ * @param host the host
+ * @return a list of IP addresses for the requested host
+ * @throws UnknownHostException if no IP addresses for the {@code host} could be found
+ */
+ List lookupByName(String host) throws UnknownHostException;
+}
diff --git a/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolverProvider.java b/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolverProvider.java
new file mode 100644
index 00000000000..0bbb92162a3
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/spi/dns/InetAddressResolverProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.spi.dns;
+
+/**
+ * Service-provider class for {@link InetAddressResolver}.
+ *
+ * A resolver provider is a factory for custom implementations of {@linkplain
+ * InetAddressResolver InetAddress resolvers}. A resolver defines operations for
+ * looking up (resolving) host names.
+ *
+ *
The driver discovers implementations of this interface via {@link java.util.ServiceLoader}.
+ *
+ *
If more fine-grained control is required for multi-tenant applications, an {@linkplain InetAddressResolver InetAddress resolver}
+ * can be configured via {@link com.mongodb.MongoClientSettings.Builder#inetAddressResolver(InetAddressResolver)}.
+ *
+ * @since 4.10
+ * @see java.util.ServiceLoader
+ */
+
+public interface InetAddressResolverProvider {
+ /**
+ * Construct a new instance of a {@link InetAddressResolver}.
+ *
+ * @return a {@link InetAddressResolver}
+ */
+ InetAddressResolver create();
+}
diff --git a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java
index 945c2aba10d..85b4a9cfeac 100644
--- a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java
+++ b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java
@@ -379,7 +379,7 @@ private static Cluster createCluster(final MongoCredential credential, final Str
ServerSettings.builder().build(),
ConnectionPoolSettings.builder().maxSize(1).build(), InternalConnectionPoolSettings.builder().build(),
streamFactory, streamFactory, credential, LoggerSettings.builder().build(), null, null, null,
- Collections.emptyList(), getServerApi());
+ Collections.emptyList(), getServerApi(), null, null);
}
private static Cluster createCluster(final ConnectionString connectionString, final StreamFactory streamFactory) {
@@ -391,7 +391,7 @@ private static Cluster createCluster(final ConnectionString connectionString, fi
new SocketStreamFactory(SocketSettings.builder().readTimeout(5, SECONDS).build(), getSslSettings(connectionString)),
connectionString.getCredential(),
LoggerSettings.builder().build(), null, null, null,
- connectionString.getCompressorList(), getServerApi());
+ connectionString.getCompressorList(), getServerApi(), null, null);
}
public static StreamFactory getStreamFactory() {
diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy
index d58a4dd067b..4f6f360d857 100644
--- a/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy
+++ b/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy
@@ -49,7 +49,7 @@ class CommandHelperSpecification extends Specification {
def setup() {
connection = new InternalStreamConnectionFactory(ClusterConnectionMode.SINGLE,
new NettyStreamFactory(SocketSettings.builder().build(), getSslSettings()),
- getCredentialWithCache(), null, null, [], LoggerSettings.builder().build(), null, getServerApi())
+ getCredentialWithCache(), null, null, [], LoggerSettings.builder().build(), null, getServerApi(), null)
.create(new ServerId(new ClusterId(), getPrimary()))
connection.open()
}
diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java
index 82e83b3b568..348f4fdf47c 100644
--- a/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java
+++ b/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java
@@ -54,7 +54,8 @@ public void setUp() {
source = System.getProperty("org.mongod.test.source");
password = System.getProperty("org.mongodb.test.password");
internalConnection = new InternalStreamConnectionFactory(ClusterConnectionMode.SINGLE, streamFactory, null, null,
- null, Collections.emptyList(), LoggerSettings.builder().build(), null, getServerApi()).create(new ServerId(new ClusterId(),
+ null, Collections.emptyList(), LoggerSettings.builder().build(), null, getServerApi(),
+ null).create(new ServerId(new ClusterId(),
new ServerAddress(host)));
connectionDescription = new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress()));
}
diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy
index 5f9f940c98b..6d1cb0133db 100644
--- a/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy
+++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy
@@ -226,7 +226,7 @@ class ServerMonitorSpecification extends OperationFunctionalSpecification {
.connectTimeout(500, TimeUnit.MILLISECONDS)
.build(),
getSslSettings()), getCredentialWithCache(), null, null, [], LoggerSettings.builder().build(), null,
- getServerApi()),
+ getServerApi(), null),
getClusterConnectionMode(), getServerApi(), SameObjectProvider.initialized(sdam))
serverMonitor.start()
serverMonitor
diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java
index a94d3319ab1..66fa750803f 100644
--- a/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java
+++ b/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java
@@ -70,7 +70,7 @@ private void setUpCluster(final ServerAddress serverAddress) {
streamFactory, streamFactory, getCredential(),
LoggerSettings.builder().build(), null, null, null,
- Collections.emptyList(), getServerApi()));
+ Collections.emptyList(), getServerApi(), null));
}
@After
diff --git a/driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy b/driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy
index be9702b6588..c4057576995 100644
--- a/driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy
@@ -703,4 +703,15 @@ class ConnectionStringSpecification extends Specification {
expect:
uri.credential == createCredential('bob', 'otherDB', 'pwd'.toCharArray())
}
+
+ def 'should use DnsClient to resolve TXT record'() {
+ given:
+ def dnsClient = { def name, def type -> ['replicaSet=java'] }
+
+ when:
+ def connectionString = new ConnectionString('mongodb+srv://free-java.mongodb-dev.net', dnsClient);
+
+ then:
+ connectionString.getRequiredReplicaSetName() == 'java'
+ }
}
diff --git a/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy b/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
index e0b7d6f5c66..db9938fcecf 100644
--- a/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
@@ -24,6 +24,8 @@ import com.mongodb.connection.SocketSettings
import com.mongodb.connection.SslSettings
import com.mongodb.connection.netty.NettyStreamFactoryFactory
import com.mongodb.event.CommandListener
+import com.mongodb.spi.dns.DnsClient
+import com.mongodb.spi.dns.InetAddressResolver
import org.bson.UuidRepresentation
import org.bson.codecs.configuration.CodecRegistry
import spock.lang.Specification
@@ -58,6 +60,8 @@ class MongoClientSettingsSpecification extends Specification {
settings.credential == null
settings.uuidRepresentation == UuidRepresentation.UNSPECIFIED
settings.contextProvider == null
+ settings.dnsClient == null
+ settings.inetAddressResolver == null
}
@SuppressWarnings('UnnecessaryObjectReferences')
@@ -119,6 +123,8 @@ class MongoClientSettingsSpecification extends Specification {
def commandListener = Stub(CommandListener)
def clusterSettings = ClusterSettings.builder().hosts([new ServerAddress('localhost')]).requiredReplicaSetName('test').build()
def contextProvider = Stub(ContextProvider)
+ def dnsClient = Stub(DnsClient)
+ def inetAddressResolver = Stub(InetAddressResolver)
when:
def settings = MongoClientSettings.builder()
@@ -141,6 +147,8 @@ class MongoClientSettingsSpecification extends Specification {
.compressorList([MongoCompressor.createZlibCompressor()])
.uuidRepresentation(UuidRepresentation.STANDARD)
.contextProvider(contextProvider)
+ .dnsClient(dnsClient)
+ .inetAddressResolver(inetAddressResolver)
.build()
then:
@@ -160,6 +168,8 @@ class MongoClientSettingsSpecification extends Specification {
settings.getCompressorList() == [MongoCompressor.createZlibCompressor()]
settings.getUuidRepresentation() == UuidRepresentation.STANDARD
settings.getContextProvider() == contextProvider
+ settings.getDnsClient() == dnsClient
+ settings.getInetAddressResolver() == inetAddressResolver
}
def 'should be easy to create new settings from existing'() {
@@ -175,6 +185,8 @@ class MongoClientSettingsSpecification extends Specification {
def commandListener = Stub(CommandListener)
def compressorList = [MongoCompressor.createZlibCompressor()]
def contextProvider = Stub(ContextProvider)
+ def dnsClient = Stub(DnsClient)
+ def inetAddressResolver = Stub(InetAddressResolver)
settings = MongoClientSettings.builder()
.heartbeatConnectTimeoutMS(24000)
@@ -197,6 +209,8 @@ class MongoClientSettingsSpecification extends Specification {
.codecRegistry(codecRegistry)
.compressorList(compressorList)
.contextProvider(contextProvider)
+ .dnsClient(dnsClient)
+ .inetAddressResolver(inetAddressResolver)
.build()
then:
@@ -464,8 +478,8 @@ class MongoClientSettingsSpecification extends Specification {
// A regression test so that if anymore fields are added then the builder(final MongoClientSettings settings) should be updated
def actual = MongoClientSettings.Builder.declaredFields.grep { !it.synthetic } *.name.sort()
def expected = ['applicationName', 'autoEncryptionSettings', 'clusterSettingsBuilder', 'codecRegistry', 'commandListeners',
- 'compressorList', 'connectionPoolSettingsBuilder', 'contextProvider', 'credential',
- 'heartbeatConnectTimeoutMS', 'heartbeatSocketTimeoutMS', 'loggerSettingsBuilder',
+ 'compressorList', 'connectionPoolSettingsBuilder', 'contextProvider', 'credential', 'dnsClient',
+ 'heartbeatConnectTimeoutMS', 'heartbeatSocketTimeoutMS', 'inetAddressResolver', 'loggerSettingsBuilder',
'readConcern', 'readPreference', 'retryReads',
'retryWrites', 'serverApi', 'serverSettingsBuilder', 'socketSettingsBuilder', 'sslSettingsBuilder',
'streamFactoryFactory', 'uuidRepresentation', 'writeConcern']
@@ -481,9 +495,9 @@ class MongoClientSettingsSpecification extends Specification {
def expected = ['addCommandListener', 'applicationName', 'applyConnectionString', 'applyToClusterSettings',
'applyToConnectionPoolSettings', 'applyToLoggerSettings', 'applyToServerSettings', 'applyToSocketSettings',
'applyToSslSettings', 'autoEncryptionSettings', 'build', 'codecRegistry', 'commandListenerList',
- 'compressorList', 'contextProvider', 'credential', 'heartbeatConnectTimeoutMS', 'heartbeatSocketTimeoutMS',
- 'readConcern', 'readPreference', 'retryReads', 'retryWrites', 'serverApi', 'streamFactoryFactory',
- 'uuidRepresentation', 'writeConcern']
+ 'compressorList', 'contextProvider', 'credential', 'dnsClient', 'heartbeatConnectTimeoutMS',
+ 'heartbeatSocketTimeoutMS', 'inetAddressResolver', 'readConcern', 'readPreference', 'retryReads', 'retryWrites',
+ 'serverApi', 'streamFactoryFactory', 'uuidRepresentation', 'writeConcern']
then:
actual == expected
}
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java
index ed14716c538..bd22307e75f 100644
--- a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java
@@ -186,7 +186,8 @@ public void setUp() {
Collections.emptyList(),
LoggerSettings.builder().build(),
new TestCommandListener(),
- ClusterFixture.getServerApi()),
+ ClusterFixture.getServerApi(),
+ null),
settings, internalSettings, sdamProvider));
sdamProvider.initialize(new DefaultSdamServerDescriptionManager(mockedCluster(), serverId, mock(ServerListener.class),
mock(ServerMonitor.class), pool, connectionMode));
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionSpecification.groovy
index 28071239a91..1ea53ef4543 100644
--- a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionSpecification.groovy
@@ -107,7 +107,7 @@ class InternalStreamConnectionSpecification extends Specification {
def getConnection() {
new InternalStreamConnection(SINGLE, SERVER_ID, new TestConnectionGenerationSupplier(), streamFactory, [], commandListener,
- initializer)
+ initializer, null)
}
def getOpenedConnection() {
@@ -172,7 +172,7 @@ class InternalStreamConnectionSpecification extends Specification {
startHandshake(_) >> { throw new MongoInternalException('Something went wrong') }
}
def connection = new InternalStreamConnection(SINGLE, SERVER_ID, new TestConnectionGenerationSupplier(), streamFactory, [], null,
- failedInitializer)
+ failedInitializer, null)
when:
connection.open()
@@ -189,7 +189,7 @@ class InternalStreamConnectionSpecification extends Specification {
startHandshakeAsync(_, _) >> { it[1].onResult(null, new MongoInternalException('Something went wrong')) }
}
def connection = new InternalStreamConnection(SINGLE, SERVER_ID, new TestConnectionGenerationSupplier(), streamFactory, [], null,
- failedInitializer)
+ failedInitializer, null)
when:
def futureResultCallback = new FutureResultCallback()
diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClients.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClients.java
index 744a69ec266..569b93083e6 100644
--- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClients.java
+++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClients.java
@@ -149,7 +149,7 @@ private static Cluster createCluster(final MongoClientSettings settings,
InternalConnectionPoolSettings.builder().prestartAsyncWorkManager(true).build(),
streamFactory, heartbeatStreamFactory, settings.getCredential(), settings.getLoggerSettings(),
getCommandListener(settings.getCommandListeners()), settings.getApplicationName(), mongoDriverInformation,
- settings.getCompressorList(), settings.getServerApi());
+ settings.getCompressorList(), settings.getServerApi(), settings.getDnsClient(), settings.getInetAddressResolver());
}
private static MongoDriverInformation wrapMongoDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) {
diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/DnsConfigurationTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/DnsConfigurationTest.java
new file mode 100644
index 00000000000..6cb1c951bae
--- /dev/null
+++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/DnsConfigurationTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.reactivestreams.client;
+
+import com.mongodb.MongoClientSettings;
+import com.mongodb.client.AbstractDnsConfigurationTest;
+import com.mongodb.client.MongoClient;
+import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient;
+
+public class DnsConfigurationTest extends AbstractDnsConfigurationTest {
+ @Override
+ protected MongoClient createMongoClient(final MongoClientSettings settings) {
+ return new SyncMongoClient(MongoClients.create(settings));
+ }
+}
diff --git a/driver-scala/src/test/scala/org/mongodb/scala/ApiAliasAndCompanionSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/ApiAliasAndCompanionSpec.scala
index 4f461599789..8f1fb8de210 100644
--- a/driver-scala/src/test/scala/org/mongodb/scala/ApiAliasAndCompanionSpec.scala
+++ b/driver-scala/src/test/scala/org/mongodb/scala/ApiAliasAndCompanionSpec.scala
@@ -55,6 +55,8 @@ class ApiAliasAndCompanionSpec extends BaseSpec {
"DocumentToDBRefTransformer",
"Function",
"FutureResultCallback",
+ "InetAddressResolver",
+ "InetAddressResolverProvider",
"Jep395RecordCodecProvider",
"KerberosSubjectProvider",
"KotlinCodecProvider",
diff --git a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java
index 52dc02a41ee..ff71b42633c 100644
--- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java
+++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java
@@ -229,7 +229,8 @@ private static Cluster createCluster(final MongoClientSettings settings,
settings.getConnectionPoolSettings(), InternalConnectionPoolSettings.builder().build(),
getStreamFactory(settings, false), getStreamFactory(settings, true),
settings.getCredential(), settings.getLoggerSettings(), getCommandListener(settings.getCommandListeners()),
- settings.getApplicationName(), mongoDriverInformation, settings.getCompressorList(), settings.getServerApi());
+ settings.getApplicationName(), mongoDriverInformation, settings.getCompressorList(), settings.getServerApi(),
+ settings.getDnsClient(), settings.getInetAddressResolver());
}
private static StreamFactory getStreamFactory(final MongoClientSettings settings, final boolean isHeartbeat) {
diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractDnsConfigurationTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractDnsConfigurationTest.java
new file mode 100644
index 00000000000..26a90337998
--- /dev/null
+++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractDnsConfigurationTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.client;
+
+import com.mongodb.ConnectionString;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.MongoException;
+import com.mongodb.ServerAddress;
+import com.mongodb.connection.ServerDescription;
+import com.mongodb.event.ClusterDescriptionChangedEvent;
+import com.mongodb.event.ClusterListener;
+import com.mongodb.spi.dns.DnsClient;
+import com.mongodb.spi.dns.DnsException;
+import com.mongodb.spi.dns.InetAddressResolver;
+import org.junit.jupiter.api.Test;
+
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public abstract class AbstractDnsConfigurationTest {
+
+ protected abstract MongoClient createMongoClient(MongoClientSettings settings);
+
+ @Test
+ public void testInetAddressResolverConfiguration() throws InterruptedException, ExecutionException, TimeoutException {
+ UnknownHostException exception = new UnknownHostException();
+ InetAddressResolver resolver = host -> {
+ throw exception;
+ };
+
+ CompletableFuture exceptionReceived = new CompletableFuture<>();
+ MongoClientSettings settings = MongoClientSettings.builder()
+ .applyToClusterSettings(builder ->
+ builder.hosts(Collections.singletonList(new ServerAddress("some.host")))
+ .addClusterListener(new ClusterListener() {
+ @Override
+ public void clusterDescriptionChanged(final ClusterDescriptionChangedEvent event) {
+ ServerDescription serverDescription = event.getNewDescription().getServerDescriptions().get(0);
+ if (serverDescription.getException() != null) {
+ exceptionReceived.complete(exception);
+ }
+ }
+ }))
+ .inetAddressResolver(resolver)
+ .build();
+
+ try (MongoClient ignored = createMongoClient(settings)) {
+ assertEquals(exception, exceptionReceived.get(10, SECONDS));
+ }
+ }
+
+ @Test
+ public void testDnsClientConfiguration() throws InterruptedException, ExecutionException, TimeoutException {
+ DnsException exception = new DnsException("", new Exception());
+ DnsClient dnsClient = (name, type) -> {
+ throw exception;
+ };
+
+ CompletableFuture exceptionReceived = new CompletableFuture<>();
+ MongoClientSettings settings = MongoClientSettings.builder()
+ .applyConnectionString(new ConnectionString("mongodb+srv://free-java.mongodb-dev.net"))
+ .applyToClusterSettings(builder ->
+ builder.addClusterListener(new ClusterListener() {
+ @Override
+ public void clusterDescriptionChanged(final ClusterDescriptionChangedEvent event) {
+ MongoException srvResolutionException = event.getNewDescription().getSrvResolutionException();
+ if (srvResolutionException != null) {
+ exceptionReceived.complete(srvResolutionException.getCause());
+ }
+ }
+ }))
+ .dnsClient(dnsClient)
+ .build();
+
+ try (MongoClient ignored = createMongoClient(settings)) {
+ assertEquals(exception, exceptionReceived.get(1, SECONDS));
+ }
+ }
+}
diff --git a/driver-sync/src/test/functional/com/mongodb/client/DnsConfigurationTest.java b/driver-sync/src/test/functional/com/mongodb/client/DnsConfigurationTest.java
new file mode 100644
index 00000000000..36593ba0239
--- /dev/null
+++ b/driver-sync/src/test/functional/com/mongodb/client/DnsConfigurationTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.client;
+
+import com.mongodb.MongoClientSettings;
+
+public class DnsConfigurationTest extends AbstractDnsConfigurationTest {
+ @Override
+ protected MongoClient createMongoClient(final MongoClientSettings settings) {
+ return MongoClients.create(settings);
+ }
+}