Skip to content

Commit 7de6b2d

Browse files
committed
Move SRV lookup from ConnectionString to MultiServerCluster
JAVA-3084
1 parent 1324d34 commit 7de6b2d

File tree

9 files changed

+283
-45
lines changed

9 files changed

+283
-45
lines changed

driver-async/src/test/functional/com/mongodb/async/client/InitialDnsSeedlistDiscoveryTest.java

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import com.mongodb.Block;
2020
import com.mongodb.ConnectionString;
2121
import com.mongodb.MongoClientException;
22+
import com.mongodb.MongoClientSettings;
23+
import com.mongodb.MongoConfigurationException;
2224
import com.mongodb.ServerAddress;
2325
import com.mongodb.async.SingleResultCallback;
2426
import com.mongodb.connection.ClusterSettings;
2527
import com.mongodb.connection.ServerDescription;
2628
import com.mongodb.connection.SslSettings;
29+
import com.mongodb.connection.netty.NettyStreamFactoryFactory;
2730
import com.mongodb.event.ClusterClosedEvent;
2831
import com.mongodb.event.ClusterDescriptionChangedEvent;
2932
import com.mongodb.event.ClusterListener;
@@ -47,6 +50,7 @@
4750
import java.util.Map;
4851
import java.util.concurrent.CountDownLatch;
4952
import java.util.concurrent.TimeUnit;
53+
import java.util.concurrent.atomic.AtomicReference;
5054

5155
import static com.mongodb.ClusterFixture.getSslSettings;
5256
import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet;
@@ -77,23 +81,65 @@ public InitialDnsSeedlistDiscoveryTest(final String filename, final String uri,
7781
}
7882

