Skip to content

Commit a43c2cf

Browse files
authored
Support per-MongoClient DNS lookup configuration (#1104)
* Add InetAddressResolver interface * Add InetAddressResolverProvider interface * Add configuration of InetAddressResolver and DnsClient in MongoClientSettings and ConnectionString * Use configured InetAddressResolver and DnsClient to resolve all SRV, TXT, A, and AAAA records JAVA-4911
1 parent 414e6be commit a43c2cf

30 files changed

+594
-70
lines changed

driver-core/src/main/com/mongodb/ConnectionString.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.mongodb.internal.diagnostics.logging.Loggers;
2424
import com.mongodb.internal.dns.DefaultDnsResolver;
2525
import com.mongodb.lang.Nullable;
26+
import com.mongodb.spi.dns.DnsClient;
2627
import org.bson.UuidRepresentation;
2728

2829
import java.io.UnsupportedEncodingException;
@@ -296,6 +297,21 @@ public class ConnectionString {
296297
* @since 3.0
297298
*/
298299
public ConnectionString(final String connectionString) {
300+
this(connectionString, null);
301+
}
302+
303+
/**
304+
* Creates a ConnectionString from the given string with the given {@link DnsClient}.
305+
*
306+
* <p>If setting {@link MongoClientSettings#getDnsClient()} explicitly, care should be taken to call this constructor with the same
307+
* {@link DnsClient}.
308+
*
309+
* @param connectionString the connection string
310+
* @param dnsClient the DNS client with which to resolve TXT record for the mongodb+srv protocol
311+
* @since 4.10
312+
* @see MongoClientSettings#getDnsClient()
313+
*/
314+
public ConnectionString(final String connectionString, @Nullable final DnsClient dnsClient) {
299315
this.connectionString = connectionString;
300316
boolean isMongoDBProtocol = connectionString.startsWith(MONGODB_PREFIX);
301317
isSrvProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
@@ -394,7 +410,7 @@ public ConnectionString(final String connectionString) {
394410
}
395411

396412
String txtRecordsQueryParameters = isSrvProtocol
397-
? new DefaultDnsResolver().resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
413+
? new DefaultDnsResolver(dnsClient).resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
398414
String connectionStringQueryParameters = unprocessedConnectionString;
399415

400416
Map<String, List<String>> connectionStringOptionsMap = parseOptions(connectionStringQueryParameters);

driver-core/src/main/com/mongodb/MongoClientSettings.java

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import com.mongodb.connection.StreamFactoryFactory;
3030
import com.mongodb.event.CommandListener;
3131
import com.mongodb.lang.Nullable;
32+
import com.mongodb.spi.dns.DnsClient;
33+
import com.mongodb.spi.dns.InetAddressResolver;
3234
import org.bson.UuidRepresentation;
3335
import org.bson.codecs.BsonCodecProvider;
3436
import org.bson.codecs.BsonValueCodecProvider;
@@ -107,6 +109,8 @@ public final class MongoClientSettings {
107109
private final boolean heartbeatConnectTimeoutSetExplicitly;
108110

109111
private final ContextProvider contextProvider;
112+
private final DnsClient dnsClient;
113+
private final InetAddressResolver inetAddressResolver;
110114

111115
/**
112116
* Gets the default codec registry. It includes the following providers:
@@ -160,6 +164,39 @@ public static Builder builder(final MongoClientSettings settings) {
160164
return new Builder(settings);
161165
}
162166

167+
/**
168+
* Gets the {@link DnsClient} to use for resolving DNS queries.
169+
*
170+
* <p>If set, it will be used to resolve SRV and TXT records for mongodb+srv connections. Otherwise,
171+
* implementations of {@link com.mongodb.spi.dns.DnsClientProvider} will be discovered via {@link java.util.ServiceLoader}.
172+
* If no implementations are discovered, then {@code com.sun.jndi.dns.DnsContextFactory} will be used to resolve these records.
173+
*
174+
* <p>If applying a connection string to these settings, care must be taken to also pass the same {@link DnsClient} as an argument to
175+
* the {@link ConnectionString} constructor.
176+
*
177+
* @return the DNS client
178+
* @since 4.10
179+
* @see ConnectionString#ConnectionString(String, DnsClient)
180+
*/
181+
@Nullable
182+
public DnsClient getDnsClient() {
183+
return dnsClient;
184+
}
185+
186+
/**
187+
* Gets the {@link InetAddressResolver} to use for looking up the {@link java.net.InetAddress} instances for each host.
188+
*
189+
* <p>If set, it will be used to look up the {@link java.net.InetAddress} for each host, via
190+
* {@link InetAddressResolver#lookupByName(String)}. Otherwise, {@link java.net.InetAddress#getAllByName(String)} will be used.
191+
*
192+
* @return the {@link java.net.InetAddress} resolver
193+
* @since 4.10
194+
*/
195+
@Nullable
196+
public InetAddressResolver getInetAddressResolver() {
197+
return inetAddressResolver;
198+
}
199+
163200
/**
164201
* A builder for {@code MongoClientSettings} so that {@code MongoClientSettings} can be immutable, and to support easier construction
165202
* through chaining.
@@ -193,6 +230,8 @@ public static final class Builder {
193230
private int heartbeatSocketTimeoutMS;
194231

195232
private ContextProvider contextProvider;
233+
private DnsClient dnsClient;
234+
private InetAddressResolver inetAddressResolver;
196235

197236
private Builder() {
198237
}
@@ -211,6 +250,8 @@ private Builder(final MongoClientSettings settings) {
211250
credential = settings.getCredential();
212251
uuidRepresentation = settings.getUuidRepresentation();
213252
serverApi = settings.getServerApi();
253+
dnsClient = settings.getDnsClient();
254+
inetAddressResolver = settings.getInetAddressResolver();
214255
streamFactoryFactory = settings.getStreamFactoryFactory();
215256
autoEncryptionSettings = settings.getAutoEncryptionSettings();
216257
contextProvider = settings.getContextProvider();
@@ -220,6 +261,7 @@ private Builder(final MongoClientSettings settings) {
220261
socketSettingsBuilder.applySettings(settings.getSocketSettings());
221262
connectionPoolSettingsBuilder.applySettings(settings.getConnectionPoolSettings());
222263
sslSettingsBuilder.applySettings(settings.getSslSettings());
264+
223265
if (settings.heartbeatConnectTimeoutSetExplicitly) {
224266
heartbeatConnectTimeoutMS = settings.heartbeatSocketSettings.getConnectTimeout(MILLISECONDS);
225267
}
@@ -273,7 +315,7 @@ public Builder applyConnectionString(final ConnectionString connectionString) {
273315
/**
274316
* Applies the {@link LoggerSettings.Builder} block and then sets the loggerSettings.
275317
*
276-
* @param block the block to apply to the LoggerSettins.
318+
* @param block the block to apply to the LoggerSettings.
277319
* @return this
278320
* @see MongoClientSettings#getLoggerSettings()
279321
* @since 4.9
@@ -580,6 +622,45 @@ public Builder contextProvider(@Nullable final ContextProvider contextProvider)
580622
return this;
581623
}
582624

625+
/**
626+
* Sets the {@link DnsClient} to use for resolving DNS queries.
627+
*
628+
* <p> If set, it will be used to resolve SRV and TXT records for mongodb+srv connections. Otherwise,
629+
* implementation of {@link com.mongodb.spi.dns.DnsClientProvider} will be discovered via {@link java.util.ServiceLoader}
630+
* and used to create an instance of {@link DnsClient}. If no implementation is discovered, then
631+
* {@code com.sun.jndi.dns.DnsContextFactory} will be used to resolve these records.
632+
*
633+
* <p>If {@linkplain #applyConnectionString(ConnectionString) applying a connection string to these settings}, care must be
634+
* taken to also pass the same {@link DnsClient} as an argument to the {@link ConnectionString} constructor.
635+
*
636+
* @param dnsClient the DNS client
637+
* @return the DNS client
638+
* @since 4.10
639+
* @see ConnectionString#ConnectionString(String, DnsClient)
640+
*/
641+
public Builder dnsClient(@Nullable final DnsClient dnsClient) {
642+
this.dnsClient = dnsClient;
643+
return this;
644+
}
645+
646+
/**
647+
* Sets the {@link InetAddressResolver} to use for looking up the {@link java.net.InetAddress} instances for each host.
648+
*
649+
* <p>If set, it will be used to look up the {@link java.net.InetAddress} for each host, via
650+
* {@link InetAddressResolver#lookupByName(String)}. Otherwise,
651+
* an implementation of {@link com.mongodb.spi.dns.InetAddressResolverProvider} will be discovered via
652+
* {@link java.util.ServiceLoader} and used to create an instance of {@link InetAddressResolver}. If no implementation is
653+
* discovered, {@link java.net.InetAddress#getAllByName(String)} will be used to lookup the {@link java.net.InetAddress}
654+
* instances for a host.
655+
*
656+
* @param inetAddressResolver the InetAddress provider
657+
* @return the {@link java.net.InetAddress} resolver
658+
* @since 4.10
659+
*/
660+
public Builder inetAddressResolver(@Nullable final InetAddressResolver inetAddressResolver) {
661+
this.inetAddressResolver = inetAddressResolver;
662+
return this;
663+
}
583664

584665
// Package-private to provide interop with MongoClientOptions
585666
Builder heartbeatConnectTimeoutMS(final int heartbeatConnectTimeoutMS) {
@@ -905,6 +986,8 @@ public boolean equals(final Object o) {
905986
&& uuidRepresentation == that.uuidRepresentation
906987
&& Objects.equals(serverApi, that.serverApi)
907988
&& Objects.equals(autoEncryptionSettings, that.autoEncryptionSettings)
989+
&& Objects.equals(dnsClient, that.dnsClient)
990+
&& Objects.equals(inetAddressResolver, that.inetAddressResolver)
908991
&& Objects.equals(contextProvider, that.contextProvider);
909992
}
910993

@@ -913,7 +996,8 @@ public int hashCode() {
913996
return Objects.hash(readPreference, writeConcern, retryWrites, retryReads, readConcern, credential, streamFactoryFactory,
914997
commandListeners, codecRegistry, loggerSettings, clusterSettings, socketSettings, heartbeatSocketSettings,
915998
connectionPoolSettings, serverSettings, sslSettings, applicationName, compressorList, uuidRepresentation, serverApi,
916-
autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, heartbeatConnectTimeoutSetExplicitly, contextProvider);
999+
autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, heartbeatConnectTimeoutSetExplicitly, dnsClient,
1000+
inetAddressResolver, contextProvider);
9171001
}
9181002

9191003
@Override
@@ -940,6 +1024,8 @@ public String toString() {
9401024
+ ", uuidRepresentation=" + uuidRepresentation
9411025
+ ", serverApi=" + serverApi
9421026
+ ", autoEncryptionSettings=" + autoEncryptionSettings
1027+
+ ", dnsClient=" + dnsClient
1028+
+ ", inetAddressResolver=" + inetAddressResolver
9431029
+ ", contextProvider=" + contextProvider
9441030
+ '}';
9451031
}
@@ -964,6 +1050,8 @@ private MongoClientSettings(final Builder builder) {
9641050
compressorList = builder.compressorList;
9651051
uuidRepresentation = builder.uuidRepresentation;
9661052
serverApi = builder.serverApi;
1053+
dnsClient = builder.dnsClient;
1054+
inetAddressResolver = builder.inetAddressResolver;
9671055
autoEncryptionSettings = builder.autoEncryptionSettings;
9681056
heartbeatSocketSettings = SocketSettings.builder()
9691057
.readTimeout(builder.heartbeatSocketTimeoutMS == 0

driver-core/src/main/com/mongodb/MongoSocketException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class MongoSocketException extends MongoException {
3333
* @param msg the message
3434
* @param e the cause
3535
*/
36-
MongoSocketException(final String msg, final ServerAddress serverAddress, final Throwable e) {
36+
public MongoSocketException(final String msg, final ServerAddress serverAddress, final Throwable e) {
3737
super(-2, msg, e);
3838
this.serverAddress = serverAddress;
3939
}

driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import com.mongodb.event.ServerListener;
3333
import com.mongodb.event.ServerMonitorListener;
3434
import com.mongodb.lang.Nullable;
35+
import com.mongodb.spi.dns.DnsClient;
36+
import com.mongodb.spi.dns.InetAddressResolver;
3537

3638
import java.util.List;
3739

@@ -59,7 +61,8 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina
5961
@Nullable final CommandListener commandListener,
6062
@Nullable final String applicationName,
6163
@Nullable final MongoDriverInformation mongoDriverInformation,
62-
final List<MongoCompressor> compressorList, @Nullable final ServerApi serverApi) {
64+
final List<MongoCompressor> compressorList, @Nullable final ServerApi serverApi,
65+
@Nullable final DnsClient dnsClient, @Nullable final InetAddressResolver inetAddressResolver) {
6366

6467
ClusterId clusterId = new ClusterId(applicationName);
6568
ClusterSettings clusterSettings;
@@ -87,20 +90,20 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina
8790
.build();
8891
}
8992

90-
DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = new DefaultDnsSrvRecordMonitorFactory(clusterId, serverSettings);
93+
DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = new DefaultDnsSrvRecordMonitorFactory(clusterId, serverSettings, dnsClient);
9194

9295
if (clusterSettings.getMode() == ClusterConnectionMode.LOAD_BALANCED) {
9396
ClusterableServerFactory serverFactory = new LoadBalancedClusterableServerFactory(serverSettings,
9497
connectionPoolSettings, internalConnectionPoolSettings, streamFactory, credential, loggerSettings, commandListener,
9598
applicationName, mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(),
96-
compressorList, serverApi);
99+
compressorList, serverApi, inetAddressResolver);
97100
return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, dnsSrvRecordMonitorFactory);
98101
} else {
99102
ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(serverSettings,
100103
connectionPoolSettings, internalConnectionPoolSettings,
101104
streamFactory, heartbeatStreamFactory, credential, loggerSettings, commandListener, applicationName,
102105
mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList,
103-
serverApi);
106+
serverApi, inetAddressResolver);
104107

105108
if (clusterSettings.getMode() == ClusterConnectionMode.SINGLE) {
106109
return new SingleServerCluster(clusterId, clusterSettings, serverFactory);

driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.mongodb.event.ServerListener;
3232
import com.mongodb.internal.inject.SameObjectProvider;
3333
import com.mongodb.lang.Nullable;
34+
import com.mongodb.spi.dns.InetAddressResolver;
3435

3536
import java.util.List;
3637

@@ -54,6 +55,8 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory
5455
private final List<MongoCompressor> compressorList;
5556
@Nullable
5657
private final ServerApi serverApi;
58+
@Nullable
59+
private final InetAddressResolver inetAddressResolver;
5760

5861
public DefaultClusterableServerFactory(
5962
final ServerSettings serverSettings, final ConnectionPoolSettings connectionPoolSettings,
@@ -63,7 +66,8 @@ public DefaultClusterableServerFactory(
6366
final LoggerSettings loggerSettings,
6467
@Nullable final CommandListener commandListener,
6568
@Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation,
66-
final List<MongoCompressor> compressorList, @Nullable final ServerApi serverApi) {
69+
final List<MongoCompressor> compressorList, @Nullable final ServerApi serverApi,
70+
@Nullable final InetAddressResolver inetAddressResolver) {
6771
this.serverSettings = serverSettings;
6872
this.connectionPoolSettings = connectionPoolSettings;
6973
this.internalConnectionPoolSettings = internalConnectionPoolSettings;
@@ -76,6 +80,7 @@ public DefaultClusterableServerFactory(
7680
this.mongoDriverInformation = mongoDriverInformation;
7781
this.compressorList = compressorList;
7882
this.serverApi = serverApi;
83+
this.inetAddressResolver = inetAddressResolver;
7984
}
8085

8186
@Override
@@ -86,11 +91,11 @@ public ClusterableServer create(final Cluster cluster, final ServerAddress serve
8691
ServerMonitor serverMonitor = new DefaultServerMonitor(serverId, serverSettings, cluster.getClock(),
8792
// no credentials, compressor list, or command listener for the server monitor factory
8893
new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, applicationName,
89-
mongoDriverInformation, emptyList(), loggerSettings, null, serverApi),
94+
mongoDriverInformation, emptyList(), loggerSettings, null, serverApi, inetAddressResolver),
9095
clusterMode, serverApi, sdamProvider);
9196
ConnectionPool connectionPool = new DefaultConnectionPool(serverId,
9297
new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, applicationName,
93-
mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi),
98+
mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi, inetAddressResolver),
9499
connectionPoolSettings, internalConnectionPoolSettings, sdamProvider);
95100
ServerListener serverListener = singleServerListener(serverSettings);
96101
SdamServerDescriptionManager sdam = new DefaultSdamServerDescriptionManager(cluster, serverId, serverListener, serverMonitor,

driver-core/src/main/com/mongodb/internal/connection/DefaultDnsSrvRecordMonitorFactory.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.mongodb.connection.ClusterId;
2020
import com.mongodb.connection.ServerSettings;
2121
import com.mongodb.internal.dns.DefaultDnsResolver;
22+
import com.mongodb.lang.Nullable;
23+
import com.mongodb.spi.dns.DnsClient;
2224

2325
import static java.util.concurrent.TimeUnit.MILLISECONDS;
2426

@@ -32,15 +34,17 @@ public class DefaultDnsSrvRecordMonitorFactory implements DnsSrvRecordMonitorFac
3234

3335
private final ClusterId clusterId;
3436
private final long noRecordsRescanFrequency;
37+
private final DnsClient dnsClient;
3538

36-
public DefaultDnsSrvRecordMonitorFactory(final ClusterId clusterId, final ServerSettings serverSettings) {
39+
public DefaultDnsSrvRecordMonitorFactory(final ClusterId clusterId, final ServerSettings serverSettings, @Nullable final DnsClient dnsClient) {
3740
this.clusterId = clusterId;
3841
this.noRecordsRescanFrequency = serverSettings.getHeartbeatFrequency(MILLISECONDS);
42+
this.dnsClient = dnsClient;
3943
}
4044

4145
@Override
4246
public DnsSrvRecordMonitor create(final String hostName, final String srvServiceName, final DnsSrvRecordInitializer dnsSrvRecordInitializer) {
4347
return new DefaultDnsSrvRecordMonitor(hostName, srvServiceName, DEFAULT_RESCAN_FREQUENCY_MILLIS, noRecordsRescanFrequency,
44-
dnsSrvRecordInitializer, clusterId, new DefaultDnsResolver());
48+
dnsSrvRecordInitializer, clusterId, new DefaultDnsResolver(dnsClient));
4549
}
4650
}

0 commit comments

Comments
 (0)