diff --git a/pom.xml b/pom.xml
index 9428c68cb..129016b7b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.arangodb
arangodb-java-driver
- 6.14.0
+ 6.15.0-SNAPSHOT
2016
jar
diff --git a/src/main/java/com/arangodb/ArangoDBException.java b/src/main/java/com/arangodb/ArangoDBException.java
index 7e5c45886..6ea9ce8c8 100644
--- a/src/main/java/com/arangodb/ArangoDBException.java
+++ b/src/main/java/com/arangodb/ArangoDBException.java
@@ -56,6 +56,12 @@ public ArangoDBException(final Throwable cause) {
this.responseCode = null;
}
+ public ArangoDBException(final String message, final Throwable cause) {
+ super(message, cause);
+ this.entity = null;
+ this.responseCode = null;
+ }
+
/**
* @return ArangoDB error message
*/
diff --git a/src/main/java/com/arangodb/ArangoDBMultipleException.java b/src/main/java/com/arangodb/ArangoDBMultipleException.java
new file mode 100644
index 000000000..4181cb95d
--- /dev/null
+++ b/src/main/java/com/arangodb/ArangoDBMultipleException.java
@@ -0,0 +1,44 @@
+package com.arangodb;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+public class ArangoDBMultipleException extends RuntimeException {
+
+ private final List exceptions;
+
+ public ArangoDBMultipleException(List exceptions) {
+ super();
+ this.exceptions = exceptions;
+ }
+
+ public List getExceptions() {
+ return exceptions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ArangoDBMultipleException that = (ArangoDBMultipleException) o;
+ return Objects.equals(exceptions, that.exceptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(exceptions);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner("\n\t", "ArangoDBMultipleException{\n\t", "\n}");
+ for (Throwable t : exceptions) {
+ StringJoiner tJoiner = new StringJoiner("\n\t\t", "\n\t\t", "");
+ for (StackTraceElement stackTraceElement : t.getStackTrace())
+ tJoiner.add("at " + stackTraceElement);
+ joiner.add(t + tJoiner.toString());
+ }
+ return joiner.toString();
+ }
+}
diff --git a/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java b/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
index bd3540f04..3a1b3f1b7 100644
--- a/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
+++ b/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
@@ -85,7 +85,7 @@ protected CompletableFuture execute(final Request request, final VstCo
final String location = e.getLocation();
final HostDescription redirectHost = HostUtils.createFromLocation(location);
hostHandler.closeCurrentOnError();
- hostHandler.fail();
+ hostHandler.fail(e);
execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1)
.whenComplete((v, err) -> {
if (v != null) {
diff --git a/src/main/java/com/arangodb/internal/http/HttpCommunication.java b/src/main/java/com/arangodb/internal/http/HttpCommunication.java
index 558b058db..c40106b8a 100644
--- a/src/main/java/com/arangodb/internal/http/HttpCommunication.java
+++ b/src/main/java/com/arangodb/internal/http/HttpCommunication.java
@@ -32,7 +32,6 @@
import java.io.Closeable;
import java.io.IOException;
-import java.net.SocketException;
/**
* @author Mark Vollmary
@@ -71,11 +70,11 @@ public void close() throws IOException {
hostHandler.close();
}
- public Response execute(final Request request, final HostHandle hostHandle) throws ArangoDBException, IOException {
+ public Response execute(final Request request, final HostHandle hostHandle) throws ArangoDBException {
return execute(request, hostHandle, 0);
}
- private Response execute(final Request request, final HostHandle hostHandle, final int attemptCount) throws ArangoDBException, IOException {
+ private Response execute(final Request request, final HostHandle hostHandle, final int attemptCount) throws ArangoDBException {
final AccessType accessType = RequestUtils.determineAccessType(request);
Host host = hostHandler.get(hostHandle, accessType);
try {
@@ -86,19 +85,20 @@ private Response execute(final Request request, final HostHandle hostHandle, fin
hostHandler.success();
hostHandler.confirm();
return response;
- } catch (final SocketException se) {
- hostHandler.fail();
+ } catch (final IOException e) {
+ hostHandler.fail(e);
if (hostHandle != null && hostHandle.getHost() != null) {
hostHandle.setHost(null);
}
final Host failedHost = host;
host = hostHandler.get(hostHandle, accessType);
if (host != null) {
- LOGGER.warn(String.format("Could not connect to %s", failedHost.getDescription()), se);
+ LOGGER.warn(String.format("Could not connect to %s", failedHost.getDescription()), e);
LOGGER.warn(String.format("Could not connect to %s. Try connecting to %s",
failedHost.getDescription(), host.getDescription()));
} else {
- throw se;
+ LOGGER.error(e.getMessage(), e);
+ throw new ArangoDBException(e);
}
}
}
@@ -107,7 +107,7 @@ private Response execute(final Request request, final HostHandle hostHandle, fin
final String location = ((ArangoDBRedirectException) e).getLocation();
final HostDescription redirectHost = HostUtils.createFromLocation(location);
hostHandler.closeCurrentOnError();
- hostHandler.fail();
+ hostHandler.fail(e);
return execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1);
} else {
throw e;
diff --git a/src/main/java/com/arangodb/internal/http/HttpProtocol.java b/src/main/java/com/arangodb/internal/http/HttpProtocol.java
index d946a5306..7997b245e 100644
--- a/src/main/java/com/arangodb/internal/http/HttpProtocol.java
+++ b/src/main/java/com/arangodb/internal/http/HttpProtocol.java
@@ -42,11 +42,7 @@ public HttpProtocol(final HttpCommunication httpCommunitaction) {
@Override
public Response execute(final Request request, final HostHandle hostHandle) throws ArangoDBException {
- try {
- return httpCommunitaction.execute(request, hostHandle);
- } catch (final IOException e) {
- throw new ArangoDBException(e);
- }
+ return httpCommunitaction.execute(request, hostHandle);
}
@Override
diff --git a/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java b/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
index 47feda2f6..93039224d 100644
--- a/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
@@ -56,8 +56,8 @@ public void success() {
}
@Override
- public void fail() {
- determineHostHandler().fail();
+ public void fail(Exception exception) {
+ determineHostHandler().fail(exception);
}
@Override
diff --git a/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java b/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
index c440f22f0..d88ac21ec 100644
--- a/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
@@ -21,7 +21,9 @@
package com.arangodb.internal.net;
import com.arangodb.ArangoDBException;
+import com.arangodb.ArangoDBMultipleException;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -33,12 +35,14 @@ public class FallbackHostHandler implements HostHandler {
private Host current;
private Host lastSuccess;
private int iterations;
+ private final List lastFailExceptions;
private boolean firstOpened;
private HostSet hosts;
public FallbackHostHandler(final HostResolver resolver) {
this.resolver = resolver;
- iterations = 0;
+ lastFailExceptions = new ArrayList<>();
+ reset();
hosts = resolver.resolve(true, false);
current = lastSuccess = hosts.getHostsList().get(0);
firstOpened = true;
@@ -49,19 +53,21 @@ public Host get(final HostHandle hostHandle, AccessType accessType) {
if (current != lastSuccess || iterations < 3) {
return current;
} else {
+ ArangoDBException e = new ArangoDBException("Cannot contact any host!",
+ new ArangoDBMultipleException(new ArrayList<>(lastFailExceptions)));
reset();
- throw new ArangoDBException("Cannot contact any host!");
+ throw e;
}
}
@Override
public void success() {
lastSuccess = current;
- iterations = 0;
+ reset();
}
@Override
- public void fail() {
+ public void fail(Exception exception) {
hosts = resolver.resolve(false, false);
final List hostList = hosts.getHostsList();
final int index = hostList.indexOf(current) + 1;
@@ -70,11 +76,13 @@ public void fail() {
if (!inBound) {
iterations++;
}
+ lastFailExceptions.add(exception);
}
@Override
public void reset() {
iterations = 0;
+ lastFailExceptions.clear();
}
@Override
diff --git a/src/main/java/com/arangodb/internal/net/HostHandler.java b/src/main/java/com/arangodb/internal/net/HostHandler.java
index dacd801f7..b432ca81e 100644
--- a/src/main/java/com/arangodb/internal/net/HostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/HostHandler.java
@@ -31,7 +31,7 @@ public interface HostHandler {
void success();
- void fail();
+ void fail(Exception exception);
void reset();
diff --git a/src/main/java/com/arangodb/internal/net/RandomHostHandler.java b/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
index a52147e55..a3ee4cac7 100644
--- a/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
@@ -54,8 +54,8 @@ public void success() {
}
@Override
- public void fail() {
- fallback.fail();
+ public void fail(Exception exception) {
+ fallback.fail(exception);
current = fallback.get(null, null);
}
diff --git a/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java b/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
index 20389245b..e4e653ff9 100644
--- a/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
@@ -21,6 +21,10 @@
package com.arangodb.internal.net;
import com.arangodb.ArangoDBException;
+import com.arangodb.ArangoDBMultipleException;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* @author Mark Vollmary
@@ -30,15 +34,17 @@ public class RoundRobinHostHandler implements HostHandler {
private final HostResolver resolver;
private int current;
private int fails;
+ private final List lastFailExceptions;
private Host currentHost;
private HostSet hosts;
public RoundRobinHostHandler(final HostResolver resolver) {
super();
this.resolver = resolver;
+ lastFailExceptions = new ArrayList<>();
hosts = resolver.resolve(true, false);
current = 0;
- fails = 0;
+ reset();
}
@Override
@@ -47,8 +53,10 @@ public Host get(final HostHandle hostHandle, AccessType accessType) {
final int size = hosts.getHostsList().size();
if (fails > size) {
+ ArangoDBException e = new ArangoDBException("Cannot contact any host!",
+ new ArangoDBMultipleException(new ArrayList<>(lastFailExceptions)));
reset();
- throw new ArangoDBException("Cannot contact any host!");
+ throw e;
}
final int index = (current++) % size;
@@ -72,17 +80,19 @@ public Host get(final HostHandle hostHandle, AccessType accessType) {
@Override
public void success() {
- fails = 0;
+ reset();
}
@Override
- public void fail() {
+ public void fail(Exception exception) {
fails++;
+ lastFailExceptions.add(exception);
}
@Override
public void reset() {
fails = 0;
+ lastFailExceptions.clear();
}
@Override
diff --git a/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java b/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
index 854ca0f1c..92187e9ba 100644
--- a/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
+++ b/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
@@ -95,13 +95,13 @@ protected synchronized C connect(final HostHandle hostHandle, final AccessType a
hostHandler.confirm();
if (!connection.isOpen()) {
// see https://github.com/arangodb/arangodb-java-driver/issues/384
- hostHandler.fail();
+ hostHandler.fail(new IOException("The connection is closed."));
host = hostHandler.get(hostHandle, accessType);
continue;
}
return connection;
} catch (final IOException e) {
- hostHandler.fail();
+ hostHandler.fail(e);
if (hostHandle != null && hostHandle.getHost() != null) {
hostHandle.setHost(null);
}
diff --git a/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java b/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
index 3691f65b0..f433194bf 100644
--- a/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
+++ b/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
@@ -143,7 +143,7 @@ protected Response execute(final Request request, final VstConnectionSync connec
final String location = e.getLocation();
final HostDescription redirectHost = HostUtils.createFromLocation(location);
hostHandler.closeCurrentOnError();
- hostHandler.fail();
+ hostHandler.fail(e);
return execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1);
}
}
diff --git a/src/test/java/com/arangodb/ArangoSslTest.java b/src/test/java/com/arangodb/ArangoSslTest.java
index 77d1f7409..1c8af8074 100644
--- a/src/test/java/com/arangodb/ArangoSslTest.java
+++ b/src/test/java/com/arangodb/ArangoSslTest.java
@@ -29,10 +29,10 @@
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
+import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.fail;
/**
@@ -84,7 +84,9 @@ public void connectWithoutValidSslContext() {
arangoDB.getVersion();
fail("this should fail");
} catch (final ArangoDBException ex) {
- assertThat(ex.getCause() instanceof SSLHandshakeException, is(true));
+ assertThat(ex.getCause(), is(instanceOf(ArangoDBMultipleException.class)));
+ List exceptions = ((ArangoDBMultipleException) ex.getCause()).getExceptions();
+ exceptions.forEach(e -> assertThat(e, is(instanceOf(SSLHandshakeException.class))));
}
}
diff --git a/src/test/java/com/arangodb/internal/HostHandlerTest.java b/src/test/java/com/arangodb/internal/HostHandlerTest.java
index 077634ed8..7a4d22db0 100644
--- a/src/test/java/com/arangodb/internal/HostHandlerTest.java
+++ b/src/test/java/com/arangodb/internal/HostHandlerTest.java
@@ -21,10 +21,13 @@
package com.arangodb.internal;
import com.arangodb.ArangoDBException;
+import com.arangodb.ArangoDBMultipleException;
import com.arangodb.internal.net.*;
import com.arangodb.util.ArangoSerialization;
import org.junit.Test;
+import java.util.List;
+
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@@ -76,10 +79,10 @@ public void init(ArangoExecutorSync executor, ArangoSerialization arangoSerializ
};
@Test
- public void fallbachHostHandlerSingleHost() {
+ public void fallbackHostHandlerSingleHost() {
final HostHandler handler = new FallbackHostHandler(SINGLE_HOST);
assertThat(handler.get(null, null), is(HOST_0));
- handler.fail();
+ handler.fail(new RuntimeException());
assertThat(handler.get(null, null), is(HOST_0));
}
@@ -88,19 +91,27 @@ public void fallbackHostHandlerMultipleHosts() {
final HostHandler handler = new FallbackHostHandler(MULTIPLE_HOSTS);
for (int i = 0; i < 3; i++) {
assertThat(handler.get(null, null), is(HOST_0));
- handler.fail();
+ handler.fail(new RuntimeException("HOST_0 failed"));
assertThat(handler.get(null, null), is(HOST_1));
- handler.fail();
+ handler.fail(new RuntimeException("HOST_1 failed"));
assertThat(handler.get(null, null), is(HOST_2));
+ handler.fail(new RuntimeException("HOST_2 failed"));
if (i < 2) {
- handler.fail();
assertThat(handler.get(null, null), is(HOST_0));
} else {
- handler.fail();
try {
handler.get(null, null);
fail();
- } catch (ArangoDBException ignored) {
+ } catch (ArangoDBException e) {
+ assertThat(e.getCause(), is(notNullValue()));
+ assertThat(e.getCause(), is(instanceOf(ArangoDBMultipleException.class)));
+ List exceptions = ((ArangoDBMultipleException) e.getCause()).getExceptions();
+ assertThat(exceptions.get(0), is(instanceOf(RuntimeException.class)));
+ assertThat(exceptions.get(0).getMessage(), is("HOST_0 failed"));
+ assertThat(exceptions.get(1), is(instanceOf(RuntimeException.class)));
+ assertThat(exceptions.get(1).getMessage(), is("HOST_1 failed"));
+ assertThat(exceptions.get(2), is(instanceOf(RuntimeException.class)));
+ assertThat(exceptions.get(2).getMessage(), is("HOST_2 failed"));
}
}
}
@@ -110,7 +121,7 @@ public void fallbackHostHandlerMultipleHosts() {
public void randomHostHandlerSingleHost() {
final HostHandler handler = new RandomHostHandler(SINGLE_HOST, new FallbackHostHandler(SINGLE_HOST));
assertThat(handler.get(null, null), is(HOST_0));
- handler.fail();
+ handler.fail(new RuntimeException());
assertThat(handler.get(null, null), is(HOST_0));
}
@@ -120,7 +131,7 @@ public void randomHostHandlerMultipeHosts() {
final Host pick0 = handler.get(null, null);
assertThat(pick0, anyOf(is(HOST_0), is(HOST_1), is(HOST_2)));
- handler.fail();
+ handler.fail(new RuntimeException());
final Host pick1 = handler.get(null, null);
assertThat(pick1, anyOf(is(HOST_0), is(HOST_1), is(HOST_2)));
@@ -135,7 +146,7 @@ public void randomHostHandlerMultipeHosts() {
public void roundRobinHostHandlerSingleHost() {
final HostHandler handler = new RoundRobinHostHandler(SINGLE_HOST);
assertThat(handler.get(null, null), is(HOST_0));
- handler.fail();
+ handler.fail(new RuntimeException());
assertThat(handler.get(null, null), is(HOST_0));
}