7983
@Test
80-
public void shouldResolve() {
84+
public void shouldResolve() throws InterruptedException {
8185

8286
if (isError) {
87+
MongoClient client = null;
8388
try {
84-
new ConnectionString(this.uri);
85-
fail();
89+
final AtomicReference<MongoConfigurationException> exceptionReference = new AtomicReference<MongoConfigurationException>();
90+
final CountDownLatch latch = new CountDownLatch(1);
91+
92+
ConnectionString connectionString = new ConnectionString(uri);
93+
final SslSettings sslSettings = getSslSettings(connectionString);
94+
com.mongodb.MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(connectionString)
95+
.applyToSslSettings(new Block<SslSettings.Builder>() {
96+
@Override
97+
public void apply(final SslSettings.Builder builder) {
98+
builder.applySettings(sslSettings);
99+
builder.invalidHostNameAllowed(true);
100+
}
101+
})
102+
.streamFactoryFactory(NettyStreamFactoryFactory.builder().build())
103+
.applyToClusterSettings(new Block<ClusterSettings.Builder>() {
104+
@Override
105+
public void apply(final ClusterSettings.Builder builder) {
106+
builder.addClusterListener(new ClusterListener() {
107+
@Override
108+
public void clusterOpening(final ClusterOpeningEvent event) {
109+
}
110+
111+
@Override
112+
public void clusterClosed(final ClusterClosedEvent event) {
113+
}
114+
115+
@Override
116+
public void clusterDescriptionChanged(final ClusterDescriptionChangedEvent event) {
117+
if (event.getNewDescription().getSrvResolutionException() != null) {
118+
exceptionReference.set(event.getNewDescription().getSrvResolutionException());
119+
latch.countDown();
120+
}
121+
}
122+
});
123+
}
124+
})
125+
.build();
126+
client = MongoClients.create(settings);
127+
if (!latch.await(5, TimeUnit.SECONDS)) {
128+
fail("");
129+
}
130+
throw exceptionReference.get();
86131
} catch (IllegalArgumentException e) {
87132
// all good
88133
} catch (MongoClientException e) {
89134
// all good
135+
} finally {
136+
if (client != null) {
137+
client.close();
138+
}
90139
}
91140
} else {
92141
ConnectionString connectionString = new ConnectionString(this.uri);
93142

94-
assertEquals(seeds.size(), connectionString.getHosts().size());
95-
assertTrue(connectionString.getHosts().containsAll(seeds));
96-
97143
for (Map.Entry<String, BsonValue> entry : options.entrySet()) {
98144
if (entry.getKey().equals("replicaSet")) {
99145
assertEquals(entry.getValue().asString().getValue(), connectionString.getRequiredReplicaSetName());
@@ -124,6 +170,7 @@ public void shouldDiscover() throws InterruptedException {
124170
assumeTrue("SSL settings don't match", getSslSettings().isEnabled() == sslSettings.isEnabled());
125171

126172
com.mongodb.MongoClientSettings settings = com.mongodb.MongoClientSettings.builder()
173+
.streamFactoryFactory(NettyStreamFactoryFactory.builder().build()) // TODO: why wasn't this necessary before? Is it now?
127174
.applyToClusterSettings(new Block<ClusterSettings.Builder>() {
128175
@Override
129176
public void apply(final ClusterSettings.Builder builder) {

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

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import java.util.concurrent.TimeUnit;
3434

3535
import static com.mongodb.internal.dns.DnsResolver.resolveAdditionalQueryParametersFromTxtRecords;
36-
import static com.mongodb.internal.dns.DnsResolver.resolveHostFromSrvRecords;
3736
import static java.lang.String.format;
3837
import static java.util.Arrays.asList;
3938
import static java.util.Collections.singletonList;
@@ -234,6 +233,7 @@ public class ConnectionString {
234233
private static final Logger LOGGER = Loggers.getLogger("uri");
235234

236235
private final MongoCredential credential;
236+
private final boolean isSrvProtocol;
237237
private final List<String> hosts;
238238
private final String database;
239239
private final String collection;
@@ -271,8 +271,8 @@ public class ConnectionString {
271271
public ConnectionString(final String connectionString) {
272272
this.connectionString = connectionString;
273273
boolean isMongoDBProtocol = connectionString.startsWith(MONGODB_PREFIX);
274-
boolean isSRVProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
275-
if (!isMongoDBProtocol && !isSRVProtocol) {
274+
isSrvProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
275+
if (!isMongoDBProtocol && !isSrvProtocol) {
276276
throw new IllegalArgumentException(format("The connection string is invalid. "
277277
+ "Connection strings must start with either '%s' or '%s", MONGODB_PREFIX, MONGODB_SRV_PREFIX));
278278
}
@@ -324,8 +324,16 @@ public ConnectionString(final String connectionString) {
324324
}
325325

326326
// Validate the hosts
327-
List<String> unresolvedHosts = unmodifiableList(parseHosts(asList(hostIdentifier.split(",")), isSRVProtocol));
328-
this.hosts = isSRVProtocol ? resolveHostFromSrvRecords(unresolvedHosts.get(0)) : unresolvedHosts;
327+
List<String> unresolvedHosts = unmodifiableList(parseHosts(asList(hostIdentifier.split(","))));
328+
if (isSrvProtocol) {
329+
if (unresolvedHosts.size() > 1) {
330+
throw new IllegalArgumentException("Only one host allowed when using mongodb+srv protocol");
331+
}
332+
if (unresolvedHosts.get(0).contains(":")) {
333+
throw new IllegalArgumentException("Host for when using mongodb+srv protocol can not contain a port");
334+
}
335+
}
336+
this.hosts = unresolvedHosts;
329337

330338
// Process the authDB section
331339
String nsPart;
@@ -353,7 +361,7 @@ public ConnectionString(final String connectionString) {
353361
collection = null;
354362
}
355363

356-
String txtRecordsQueryParameters = isSRVProtocol ? resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
364+
String txtRecordsQueryParameters = isSrvProtocol ? resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
357365
String connectionStringQueryParamenters = unprocessedConnectionString;
358366

359367
Map<String, List<String>> connectionStringOptionsMap = parseOptions(connectionStringQueryParamenters);
@@ -363,7 +371,7 @@ public ConnectionString(final String connectionString) {
363371
+ "'%s' contains the keys %s", ALLOWED_OPTIONS_IN_TXT_RECORD, unresolvedHosts.get(0), txtRecordsOptionsMap.keySet()));
364372
}
365373
Map<String, List<String>> combinedOptionsMaps = combineOptionsMaps(txtRecordsOptionsMap, connectionStringOptionsMap);
366-
if (isSRVProtocol && !combinedOptionsMaps.containsKey("ssl")) {
374+
if (isSrvProtocol && !combinedOptionsMaps.containsKey("ssl")) {
367375
combinedOptionsMaps.put("ssl", singletonList("true"));
368376
}
369377
translateOptions(combinedOptionsMaps);
@@ -867,7 +875,7 @@ private int parseInteger(final String input, final String key) {
867875
}
868876
}
869877

870-
private List<String> parseHosts(final List<String> rawHosts, final boolean isSRVProtocol) {
878+
private List<String> parseHosts(final List<String> rawHosts) {
871879
if (rawHosts.size() == 0){
872880
throw new IllegalArgumentException("The connection string must contain at least one host");
873881
}
@@ -893,20 +901,11 @@ private List<String> parseHosts(final List<String> rawHosts, final boolean isSRV
893901
+ "Reserved characters such as ':' must be escaped according RFC 2396. "
894902
+ "Any IPv6 address literal must be enclosed in '[' and ']' according to RFC 2732.", host));
895903
} else if (colonCount == 1) {
896-
if (isSRVProtocol) {
897-
throw new IllegalArgumentException("A connection string using the mongodb+srv protocol can not"
898-
+ "contain a host name that specifies a port");
899-
}
900-
901904
validatePort(host, host.substring(host.indexOf(":") + 1));
902905
}
903906
}
904907
hosts.add(host);
905908
}
906-
if (isSRVProtocol && hosts.size() > 1) {
907-
throw new IllegalArgumentException("The mongodb+srv protocol requires a single host name but this connection string has more "
908-
+ "than one: " + connectionString);
909-
}
910909
Collections.sort(hosts);
911910
return hosts;
912911
}
@@ -970,6 +969,15 @@ public char[] getPassword() {
970969
return credential != null ? credential.getPassword() : null;
971970
}
972971

972+
/**
973+
* Returns true if the connection string requires SRV protocol to resolve the host lists from the configured host.
974+
*
975+
* @return true if SRV protocol is required to resolve hosts.
976+
*/
977+
public boolean isSrvProtocol() {
978+
return isSrvProtocol;
979+
}
980+
973981
/**
974982
* Gets the list of hosts
975983
*

driver-core/src/main/com/mongodb/connection/ClusterDescription.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.mongodb.connection;
1818

19+
import com.mongodb.MongoConfigurationException;
1920
import com.mongodb.ReadPreference;
2021
import com.mongodb.ServerAddress;
2122
import com.mongodb.TagSet;
@@ -45,6 +46,7 @@ public class ClusterDescription {
4546
private final List<ServerDescription> serverDescriptions;
4647
private final ClusterSettings clusterSettings;
4748
private final ServerSettings serverSettings;
49+
private final MongoConfigurationException srvResolutionException;
4850

4951
/**
5052
* Creates a new ClusterDescription.
@@ -72,9 +74,29 @@ public ClusterDescription(final ClusterConnectionMode connectionMode, final Clus
7274
final List<ServerDescription> serverDescriptions,
7375
final ClusterSettings clusterSettings,
7476
final ServerSettings serverSettings) {
77+
this(connectionMode, type, null, serverDescriptions, clusterSettings, serverSettings);
78+
}
79+
80+
/**
81+
* Creates a new ClusterDescription.
82+
*
83+
* @param connectionMode whether to connect directly to a single server or to multiple servers
84+
* @param type what sort of cluster this is
85+
* @param srvResolutionException an exception resolving the SRV record
86+
* @param serverDescriptions the descriptions of all the servers currently in this cluster
87+
* @param clusterSettings the cluster settings
88+
* @param serverSettings the server settings
89+
* @since 3.10
90+
*/
91+
public ClusterDescription(final ClusterConnectionMode connectionMode, final ClusterType type,
92+
final MongoConfigurationException srvResolutionException,
93+
final List<ServerDescription> serverDescriptions,
94+
final ClusterSettings clusterSettings,
95+
final ServerSettings serverSettings) {
7596
notNull("all", serverDescriptions);
7697
this.connectionMode = notNull("connectionMode", connectionMode);
7798
this.type = notNull("type", type);
99+
this.srvResolutionException = srvResolutionException;
78100
this.serverDescriptions = new ArrayList<ServerDescription>(serverDescriptions);
79101
this.clusterSettings = clusterSettings;
80102
this.serverSettings = serverSettings;
@@ -186,6 +208,16 @@ public ClusterType getType() {
186208
return type;
187209
}
188210

211+
/**
212+
* Gets any exception encountered while resolving the SRV record for the initial host.
213+
*
214+
* @return any exception encountered while resolving the SRV record for the initial host, or null if none
215+
* @since 3.10
216+
*/
217+
public MongoConfigurationException getSrvResolutionException() {
218+
return srvResolutionException;
219+
}
220+
189221
/**
190222
* Returns an unmodifiable list of the server descriptions in this cluster description.
191223
*
@@ -384,13 +416,27 @@ public boolean equals(final Object o) {
384416
return false;
385417
}
386418

419+
// Compare class equality and message as exceptions rarely override equals
420+
Class<?> thisExceptionClass = srvResolutionException != null ? srvResolutionException.getClass() : null;
421+
Class<?> thatExceptionClass = that.srvResolutionException != null ? that.srvResolutionException.getClass() : null;
422+
if (thisExceptionClass != null ? !thisExceptionClass.equals(thatExceptionClass) : thatExceptionClass != null) {
423+
return false;
424+
}
425+
426+
String thisExceptionMessage = srvResolutionException != null ? srvResolutionException.getMessage() : null;
427+
String thatExceptionMessage = that.srvResolutionException != null ? that.srvResolutionException.getMessage() : null;
428+
if (thisExceptionMessage != null ? !thisExceptionMessage.equals(thatExceptionMessage) : thatExceptionMessage != null) {
429+
return false;
430+
}
431+
387432
return true;
388433
}
389434

390435
@Override
391436
public int hashCode() {
392437
int result = connectionMode.hashCode();
393438
result = 31 * result + type.hashCode();
439+
result = 31 * result + (srvResolutionException == null ? 0 : srvResolutionException.hashCode());
394440
result = 31 * result + serverDescriptions.hashCode();
395441
return result;
396442
}
@@ -399,6 +445,7 @@ public int hashCode() {
399445
public String toString() {
400446
return "ClusterDescription{"
401447
+ "type=" + getType()
448+
+ (srvResolutionException == null ? "" : ", srvResolutionException=" + srvResolutionException)
402449
+ ", connectionMode=" + connectionMode
403450
+ ", serverDescriptions=" + serverDescriptions
404451
+ '}';
@@ -416,7 +463,11 @@ public String getShortDescription() {
416463
serverDescriptions.append(delimiter).append(cur.getShortDescription());
417464
delimiter = ", ";
418465
}
419-
return format("{type=%s, servers=[%s]", type, serverDescriptions);
466+
if (srvResolutionException == null) {
467+
return format("{type=%s, servers=[%s]", type, serverDescriptions);
468+
} else {
469+
return format("{type=%s, srvResolutionException=%s, servers=[%s]", type, srvResolutionException, serverDescriptions);
470+
}
420471
}
421472

422473
private interface Predicate {

0 commit comments

Comments
 (0)