Skip to content

Commit e1680a2

Browse files
committed
Complete unix socket support
Ensure jnr library is optional via UnixServerAddress Added unix socket support to the connection string. JAVA-163
1 parent d2ff8e1 commit e1680a2

File tree

24 files changed

+504
-109
lines changed

24 files changed

+504
-109
lines changed

.evergreen/.evg.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,15 @@ functions:
216216
${PREPARE_SHELL}
217217
AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" TOPOLOGY="${TOPOLOGY}" COMPRESSOR="${COMPRESSOR}" JDK="${JDK}" .evergreen/run-tests.sh
218218
219+
"run socket tests":
220+
- command: shell.exec
221+
type: test
222+
params:
223+
working_dir: "src"
224+
script: |
225+
${PREPARE_SHELL}
226+
AUTH="${AUTH}" MONGODB_URI="${MONGODB_URI}" TOPOLOGY="${TOPOLOGY}" COMPRESSOR="${COMPRESSOR}" JDK="${JDK}" .evergreen/run-socket-tests.sh
227+
219228
"run plain auth test":
220229
- command: shell.exec
221230
type: test
@@ -360,6 +369,14 @@ tasks:
360369
commands:
361370
- func: "run gssapi auth test"
362371

372+
- name: "socket-test"
373+
depends_on:
374+
- variant: "static-checks"
375+
name: "static-analysis"
376+
commands:
377+
- func: "bootstrap mongo-orchestration"
378+
- func: "run socket tests"
379+
363380
- name: publish-snapshot
364381
depends_on:
365382
- variant: ".tests-variant"
@@ -520,6 +537,20 @@ buildvariants:
520537
tasks:
521538
- name: "test"
522539

540+
- matrix_name: "tests-socket"
541+
matrix_spec: { auth: "*", ssl: "nossl", jdk: "jdk8", version: "3.6", topology: "*", os: "linux" }
542+
display_name: "Socket: ${version} ${topology} ${auth} ${jdk} ${os} "
543+
tags: ["tests-socket-variant"]
544+
tasks:
545+
- name: "socket-test"
546+
547+
- matrix_name: "tests-socket-snappy-compression"
548+
matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: "3.6", topology: "standalone", os: "linux" }
549+
display_name: "Socket: ${version} ${compressor} ${topology} ${auth} ${jdk} ${os} "
550+
tags: ["tests-socket-variant"]
551+
tasks:
552+
- name: "socket-test"
553+
523554
- matrix_name: "test-gssapi"
524555
matrix_spec: { jdk: "*", os: "linux" }
525556
display_name: "GSSAPI (Kerberos) Auth test ${jdk} ${os} "

