Skip to content

Commit 9d348e0

Browse files
committed
JAVA-2226: Add support for streamType=netty to connection string and apply it to async MongoClient construction
1 parent 20c7f9c commit 9d348e0

File tree

6 files changed

+210
-26
lines changed

6 files changed

+210
-26
lines changed

docs/reference/content/driver-async/reference/connecting/connection-settings.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,30 @@ following reasons:
108108
To configure the driver to use Netty, the application must configure the MongoClientSettings appropriately:
109109

110110
```java
111-
MongoClientSettings.builder()
112-
.streamFactoryFactory(new NettyStreamFactoryFactory())
113-
.build();
114-
115-
```
111+
MongoClient client = MongoClients.create(MongoClientSettings.builder()
112+
.clusterSettings(ClusterSettings.builder()
113+
.hosts(Arrays.asList(new ServerAddress()))
114+
.build())
115+
.streamFactoryFactory(NettyStreamFactoryFactory.builder().build())
116+
.build());
117+
118+
```
119+
120+
or via connection string:
121+
122+
```java
123+
MongoClient client = MongoClients.create("mongodb://localhost/?streamType=netty");
124+
```
116125

117126
By default the Netty-based streams will use the [NioEventLoopGroup](http://netty.io/4.0/api/io/netty/channel/nio/NioEventLoopGroup.html)
118127
and Netty's [default `ByteBufAllocator`](http://netty.io/4.0/api/io/netty/buffer/ByteBufAllocator.html#DEFAULT), but these are
119128
configurable via the [`NettyStreamFactoryFactory`]({{< apiref "com/mongodb/connection/netty/NettyStreamFactoryFactory" >}}) constructor.
120129

130+
{{% note %}}
131+
Netty is an optional dependency of the asynchronous driver. If your application requires Netty it must explicitly add a dependency to
132+
Netty artifacts. The driver is currently tested against Netty 4.0.
133+
{{% /note %}}
134+
121135
{{% note %}}
122136
Netty may also be configured by setting the `org.mongodb.async.type` system property to `netty`, but this should be considered as
123137
deprecated as of the 3.1 driver release.

docs/reference/content/driver-async/reference/connecting/ssl.md

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ The async Java driver supports SSL connections to MongoDB servers using the unde
1515
and the stream factory to [`NettyStreamFactoryFactory`]({{< apiref "com/mongodb/connection/netty/NettyStreamFactoryFactory" >}}), as in:
1616

1717
```java
18-
MongoClientSettings.builder()
19-
.sslSettings(SslSettings.builder()
20-
.enabled(true)
21-
.build())
22-
.streamFactoryFactory(new NettyStreamFactoryFactory())
23-
.build()
18+
MongoClient client = MongoClients.create(MongoClientSettings.builder()
19+
.clusterSettings(ClusterSettings.builder()
20+
.hosts(Arrays.asList(new ServerAddress()))
21+
.build())
22+
.sslSettings(SslSettings.builder()
23+
.enabled(true)
24+
.build())
25+
.streamFactoryFactory(NettyStreamFactoryFactory.builder().build())
26+
.build());
27+
28+
```
29+
30+
or via connection string:
31+
32+
```java
33+
MongoClient client = MongoClients.create("mongodb://localhost/?ssl=true&streamType=netty");
2434
```
2535

2636
See [Netty Configuration]({{< relref "driver-async/reference/connecting/connection-settings.md#netty-configuration" >}}) for details on
@@ -34,14 +44,23 @@ constructing a `MongoClient`. However, this host name verification requires a J
3444
to disable host name verification, you must expicitly indicate this in `SslSettings` using the `invalidHostNameAllowed` property:
3545

3646
```java
37-
MongoClientSettings.builder()
38-
.sslSettings(SslSettings.builder()
39-
.enabled(true)
40-
.invalidHostNameAllowed(true)
41-
.build())
42-
.streamFactoryFactory(new NettyStreamFactoryFactory())
43-
.build()
44-
```
47+
MongoClient client = MongoClients.create(MongoClientSettings.builder()
48+
.clusterSettings(ClusterSettings.builder()
49+
.hosts(Arrays.asList(new ServerAddress()))
50+
.build())
51+
.sslSettings(SslSettings.builder()
52+
.enabled(true)
53+
.invalidHostNameAllowed(true)
54+
.build())
55+
.streamFactoryFactory(NettyStreamFactoryFactory.builder().build())
56+
.build());
57+
```
58+
59+
or via connection string:
60+
61+
```java
62+
MongoClient client = MongoClients.create("mongodb://localhost/?ssl=true&sslInvalidHostNameAllowed=true&streamType=netty");
63+
```
4564

