Skip to content

Commit 9e8fa21

Browse files
author
Mark
committed
added support for multiple hosts as fallbacks
1 parent 9752528 commit 9e8fa21

File tree

16 files changed

+488
-96
lines changed

16 files changed

+488
-96
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ v4.1.10 (2017-02-xx)
22
---------------------------
33
* changed velocystream message sending to async
44
* changed return value of getVertex/getEdge to null if not exists
5+
* added support for multiple hosts as fallbacks
56
* added support serializing collections with null elements
67
* added support serializing non-generic classes that extend collections
78
* added support serializing/deserializing byte and Byte

src/main/java/com/arangodb/ArangoDB.java

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
import java.io.IOException;
2424
import java.io.InputStream;
2525
import java.lang.annotation.Annotation;
26+
import java.util.ArrayList;
2627
import java.util.Collection;
28+
import java.util.List;
2729
import java.util.Properties;
2830

2931
import javax.net.ssl.SSLContext;
@@ -43,6 +45,8 @@
4345
import com.arangodb.internal.velocystream.Communication;
4446
import com.arangodb.internal.velocystream.CommunicationSync;
4547
import com.arangodb.internal.velocystream.ConnectionSync;
48+
import com.arangodb.internal.velocystream.DefaultHostHandler;
49+
import com.arangodb.internal.velocystream.Host;
4650
import com.arangodb.model.LogOptions;
4751
import com.arangodb.model.UserCreateOptions;
4852
import com.arangodb.model.UserUpdateOptions;
@@ -68,17 +72,8 @@ public class ArangoDB extends InternalArangoDB<ArangoExecutorSync, Response, Con
6872

