Repository: tinkerpop
Updated Branches:
  refs/heads/tp32 88d6f7786 -> b510613b1


TINKERPOP-2044 Configurable traversal to validate host connectivity.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/b510613b
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/b510613b
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/b510613b

Branch: refs/heads/tp32
Commit: b510613b10962a3e8d95c3e590b5f58297227d0e
Parents: 88d6f77
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Sep 28 15:54:30 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Oct 4 08:16:44 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../src/reference/gremlin-applications.asciidoc |  1 +
 .../tinkerpop/gremlin/driver/Cluster.java       | 30 ++++++++++++++++++--
 .../gremlin/driver/ConnectionPool.java          |  3 +-
 .../tinkerpop/gremlin/driver/Settings.java      | 15 +++++++---
 .../tinkerpop/gremlin/driver/SettingsTest.java  |  8 ++++--
 .../server/GremlinDriverIntegrateTest.java      | 27 +++++++++++++++++-
 7 files changed, 74 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index c4523d8..f3340cc 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -39,6 +39,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added synchronized `Map` to Gryo 1.0 registrations.
 * Added `Triple` to Gryo 1.0 registrations.
 * Improved escaping of special characters in strings passed to the 
`GroovyTranslator`.
+* Added `Cluster` configuration option to set a custom validation script to 
use to test server connectivity in the Java driver.
 * Improved ability of `GroovyTranslator` to handle more types supported by 
GraphSON.
 * Improved ability of `GroovyTranslator` to handle custom types.
 * Added better internal processing of `Column` in `by(Function)`.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/docs/src/reference/gremlin-applications.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/gremlin-applications.asciidoc 
b/docs/src/reference/gremlin-applications.asciidoc
index 1cd9964..5607667 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -750,6 +750,7 @@ The following table describes the various configuration 
options for the Gremlin
 |connectionPool.sslSkipCertValidation |Configures the `TrustManager` to trust 
all certs without any validation. Should not be used in production.|false
 |connectionPool.trustStore |File location for a SSL Certificate Chain to use 
when SSL is enabled. If this value is not provided and SSL is enabled, the 
default `TrustManager` will be used. |_none_
 |connectionPool.trustStorePassword |The password of the `trustStore` if it is 
password-protected |_none_
+|connectionPool.validationRequest |A script that is used to test server 
connectivity. A good script to use is one that evaluates quickly and returns no 
data. The default simply returns an empty string, but if a graph is required by 
a particular provider, a good traversal might be `g.inject()`. |_''_
 |hosts |The list of hosts that the driver will connect to. |localhost
 |jaasEntry |Sets the `AuthProperties.Property.JAAS_ENTRY` properties for 
authentication to Gremlin Server. |_none_
 |nioPoolSize |Size of the pool for handling request/response operations. 
|available processors

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
----------------------------------------------------------------------
diff --git 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
index 9adaaa1..c090584 100644
--- 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
+++ 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
@@ -25,11 +25,16 @@ import io.netty.handler.ssl.SslContextBuilder;
 import io.netty.handler.ssl.SslProvider;
 import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
 import org.apache.commons.configuration.Configuration;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion;