.evergreen/run-socket-tests.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/bash
2+
3+
set -o xtrace # Write all commands first to stderr
4+
set -o errexit # Exit the script with error if any of the commands fail
5+
6+
# Supported/used environment variables:
7+
# AUTH Set to enable authentication. Values are: "auth" / "noauth" (default)
8+
# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info)
9+
# TOPOLOGY Allows you to modify variables and the MONGODB_URI based on test topology
10+
# Supported values: "server", "replica_set", "sharded_cluster"
11+
# COMPRESSOR Set to enable compression. Values are "snappy" and "zlib" (default is no compression)
12+
# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java
13+
# "jdk5", "jdk6", "jdk7", "jdk8"
14+
15+
AUTH=${AUTH:-noauth}
16+
MONGODB_URI=${MONGODB_URI:-}
17+
JDK=${JDK:-jdk}
18+
JAVA_HOME="/opt/java/${JDK}"
19+
TOPOLOGY=${TOPOLOGY:-server}
20+
COMPRESSOR=${COMPRESSOR:-}
21+
22+
############################################
23+
# Main Program #
24+
############################################
25+
26+
SOCKET_REGEX='(.*)localhost:([0-9]+)?(.*)'
27+
while [[ $MONGODB_URI =~ $SOCKET_REGEX ]]; do
28+
MONGODB_URI="${BASH_REMATCH[1]}%2Ftmp%2Fmongodb-${BASH_REMATCH[2]}.sock${BASH_REMATCH[3]}"
29+
done
30+
31+
# Provision the correct connection string and set up SSL if needed
32+
if [ "$TOPOLOGY" == "sharded_cluster" ]; then
33+
34+
if [ "$AUTH" = "auth" ]; then
35+
export MONGODB_URI="mongodb://bob:pwd123@%2Ftmp%2Fmongodb-27017.sock/?authSource=admin"
36+
else
37+
export MONGODB_URI="mongodb://%2Ftmp%2Fmongodb-27017.sock/"
38+
fi
39+
fi
40+
41+
if [ "$COMPRESSOR" != "" ]; then
42+
if [[ "$MONGODB_URI" == *"?"* ]]; then
43+
export MONGODB_URI="${MONGODB_URI}&compressors=${COMPRESSOR}"
44+
else
45+
export MONGODB_URI="${MONGODB_URI}/?compressors=${COMPRESSOR}"
46+
fi
47+
fi
48+
49+
echo "Running $AUTH tests over for $TOPOLOGY and connecting to $MONGODB_URI"
50+
51+
# We always compile with the latest version of java
52+
export JAVA_HOME="/opt/java/jdk8"
53+
54+
echo "Running tests with ${JDK}"
55+
./gradlew -version
56+
./gradlew -PjdkHome=/opt/java/${JDK} -Dorg.mongodb.test.uri=${MONGODB_URI} ${GRADLE_EXTRA_VARS} --stacktrace --info :driver-legacy:test :driver-sync:test