6973
public static class Builder {
7074

71-
private static final String PROPERTY_KEY_HOST = "arangodb.host";
72-
private static final String PROPERTY_KEY_PORT = "arangodb.port";
73-
private static final String PROPERTY_KEY_TIMEOUT = "arangodb.timeout";
74-
private static final String PROPERTY_KEY_USER = "arangodb.user";
75-
private static final String PROPERTY_KEY_PASSWORD = "arangodb.password";
76-
private static final String PROPERTY_KEY_USE_SSL = "arangodb.usessl";
77-
private static final String PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE = "arangodb.chunksize";
78-
private static final String DEFAULT_PROPERTY_FILE = "/arangodb.properties";
79-
80-
private String host;
81-
private Integer port;
75+
private final List<Host> hosts;
76+
private Host host;
8277
private Integer timeout;
8378
private String user;
8479
private String password;
@@ -95,48 +90,67 @@ public Builder() {
9590
collectionCache = new CollectionCache();
9691
vpackParser = new VPackParser();
9792
VPackConfigure.configure(vpackBuilder, vpackParser, collectionCache);
93+
host = new Host(ArangoDBConstants.DEFAULT_HOST, ArangoDBConstants.DEFAULT_PORT);
94+
hosts = new ArrayList<Host>();
9895
loadProperties(ArangoDB.class.getResourceAsStream(DEFAULT_PROPERTY_FILE));
9996
}
10097

101-
public Builder loadProperties(final InputStream in) {
98+
public Builder loadProperties(final InputStream in) throws ArangoDBException {
10299
if (in != null) {
103100
final Properties properties = new Properties();
104101
try {
105102
properties.load(in);
106-
host = getProperty(properties, PROPERTY_KEY_HOST, host, ArangoDBConstants.DEFAULT_HOST);
107-
port = Integer
108-
.parseInt(getProperty(properties, PROPERTY_KEY_PORT, port, ArangoDBConstants.DEFAULT_PORT));
109-
timeout = Integer.parseInt(
110-
getProperty(properties, PROPERTY_KEY_TIMEOUT, timeout, ArangoDBConstants.DEFAULT_TIMEOUT));
111-
user = getProperty(properties, PROPERTY_KEY_USER, user, null);
112-
password = getProperty(properties, PROPERTY_KEY_PASSWORD, password, null);
113-
useSsl = Boolean.parseBoolean(
114-
getProperty(properties, PROPERTY_KEY_USE_SSL, useSsl, ArangoDBConstants.DEFAULT_USE_SSL));
115-
chunksize = Integer.parseInt(getProperty(properties, PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE,
116-
chunksize, ArangoDBConstants.CHUNK_DEFAULT_CONTENT_SIZE));
103+
loadHosts(properties, this.hosts);
104+
final String host = loadHost(properties, this.host.getHost());
105+
final int port = loadPort(properties, this.host.getPort());
106+
this.host = new Host(host, port);
107+
timeout = loadTimeout(properties, timeout);
108+
user = loadUser(properties, user);
109+
password = loadPassword(properties, password);
110+
useSsl = loadUseSsl(properties, useSsl);
111+
chunksize = loadChunkSize(properties, chunksize);
117112
} catch (final IOException e) {
118113
throw new ArangoDBException(e);
119114
}
120115
}
121116
return this;
122117
}
123118

124-
private <T> String getProperty(
125-
final Properties properties,
126-
final String key,
127-
final T currentValue,
128-
final T defaultValue) {
129-
return properties.getProperty(key,
130-
currentValue != null ? currentValue.toString() : defaultValue != null ? defaultValue.toString() : null);
131-
}
132-
119+
/**
120+
* @deprecated will be removed in version 4.2.0 use {@link #host(String, int)} instead
121+
*
122+
* @param host
123+
* @return
124+
*/
125+
@Deprecated
133126
public Builder host(final String host) {
134-
this.host = host;
127+
this.host = new Host(host, this.host.getPort());
135128
return this;
136129
}
137130

131+
/**
132+
* @deprecated will be removed in version 4.2.0 use {@link #host(String, int)} instead
133+
*
134+
* @param port
135+
* @return
136+
*/
137+
@Deprecated
138138
public Builder port(final Integer port) {
139-
this.port = port;
139+
host = new Host(host.getHost(), port);
140+
return this;
141+
}
142+
143+
/**
144+
* Adds a host to connect to. Multiple hosts can be added to provide fallbacks.
145+
*
146+
* @param host
147+
* address of the host
148+
* @param port
149+
* port of the host
150+
* @return {@link ArangoDB.Builder}
151+
*/
152+
public Builder host(final String host, final int port) {
153+
hosts.add(new Host(host, port));
140154
return this;
141155
}
142156

@@ -240,9 +254,12 @@ public <A extends Annotation> Builder annotationFieldNaming(
240254
}
241255

242256
public ArangoDB build() {
257+
if (hosts.isEmpty()) {
258+
hosts.add(host);
259+
}
243260
return new ArangoDB(
244-
new CommunicationSync.Builder().host(host).port(port).timeout(timeout).user(user).password(password)
245-
.useSsl(useSsl).sslContext(sslContext).chunksize(chunksize),
261+
new CommunicationSync.Builder(new DefaultHostHandler(hosts)).timeout(timeout).user(user)
262+
.password(password).useSsl(useSsl).sslContext(sslContext).chunksize(chunksize),
246263
vpackBuilder.build(), vpackBuilder.serializeNullValues(true).build(), vpackParser, collectionCache);
247264
}
248265

src/main/java/com/arangodb/internal/InternalArangoDB.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
import java.util.Collection;
2525
import java.util.Iterator;
2626
import java.util.Map.Entry;
27+
import java.util.Properties;
2728

29+
import com.arangodb.ArangoDBException;
2830
import com.arangodb.entity.LogLevelEntity;
2931
import com.arangodb.entity.UserEntity;
3032
import com.arangodb.internal.ArangoExecutor.ResponseDeserializer;
3133
import com.arangodb.internal.velocystream.Connection;
34+
import com.arangodb.internal.velocystream.Host;
3235
import com.arangodb.model.DBCreateOptions;
3336
import com.arangodb.model.LogOptions;
3437
import com.arangodb.model.OptionsBuilder;
@@ -50,10 +53,78 @@
5053
public class InternalArangoDB<E extends ArangoExecutor<R, C>, R, C extends Connection>
5154
extends ArangoExecuteable<E, R, C> {
5255

56+
private static final String PROPERTY_KEY_HOSTS = "arangodb.hosts";
57+
private static final String PROPERTY_KEY_HOST = "arangodb.host";
58+
private static final String PROPERTY_KEY_PORT = "arangodb.port";
59+
private static final String PROPERTY_KEY_TIMEOUT = "arangodb.timeout";
60+
private static final String PROPERTY_KEY_USER = "arangodb.user";
61+
private static final String PROPERTY_KEY_PASSWORD = "arangodb.password";
62+
private static final String PROPERTY_KEY_USE_SSL = "arangodb.usessl";
63+
private static final String PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE = "arangodb.chunksize";
64+
protected static final String DEFAULT_PROPERTY_FILE = "/arangodb.properties";
65+
5366
public InternalArangoDB(final E executor) {
5467
super(executor);
5568
}
5669

70+
protected static void loadHosts(final Properties properties, final Collection<Host> hosts) {
71+
final String hostsProp = properties.getProperty(PROPERTY_KEY_HOSTS);
72+
if (hostsProp != null) {
73+
final String[] hostsSplit = hostsProp.split(",");
74+
for (final String host : hostsSplit) {
75+
final String[] split = host.split(":");
76+
if (split.length != 2 || !split[1].matches("[0-9]+")) {
77+
throw new ArangoDBException(String.format(
78+
"Could not load property-value arangodb.hosts=%s. Expected format host:ip,host:ip,...",
79+
hostsProp));
80+
} else {
81+
hosts.add(new Host(split[0], Integer.valueOf(split[1])));
82+
}
83+
}
84+
}
85+
}
86+
87+
protected static String loadHost(final Properties properties, final String currentValue) {
88+
return getProperty(properties, PROPERTY_KEY_HOST, currentValue, ArangoDBConstants.DEFAULT_HOST);
89+
}
90+
91+
protected static Integer loadPort(final Properties properties, final int currentValue) {
92+
return Integer
93+
.parseInt(getProperty(properties, PROPERTY_KEY_PORT, currentValue, ArangoDBConstants.DEFAULT_PORT));
94+
}
95+
96+
protected static Integer loadTimeout(final Properties properties, final Integer currentValue) {
97+
return Integer.parseInt(
98+
getProperty(properties, PROPERTY_KEY_TIMEOUT, currentValue, ArangoDBConstants.DEFAULT_TIMEOUT));
99+
}
100+
101+
protected static String loadUser(final Properties properties, final String currentValue) {
102+
return getProperty(properties, PROPERTY_KEY_USER, currentValue, null);
103+
}
104+
105+
protected static String loadPassword(final Properties properties, final String currentValue) {
106+
return getProperty(properties, PROPERTY_KEY_PASSWORD, currentValue, null);
107+
}
108+
109+
protected static Boolean loadUseSsl(final Properties properties, final Boolean currentValue) {
110+
return Boolean.parseBoolean(
111+
getProperty(properties, PROPERTY_KEY_USE_SSL, currentValue, ArangoDBConstants.DEFAULT_USE_SSL));
112+
}
113+
114+
protected static Integer loadChunkSize(final Properties properties, final Integer currentValue) {
115+
return Integer.parseInt(getProperty(properties, PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE, currentValue,
116+
ArangoDBConstants.CHUNK_DEFAULT_CONTENT_SIZE));
117+
}
118+
119+
private static <T> String getProperty(
120+
final Properties properties,
121+
final String key,
122+
final T currentValue,
123+
final T defaultValue) {
124+
return properties.getProperty(key,
125+
currentValue != null ? currentValue.toString() : defaultValue != null ? defaultValue.toString() : null);
126+
}
127+
57128
protected Request createDatabaseRequest(final String name) {
58129
final Request request = new Request(ArangoDBConstants.SYSTEM, RequestType.POST,
59130
ArangoDBConstants.PATH_API_DATABASE);

src/main/java/com/arangodb/internal/velocystream/Communication.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ public abstract class Communication<R, C extends Connection> {
6060

6161
protected final Integer chunksize;
6262

63-
protected Communication(final String host, final Integer port, final Integer timeout, final String user,
64-
final String password, final Boolean useSsl, final SSLContext sslContext, final VPack vpack,
65-
final CollectionCache collectionCache, final Integer chunksize, final C connection) {
63+
protected Communication(final Integer timeout, final String user, final String password, final Boolean useSsl,
64+
final SSLContext sslContext, final VPack vpack, final CollectionCache collectionCache, final Integer chunksize,
65+
final C connection) {
6666
this.user = user;
6767
this.password = password;
6868
this.vpack = vpack;

src/main/java/com/arangodb/internal/velocystream/CommunicationSync.java

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,18 @@ public class CommunicationSync extends Communication<Response, ConnectionSync> {
4242
private static final Logger LOGGER = LoggerFactory.getLogger(CommunicationSync.class);
4343

4444
public static class Builder {
45-
private String host;
46-
private Integer port;
45+
46+
private final HostHandler hostHandler;
4747
private Integer timeout;
4848
private String user;
4949
private String password;
5050
private Boolean useSsl;
5151
private SSLContext sslContext;
5252
private Integer chunksize;
5353

54-
public Builder() {
54+
public Builder(final HostHandler hostHandler) {
5555
super();
56-
}
57-
58-
public Builder host(final String host) {
59-
this.host = host;
60-
return this;
61-
}
62-
63-
public Builder port(final Integer port) {
64-
this.port = port;
65-
return this;
56+
this.hostHandler = hostHandler;
6657
}
6758

6859
public Builder timeout(final Integer timeout) {
@@ -96,16 +87,16 @@ public Builder chunksize(final Integer chunksize) {
9687
}
9788

9889
public Communication<Response, ConnectionSync> build(final VPack vpack, final CollectionCache collectionCache) {
99-
return new CommunicationSync(host, port, timeout, user, password, useSsl, sslContext, vpack,
90+
return new CommunicationSync(hostHandler, timeout, user, password, useSsl, sslContext, vpack,
10091
collectionCache, chunksize);
10192
}
10293
}
10394

104-
protected CommunicationSync(final String host, final Integer port, final Integer timeout, final String user,
95+
protected CommunicationSync(final HostHandler hostHandler, final Integer timeout, final String user,
10596
final String password, final Boolean useSsl, final SSLContext sslContext, final VPack vpack,
10697
final CollectionCache collectionCache, final Integer chunksize) {
107-
super(host, port, timeout, user, password, useSsl, sslContext, vpack, collectionCache, chunksize,
108-
new ConnectionSync.Builder(new MessageStore()).host(host).port(port).timeout(timeout).useSsl(useSsl)
98+
super(timeout, user, password, useSsl, sslContext, vpack, collectionCache, chunksize,
99+
new ConnectionSync.Builder(hostHandler, new MessageStore()).timeout(timeout).useSsl(useSsl)
109100
.sslContext(sslContext).build());
110101
}
111102

src/main/java/com/arangodb/internal/velocystream/Connection.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ public abstract class Connection {
5757
private ExecutorService executor;
5858
protected final MessageStore messageStore;
5959

60-
private final String host;
61-
private final Integer port;
60+
private final HostHandler hostHandler;
6261
private final Integer timeout;
6362
private final Boolean useSsl;
6463
private final SSLContext sslContext;
@@ -67,11 +66,10 @@ public abstract class Connection {
6766
private OutputStream outputStream;
6867
private InputStream inputStream;
6968

70-
protected Connection(final String host, final Integer port, final Integer timeout, final Boolean useSsl,
69+
protected Connection(final HostHandler hostHandler, final Integer timeout, final Boolean useSsl,
7170
final SSLContext sslContext, final MessageStore messageStore) {
7271
super();
73-
this.host = host;
74-
this.port = port;
72+
this.hostHandler = hostHandler;
7573
this.timeout = timeout;
7674
this.useSsl = useSsl;
7775
this.sslContext = sslContext;
@@ -86,22 +84,36 @@ public synchronized void open() throws IOException {
8684
if (isOpen()) {
8785
return;
8886
}
89-
if (useSsl != null && useSsl) {
90-
if (sslContext != null) {
91-
socket = sslContext.getSocketFactory().createSocket();
92-
} else {
93-
socket = SSLSocketFactory.getDefault().createSocket();
87+
Host host = hostHandler.get();
88+
while (true) {
89+
if (LOGGER.isDebugEnabled()) {
90+
LOGGER.debug(String.format("Open connection to %s", host));
91+
}
92+
try {
93+
if (useSsl != null && useSsl) {
94+
if (sslContext != null) {
95+
socket = sslContext.getSocketFactory().createSocket();
96+
} else {
97+
socket = SSLSocketFactory.getDefault().createSocket();
98+
}
99+
} else {
100+
socket = SocketFactory.getDefault().createSocket();
101+
}
102+
socket.connect(new InetSocketAddress(host.getHost(), host.getPort()),
103+
timeout != null ? timeout : ArangoDBConstants.DEFAULT_TIMEOUT);
104+
hostHandler.success();
105+
break;
106+
} catch (final IOException e) {
107+
hostHandler.fail();
108+
final Host failedHost = host;
109+
host = hostHandler.change();
110+
if (host != null) {
111+
LOGGER.warn(String.format("Could not connect to %s. Try connecting to %s", failedHost, host));
112+
} else {
113+
throw e;
114+
}
94115
}
95-
} else {
96-
socket = SocketFactory.getDefault().createSocket();
97-
}
98-
final String host = this.host != null ? this.host : ArangoDBConstants.DEFAULT_HOST;
99-
final Integer port = this.port != null ? this.port : ArangoDBConstants.DEFAULT_PORT;
100-
if (LOGGER.isDebugEnabled()) {
101-
LOGGER.debug(String.format("Open connection to addr=%s,port=%s", host, port));
102116
}
103-
socket.connect(new InetSocketAddress(host, port),
104-
timeout != null ? timeout : ArangoDBConstants.DEFAULT_TIMEOUT);
105117
socket.setKeepAlive(true);
106118
socket.setTcpNoDelay(true);
107119
if (LOGGER.isDebugEnabled()) {

0 commit comments

Comments
 (0)