+import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,10 +65,10 @@ import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 /**
@@ -204,7 +209,8 @@ public final class Cluster {
                 
.maxSimultaneousUsagePerConnection(settings.connectionPool.maxSimultaneousUsagePerConnection)
                 
.minSimultaneousUsagePerConnection(settings.connectionPool.minSimultaneousUsagePerConnection)
                 .maxConnectionPoolSize(settings.connectionPool.maxSize)
-                .minConnectionPoolSize(settings.connectionPool.minSize);
+                .minConnectionPoolSize(settings.connectionPool.minSize)
+                .validationRequest(settings.connectionPool.validationRequest);
 
         if (settings.username != null && settings.password != null)
             builder.credentials(settings.username, settings.password);
@@ -465,6 +471,10 @@ public final class Cluster {
         return manager.authProps;
     }
 
+    RequestMessage.Builder validationRequest() {
+        return manager.validationRequest.get();
+    }
+
     SslContext createSSLContext() throws Exception {
         // if the context is provided then just use that and ignore the other 
settings
         if (manager.sslContextOptional.isPresent())
@@ -575,6 +585,7 @@ public final class Cluster {
         private String trustStore = null;
         private String trustStorePassword = null;
         private String keyStoreType = null;
+        private String validationRequest = "''";
         private List<String> sslEnabledProtocols = new ArrayList<>();
         private List<String> sslCipherSuites = new ArrayList<>();
         private boolean sslSkipCertValidation = false;
@@ -889,6 +900,17 @@ public final class Cluster {
         }
 
         /**
+         * Specify a valid Gremlin script that can be used to test remote 
operations. This script should be designed
+         * to return quickly with the least amount of overhead possible. By 
default, the script sends an empty string.
+         * If the graph does not support that sort of script because it 
requires all scripts to include a reference
+         * to a graph then a good option might be {@code g.inject()}.
+         */
+        public Builder validationRequest(final String script) {
+            validationRequest = script;
+            return this;
+        }
+
+        /**
          * Time in milliseconds to wait before attempting to reconnect to a 
dead host after it has been marked dead.
          *
          * @deprecated As of release 3.2.3, the value of the initial delay is 
now the same as the {@link #reconnectInterval}.
@@ -1020,6 +1042,7 @@ public final class Cluster {
         private final LoadBalancingStrategy loadBalancingStrategy;
         private final AuthProperties authProps;
         private final Optional<SslContext> sslContextOptional;
+        private final Supplier<RequestMessage.Builder> validationRequest;
 
         private final ScheduledThreadPoolExecutor executor;
 
@@ -1066,6 +1089,7 @@ public final class Cluster {
             connectionPoolSettings.sslSkipCertValidation = 
builder.sslSkipCertValidation;
             connectionPoolSettings.keepAliveInterval = 
builder.keepAliveInterval;
             connectionPoolSettings.channelizer = builder.channelizer;
+            connectionPoolSettings.validationRequest = 
builder.validationRequest;
 
             sslContextOptional = Optional.ofNullable(builder.sslContext);
 
@@ -1079,6 +1103,8 @@ public final class Cluster {
             this.executor = new 
ScheduledThreadPoolExecutor(builder.workerPoolSize,
                     new 
BasicThreadFactory.Builder().namingPattern("gremlin-driver-worker-%d").build());
             this.executor.setRemoveOnCancelPolicy(true);
+
+            validationRequest = () -> 
RequestMessage.build(Tokens.OPS_EVAL).add(Tokens.ARGS_GREMLIN, 
builder.validationRequest);
         }
 
         private void validateBuilder(final Builder builder) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
----------------------------------------------------------------------
diff --git 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
index 8d63e13..39ded33 100644
--- 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
+++ 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
@@ -387,7 +387,6 @@ final class ConnectionPool {
 
         // let the load-balancer know that the host is acting poorly
         this.cluster.loadBalancingStrategy().onUnavailable(host);
-
     }
 
     /**
@@ -400,7 +399,7 @@ final class ConnectionPool {
         Connection connection = null;
         try {
             connection = 
borrowConnection(cluster.connectionPoolSettings().maxWaitForConnection, 
TimeUnit.MILLISECONDS);
-            final RequestMessage ping = 
RequestMessage.build(Tokens.OPS_EVAL).add(Tokens.ARGS_GREMLIN, "''").create();
+            final RequestMessage ping = 
client.buildMessage(cluster.validationRequest()).create();
             final CompletableFuture<ResultSet> f = new CompletableFuture<>();
             connection.write(ping, f);
             f.get().all().get();

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
----------------------------------------------------------------------
diff --git 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
index fedd337..c2ae045 100644
--- 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
+++ 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
@@ -243,6 +243,8 @@ final class Settings {
             if (connectionPoolConf.containsKey("keepAliveInterval"))
                 cpSettings.keepAliveInterval = 
connectionPoolConf.getLong("keepAliveInterval");
 
+            if (connectionPoolConf.containsKey("validationRequest"))
+                cpSettings.validationRequest = 
connectionPoolConf.getString("validationRequest");
 
             settings.connectionPool = cpSettings;
         }
@@ -258,28 +260,28 @@ final class Settings {
 
         /**
          * The trusted certificate in PEM format.
-         * @deprecated As of release 3.2.10, replaced by {@link trustStore}
+         * @deprecated As of release 3.2.10, replaced by {@link #trustStore}
          */
         @Deprecated
         public String trustCertChainFile = null;
 
         /**
          * The X.509 certificate chain file in PEM format.
-         * @deprecated As of release 3.2.10, replaced by {@link keyStore}
+         * @deprecated As of release 3.2.10, replaced by {@link #keyStore}
          */
         @Deprecated
         public String keyCertChainFile = null;
 
         /**
          * The PKCS#8 private key file in PEM format.
-         * @deprecated As of release 3.2.10, replaced by {@link keyStore}
+         * @deprecated As of release 3.2.10, replaced by {@link #keyStore}
          */
         @Deprecated
         public String keyFile = null;
 
         /**
          * The password of the {@link #keyFile}, or {@code null} if it's not 
password-protected.
-         * @deprecated As of release 3.2.10, replaced by {@link 
keyStorePassword}
+         * @deprecated As of release 3.2.10, replaced by {@link 
#keyStorePassword}
          */
         @Deprecated
         public String keyPassword = null;