4665
### JVM system properties
4766

driver-async/src/main/com/mongodb/async/client/MongoClients.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.mongodb.connection.SocketSettings;
2828
import com.mongodb.connection.SslSettings;
2929
import com.mongodb.connection.StreamFactory;
30+
import com.mongodb.connection.netty.NettyStreamFactoryFactory;
3031
import com.mongodb.event.CommandEventMulticaster;
3132
import com.mongodb.event.CommandListener;
3233
import com.mongodb.management.JMXConnectionPoolListener;
@@ -38,6 +39,7 @@
3839

3940
import java.util.List;
4041

42+
import static java.lang.String.format;
4143
import static java.util.Arrays.asList;
4244
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
4345

@@ -68,23 +70,42 @@ public static MongoClient create(final MongoClientSettings settings) {
6870
}
6971

7072
/**
71-
* Create a new client with the given connection string.
73+
* Create a new client with the given connection string as if by a call to {@link #create(ConnectionString)}.
7274
*
7375
* @param connectionString the connection
7476
* @return the client
77+
* @see #create(ConnectionString)
7578
*/
7679
public static MongoClient create(final String connectionString) {
7780
return create(new ConnectionString(connectionString));
7881
}
7982

8083
/**
8184
* Create a new client with the given connection string.
85+
* <p>
86+
* For each of the settings classed configurable via {@link MongoClientSettings}, the connection string is applied by calling the
87+
* {@code applyConnectionString} method on an instance of setting's builder class, building the setting, and adding it to an instance of
88+
* {@link com.mongodb.async.client.MongoClientSettings.Builder}.
89+
* </p>
90+
* <p>
91+
* The connection string's stream type is then applied by setting the
92+
* {@link com.mongodb.connection.StreamFactoryFactory} to an instance of {@link NettyStreamFactoryFactory},
93+
* </p>
8294
*
8395
* @param connectionString the settings
8496
* @return the client
97+
* @throws IllegalArgumentException if the connection string's stream type is not one of "netty" or "nio2"
98+
*
99+
* @see ConnectionString#getStreamType()
100+
* @see com.mongodb.async.client.MongoClientSettings.Builder
101+
* @see com.mongodb.connection.ClusterSettings.Builder#applyConnectionString(ConnectionString)
102+
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#applyConnectionString(ConnectionString)
103+
* @see com.mongodb.connection.ServerSettings.Builder#applyConnectionString(ConnectionString)
104+
* @see com.mongodb.connection.SslSettings.Builder#applyConnectionString(ConnectionString)
105+
* @see com.mongodb.connection.SocketSettings.Builder#applyConnectionString(ConnectionString)
85106
*/
86107
public static MongoClient create(final ConnectionString connectionString) {
87-
return create(MongoClientSettings.builder()
108+
MongoClientSettings.Builder builder = MongoClientSettings.builder()
88109
.clusterSettings(ClusterSettings.builder()
89110
.applyConnectionString(connectionString)
90111
.build())
@@ -100,8 +121,15 @@ public static MongoClient create(final ConnectionString connectionString) {
100121
.build())
101122
.socketSettings(SocketSettings.builder()
102123
.applyConnectionString(connectionString)
103-
.build())
104-
.build());
124+
.build());
125+
if (connectionString.getStreamType() != null) {
126+
if (connectionString.getStreamType().toLowerCase().equals("netty")) {
127+
builder.streamFactoryFactory(NettyStreamFactoryFactory.builder().build());
128+
} else if (!connectionString.getStreamType().toLowerCase().equals("nio2")) {
129+
throw new IllegalArgumentException(format("Unsupported stream type %s", connectionString.getStreamType()));
130+
}
131+
}
132+
return create(builder.build());
105133
}
106134