docs/reference/content/driver/tutorials/connect-to-mongodb.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ To connect to a standalone MongoDB instance:
7979
MongoClient mongoClient = new MongoClient( "host1" );
8080
```
8181

82+
- You can connect to the Unix domain socket (requires the `jnr.unixsocket` library):
83+
84+
```java
85+
MongoClient mongoClient = new MongoClient( "/tmp/mongodb-27017.sock" );
86+
```
87+
8288
- You can explicitly specify the hostname and the port:
8389

8490
```java
@@ -92,6 +98,12 @@ To connect to a standalone MongoDB instance:
9298
MongoClient mongoClient = new MongoClient(new MongoClientURI("mongodb://host1:27017"));
9399
```
94100

101+
Unix domain socket connections via the connection string (requires the `jnr.unixsocket` library and the path to be urlencoded):
102+
103+
```java
104+
MongoClient mongoClient = new MongoClient(new MongoClientURI("mongodb://%2Ftmp%2Fmongodb-27107.sock"));
105+
```
106+
95107
## Connect to a Replica Set
96108

97109
To connect to a [replica set]({{<docsref "replication/">}}), you must specify one or more members to the

docs/reference/content/whats-new.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ title = "What's New"
77
pre = "<i class='fa fa-level-up'></i>"
88
+++
99

10+
## What's New in 3.7
11+
12+
Key new features of the 3.7 Java driver release:
13+
14+
### Unix domain socket support
15+
16+
The 3.7 driver adds support for Unix domain sockets via the [`jnr.unixsocket`](http://https://github.com/jnr/jnr-unixsocket) library.
17+
Connecting to Unix domain sockets is done via the [`ConnectionString`]({{< apiref "com/mongodb/ConnectionString" >}}) or via
18+
[`UnixServerAddress`]({{<apiref "com/mongodb/UnixServerAddress.html">}}).
19+
1020
## What's New in 3.6
1121

1222
Key new features of the 3.6 Java driver release:

driver-core/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ jar {
5454
'org.ietf.jgss.*',
5555
'io.netty.*;resolution:=optional',
5656
'org.xerial.snappy.*;resolution:=optional',
57-
'org.slf4j;resolution:=optional'
57+
'org.slf4j;resolution:=optional',
58+
'jnr.unixsocket;resolution:=optional'
5859
}
5960
}
6061

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
* <li>{@code username:password@} are optional. If given, the driver will attempt to login to a database after
5353
* connecting to a database server. For some authentication mechanisms, only the username is specified and the password is not,
5454
* in which case the ":" after the username is left off as well</li>
55-
* <li>{@code host1} is the only required part of the connection string. It identifies a server address to connect to.</li>
55+
* <li>{@code host1} is the only required part of the connection string. It identifies a server address to connect to.
56+
* Support for Unix domain sockets was added in 3.7. Note: The path must be urlencoded eg: {@code mongodb://%2Ftmp%2Fmongodb-27017.sock}
57+
* and the {@code jnr.unixsocket} library installed.
58+
* </li>
5659
* <li>{@code :portX} is optional and defaults to :27017 if not provided.</li>
5760
* <li>{@code /database} is the name of the database to login to and thus is only relevant if the
5861
* {@code username:password@} syntax is used. If not specified the "admin" database will be used by default.</li>
@@ -833,8 +836,7 @@ private List<String> parseHosts(final List<String> rawHosts, final boolean isSRV
833836
if (host.length() == 0) {
834837
throw new IllegalArgumentException(format("The connection string contains an empty host '%s'. ", rawHosts));
835838
} else if (host.endsWith(".sock")) {
836-
throw new IllegalArgumentException(format("The connection string contains an invalid host '%s'. "
837-
+ "Unix Domain Socket which is not supported by the Java driver", host));
839+
host = urldecode(host);
838840
} else if (host.startsWith("[")) {
839841
if (!host.contains("]")) {
840842
throw new IllegalArgumentException(format("The connection string contains an invalid host '%s'. "

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

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@
1919
import com.mongodb.annotations.Immutable;
2020
import com.mongodb.lang.Nullable;
2121

22-
import jnr.unixsocket.UnixSocketAddress;
23-
24-
import java.io.File;
2522
import java.io.Serializable;
2623
import java.net.InetAddress;
2724
import java.net.InetSocketAddress;
28-
import java.net.SocketAddress;
2925
import java.net.UnknownHostException;
3026

3127
/**
@@ -37,7 +33,6 @@ public class ServerAddress implements Serializable {
3733

3834
private final String host;
3935
private final int port;
40-
private final SocketAddress address;
4136

4237
/**
4338
* Creates a ServerAddress with default host and port
@@ -61,7 +56,7 @@ public ServerAddress(@Nullable final String host) {
6156
* @param inetAddress host address
6257
*/
6358
public ServerAddress(final InetAddress inetAddress) {
64-
this(inetAddress.getHostName(), defaultPort(), new InetSocketAddress(inetAddress, defaultPort()));
59+
this(inetAddress.getHostName(), defaultPort());
6560
}
6661

6762
/**
@@ -71,7 +66,7 @@ public ServerAddress(final InetAddress inetAddress) {
7166
* @param port mongod port
7267
*/
7368
public ServerAddress(final InetAddress inetAddress, final int port) {
74-
this(inetAddress.getHostName(), port, new InetSocketAddress(inetAddress, port));
69+
this(inetAddress.getHostName(), port);
7570
}
7671

7772
/**
@@ -80,38 +75,7 @@ public ServerAddress(final InetAddress inetAddress, final int port) {
8075
* @param inetSocketAddress inet socket address containing hostname and port
8176
*/
8277
public ServerAddress(final InetSocketAddress inetSocketAddress) {
83-
this(inetSocketAddress.getHostName(), inetSocketAddress.getPort(), inetSocketAddress);
84-
}
85-
86-
/**
87-
* Creates a ServerAddress
88-
*
89-
* @param serverAddress an instance to be shallow-copied
90-
*/
91-
public ServerAddress(final ServerAddress serverAddress) {
92-
this(serverAddress.host, serverAddress.port, serverAddress.address);
93-
}
94-
95-
/**
96-
* Creates a ServerAddress
97-
*
98-
* @param path the file used for the Unix domain socket
99-
*/
100-
public ServerAddress(final File path) {
101-
this(path.toString(), 0, new UnixSocketAddress(path));
102-
}
103-
104-
/**
105-
* Creates a ServerAddress - intended for internal usage
106-
*
107-
* @param host hostname
108-
* @param port mongod port
109-
* @param address an instance of socket address or `null`
110-
*/
111-
protected ServerAddress(final String host, final int port, final SocketAddress address) {
112-
this.host = host;
113-
this.port = port;
114-
this.address = address;
78+
this(inetSocketAddress.getAddress(), inetSocketAddress.getPort());
11579
}
11680

11781
/**
@@ -121,29 +85,31 @@ protected ServerAddress(final String host, final int port, final SocketAddress a
12185
* @param port mongod port
12286
*/
12387
public ServerAddress(@Nullable final String host, final int port) {
124-
String hostToUse = host == null ? defaultHost() : host;
88+
String hostToUse = host;
89+
if (hostToUse == null) {
90+
hostToUse = defaultHost();
91+
}
12592
hostToUse = hostToUse.trim();
12693
if (hostToUse.length() == 0) {
12794
hostToUse = defaultHost();
12895
}
129-
13096
int portToUse = port;
13197

13298
if (hostToUse.startsWith("[")) {
133-
int idx = hostToUse.indexOf("]");
99+
int idx = host.indexOf("]");
134100
if (idx == -1) {
135101
throw new IllegalArgumentException("an IPV6 address must be encosed with '[' and ']'"
136102
+ " according to RFC 2732.");
137103
}
138104

139-
int portIdx = hostToUse.indexOf("]:");
105+
int portIdx = host.indexOf("]:");
140106
if (portIdx != -1) {
141107
if (port != defaultPort()) {
142108
throw new IllegalArgumentException("can't specify port in construct and via host");
143109
}
144-
portToUse = Integer.parseInt(hostToUse.substring(portIdx + 2));
110+
portToUse = Integer.parseInt(host.substring(portIdx + 2));
145111
}
146-
hostToUse = hostToUse.substring(1, idx);
112+
hostToUse = host.substring(1, idx);
147113
} else {
148114
int idx = hostToUse.indexOf(":");
149115
int lastIdx = hostToUse.lastIndexOf(":");
@@ -161,7 +127,6 @@ public ServerAddress(@Nullable final String host, final int port) {
161127
}
162128
this.host = hostToUse.toLowerCase();
163129
this.port = portToUse;
164-
this.address = null;
165130
}
166131

167132
@Override
@@ -216,10 +181,7 @@ public int getPort() {
216181
*
217182
* @return socket address
218183
*/
219-
public SocketAddress getSocketAddress() {
220-
if (address != null) {
221-
return address;
222-
}
184+
public InetSocketAddress getSocketAddress() {
223185
try {
224186
return new InetSocketAddress(InetAddress.getByName(host), port);
225187
} catch (UnknownHostException e) {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2008-present 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+
package com.mongodb;
18+
19+
import com.mongodb.annotations.Immutable;
20+
import jnr.unixsocket.UnixSocketAddress;
21+
22+
import java.net.InetSocketAddress;
23+
import java.net.SocketAddress;
24+
25+
import static com.mongodb.assertions.Assertions.isTrueArgument;
26+
import static com.mongodb.assertions.Assertions.notNull;
27+
28+
/**
29+
* Represents the location of a MongoD unix domain socket.
30+
*
31+
* <p>Requires the 'jnr.unixsocket' library.</p>
32+
* @since 3.7
33+
*/
34+
@Immutable
35+
public final class UnixServerAddress extends ServerAddress {
36+
private static final long serialVersionUID = 154466643544866543L;
37+
38+
/**
39+
* Creates a new instance
40+
* @param path the path of the MongoD unix domain socket.
41+
*/
42+
public UnixServerAddress(final String path) {
43+
super(notNull("The path cannot be null", path));
44+
isTrueArgument("The path must end in .sock", path.endsWith(".sock"));
45+
}
46+
47+
@Override
48+
public InetSocketAddress getSocketAddress() {
49+
throw new UnsupportedOperationException("Cannot return a InetSocketAddress from a UnixServerAddress");
50+
}
51+
52+
/**
53+
* @return the SocketAddress for the MongoD unix domain socket.
54+
*/
55+
public SocketAddress getUnixSocketAddress() {
56+
return new UnixSocketAddress(getHost());
57+
}
58+
59+
@Override
60+
public String toString() {
61+
return getHost();
62+
}
63+
}

0 commit comments

Comments
 (0)