@@ -420,6 +422,11 @@ final class Settings {
         public String channelizer = 
Channelizer.WebSocketChannelizer.class.getName();
 
         /**
+         * A valid Gremlin script that can be used to test remote operations.
+         */
+        public String validationRequest = "''";
+
+        /**
          * @deprecated as of 3.1.1-incubating, and not replaced as this 
property was never implemented internally
          * as the way to establish sessions
          */

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
index 56e0ec8..63c4308 100644
--- 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
+++ 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
@@ -18,7 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 
 import org.apache.commons.configuration.BaseConfiguration;
 import org.apache.commons.configuration.Configuration;
@@ -69,6 +71,7 @@ public class SettingsTest {
         conf.setProperty("connectionPool.reconnectInitialDelay", 1000);
         conf.setProperty("connectionPool.resultIterationBatchSize", 1100);
         conf.setProperty("connectionPool.channelizer", "channelizer0");
+        conf.setProperty("connectionPool.validationRequest", "g.inject()");
 
         final Settings settings = Settings.from(conf);
 
@@ -82,7 +85,7 @@ public class SettingsTest {
         assertEquals(Arrays.asList("255.0.0.1", "255.0.0.2", "255.0.0.3"), 
settings.hosts);
         assertEquals("my.serializers.MySerializer", 
settings.serializer.className);
         assertEquals("thing", settings.serializer.config.get("any"));
-        assertEquals(true, settings.connectionPool.enableSsl);
+        assertThat(settings.connectionPool.enableSsl, is(true));
         assertEquals("X.509", settings.connectionPool.keyCertChainFile);
         assertEquals("PKCS#8", settings.connectionPool.keyFile);
         assertEquals("password1", settings.connectionPool.keyPassword);
@@ -94,7 +97,7 @@ public class SettingsTest {
         assertEquals("password3", settings.connectionPool.trustStorePassword);
         assertEquals(Arrays.asList("TLSv1.1","TLSv1.2"), 
settings.connectionPool.sslEnabledProtocols);
         assertEquals(Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"), 
settings.connectionPool.sslCipherSuites);
-        assertEquals(true, settings.connectionPool.sslSkipCertValidation);
+        assertThat(settings.connectionPool.sslSkipCertValidation, is(true));
         assertEquals(100, settings.connectionPool.minSize);
         assertEquals(200, settings.connectionPool.maxSize);
         assertEquals(300, 
settings.connectionPool.minSimultaneousUsagePerConnection);
@@ -107,5 +110,6 @@ public class SettingsTest {
         assertEquals(1000, settings.connectionPool.reconnectInitialDelay);
         assertEquals(1100, settings.connectionPool.resultIterationBatchSize);
         assertEquals("channelizer0", settings.connectionPool.channelizer);
+        assertEquals("g.inject()", settings.connectionPool.validationRequest);
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b510613b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index c5ff608..1f759d0 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -285,7 +285,7 @@ public class GremlinDriverIntegrateTest extends 
AbstractGremlinServerIntegration
     }
 
     @Test
-    public void shouldEventuallySucceedOnSameServer() throws Exception {
+    public void shouldEventuallySucceedOnSameServerWithDefault() throws 
Exception {
         stopServer();
 
         final Cluster cluster = TestClientFactory.open();
@@ -310,6 +310,31 @@ public class GremlinDriverIntegrateTest extends 
AbstractGremlinServerIntegration
     }
 
     @Test
+    public void shouldEventuallySucceedOnSameServerWithScript() throws 
Exception {
+        stopServer();
+
+        final Cluster cluster = 
TestClientFactory.build().validationRequest("g.inject()").create();
+        final Client client = cluster.connect();
+
+        try {
+            client.submit("1+1").all().join().get(0).getInt();
+            fail("Should not have gone through because the server is not 
running");
+        } catch (Exception i) {
+            final Throwable root = ExceptionUtils.getRootCause(i);
+            assertThat(root, instanceOf(TimeoutException.class));
+        }
+
+        startServer();
+
+        // default reconnect time is 1 second so wait some extra time to be 
sure it has time to try to bring it
+        // back to life
+        TimeUnit.SECONDS.sleep(3);
+        assertEquals(2, client.submit("1+1").all().join().get(0).getInt());
+
+        cluster.close();
+    }
+
+    @Test
     public void shouldEventuallySucceedWithRoundRobin() throws Exception {
         final String noGremlinServer = "74.125.225.19";
         final Cluster cluster = 
TestClientFactory.build().addContactPoint(noGremlinServer).create();

Reply via email to