107135
/**
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2016 MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package com.mongodb.async.client
19+
20+
import com.mongodb.MongoCredential
21+
import com.mongodb.ServerAddress
22+
import com.mongodb.connection.AsynchronousSocketChannelStreamFactoryFactory
23+
import com.mongodb.connection.netty.NettyStreamFactoryFactory
24+
25+
import static java.util.concurrent.TimeUnit.MILLISECONDS
26+
27+
class MongoClientsSpecification extends FunctionalSpecification {
28+
29+
def 'should apply connection string to cluster settings'() {
30+
when:
31+
def client = MongoClients.create('mongodb://localhost,localhost:27018/')
32+
33+
then:
34+
client.settings.clusterSettings.hosts == [new ServerAddress('localhost'), new ServerAddress('localhost:27018')]
35+
36+
cleanup:
37+
client?.close()
38+
}
39+
40+
def 'should apply connection string to credential list'() {
41+
when:
42+
def client = MongoClients.create('mongodb://u:p@localhost/')
43+
44+
then:
45+
client.settings.credentialList == [MongoCredential.createCredential('u', 'admin', 'p'.toCharArray())]
46+
47+
cleanup:
48+
client?.close()
49+
}
50+
51+
def 'should apply connection string to server settings'() {
52+
when:
53+
def client = MongoClients.create('mongodb://localhost/?heartbeatFrequencyMS=50')
54+
55+
then:
56+
client.settings.serverSettings.getHeartbeatFrequency(MILLISECONDS) == 50
57+
58+
cleanup:
59+
client?.close()
60+
}
61+
62+
def 'should apply connection string to connection pool settings'() {
63+
when:
64+
def client = MongoClients.create('mongodb://localhost/?maxIdleTimeMS=200&maxLifeTimeMS=300')
65+
66+
then:
67+
client.settings.connectionPoolSettings.getMaxConnectionIdleTime(MILLISECONDS) == 200
68+
client.settings.connectionPoolSettings.getMaxConnectionLifeTime(MILLISECONDS) == 300
69+
70+
cleanup:
71+
client?.close()
72+
}
73+
74+
def 'should apply connection string to netty stream type'() {
75+
when:
76+
def client = MongoClients.create('mongodb://localhost/?ssl=true&streamType=Netty')
77+
78+
then:
79+
client.settings.sslSettings.enabled
80+
client.settings.streamFactoryFactory instanceof NettyStreamFactoryFactory
81+
82+
cleanup:
83+
client?.close()
84+
}
85+
86+
def 'should apply connection string to nio2 stream type'() {
87+
when:
88+
def client = MongoClients.create('mongodb://localhost/?streamType=NIO2')
89+
90+
then:
91+
client.settings.streamFactoryFactory instanceof AsynchronousSocketChannelStreamFactoryFactory
92+
93+
cleanup:
94+
client?.close()
95+
}
96+
97+
def 'should apply connection string to socket settings'() {
98+
when:
99+
def client = MongoClients.create('mongodb://localhost/?connectTimeoutMS=300')
100+
101+
then:
102+
client.settings.socketSettings.getConnectTimeout(MILLISECONDS) == 300
103+
104+
cleanup:
105+
client?.close()
106+
}
107+
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
* <p>Connection Configuration:</p>
8686
* <p>Connection Configuration:</p>
8787
* <ul>
88+
* <li>{@code streamType=nio2|netty}: The stream type to use for connections. If unspecified, nio2 will be used.</li>
8889
* <li>{@code ssl=true|false}: Whether to connect using SSL.</li>
8990
* <li>{@code sslInvalidHostNameAllowed=true|false}: Whether to allow invalid host names for SSL connections.</li>
9091
* <li>{@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.</li>
@@ -204,6 +205,7 @@ public class ConnectionString {
204205
private Integer socketTimeout;
205206
private Boolean sslEnabled;
206207
private Boolean sslInvalidHostnameAllowed;
208+
private String streamType;
207209
private String requiredReplicaSetName;
208210
private Integer serverSelectionTimeout;
209211
private Integer localThreshold;
@@ -314,6 +316,7 @@ public ConnectionString(final String connectionString) {
314316
GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
315317
GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
316318
GENERAL_OPTIONS_KEYS.add("ssl");
319+
GENERAL_OPTIONS_KEYS.add("streamtype");
317320
GENERAL_OPTIONS_KEYS.add("sslinvalidhostnameallowed");
318321
GENERAL_OPTIONS_KEYS.add("replicaset");
319322
GENERAL_OPTIONS_KEYS.add("readconcernlevel");
@@ -379,6 +382,8 @@ private void translateOptions(final Map<String, List<String>> optionsMap) {
379382
sslInvalidHostnameAllowed = true;
380383
} else if (key.equals("ssl") && parseBoolean(value, "ssl")) {
381384
sslEnabled = true;
385+
} else if (key.equals("streamtype")) {
386+
streamType = value;
382387
} else if (key.equals("replicaset")) {
383388
requiredReplicaSetName = value;
384389
} else if (key.equals("readconcernlevel")) {
@@ -905,6 +910,15 @@ public Boolean getSslEnabled() {
905910
return sslEnabled;
906911
}
907912

913+
/**
914+
* Gets the stream type value specified in the connection string.
915+
* @return the stream type value
916+
* @since 3.3
917+
*/
918+
public String getStreamType() {
919+
return streamType;
920+
}
921+
908922
/**
909923
* Gets the SSL invalidHostnameAllowed value specified in the connection string.
910924
*

driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,22 @@ class ConnectionStringSpecification extends Specification {
111111
connectionString.getServerSelectionTimeout() == 25000
112112
connectionString.getLocalThreshold() == 30
113113
connectionString.getHeartbeatFrequency() == 20000
114+
connectionString.getStreamType() == 'netty'
114115

115116
where:
116117
connectionString <<
117118
[new ConnectionString('mongodb://localhost/?minPoolSize=5&maxPoolSize=10&waitQueueMultiple=7&waitQueueTimeoutMS=150&'
118119
+ 'maxIdleTimeMS=200&maxLifeTimeMS=300&replicaSet=test&'
119120
+ 'connectTimeoutMS=2500&socketTimeoutMS=5500&'
120-
+ 'safe=false&w=1&wtimeout=2500&fsync=true&readPreference=primary&ssl=true&'
121+
+ 'safe=false&w=1&wtimeout=2500&fsync=true&readPreference=primary&ssl=true&streamType=netty&'
121122
+ 'sslInvalidHostNameAllowed=true&'
122123
+ 'serverSelectionTimeoutMS=25000&'
123124
+ 'localThresholdMS=30&'
124125
+ 'heartbeatFrequencyMS=20000'),
125126
new ConnectionString('mongodb://localhost/?minPoolSize=5;maxPoolSize=10;waitQueueMultiple=7;waitQueueTimeoutMS=150;'
126127
+ 'maxIdleTimeMS=200;maxLifeTimeMS=300;replicaSet=test;'
127128
+ 'connectTimeoutMS=2500;socketTimeoutMS=5500;'
128-
+ 'safe=false;w=1;wtimeout=2500;fsync=true;readPreference=primary;ssl=true;'
129+
+ 'safe=false;w=1;wtimeout=2500;fsync=true;readPreference=primary;ssl=true;streamType=netty;'
129130
+ 'sslInvalidHostNameAllowed=true;'
130131
+ 'serverSelectionTimeoutMS=25000;'
131132
+ 'localThresholdMS=30;'
@@ -134,7 +135,7 @@ class ConnectionStringSpecification extends Specification {
134135
+ 'maxIdleTimeMS=200&maxLifeTimeMS=300&replicaSet=test;'
135136
+ 'connectTimeoutMS=2500;'
136137
+ 'socketTimeoutMS=5500&'
137-
+ 'safe=false&w=1;wtimeout=2500;fsync=true&readPreference=primary;ssl=true&'
138+
+ 'safe=false&w=1;wtimeout=2500;fsync=true&readPreference=primary;ssl=true&streamType=netty;'
138139
+ 'sslInvalidHostNameAllowed=true;'
139140
+ 'serverSelectionTimeoutMS=25000&'
140141
+ 'localThresholdMS=30;'
@@ -188,6 +189,7 @@ class ConnectionStringSpecification extends Specification {
188189
connectionString.getReadPreference() == null;
189190
connectionString.getRequiredReplicaSetName() == null
190191
connectionString.getSslEnabled() == null
192+
connectionString.getStreamType() == null
191193
}
192194

193195
@Unroll

0 commit comments

Comments
 (0)