diff --git a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java
index 4e4c0f380e..6397e826ec 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java
@@ -47,10 +47,12 @@
import static java.lang.String.format;
import static org.neo4j.driver.internal.security.SecurityPlan.insecure;
-import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED;
public class DriverFactory
{
+ public static final String BOLT_URI_SCHEME = "bolt";
+ public static final String BOLT_ROUTING_URI_SCHEME = "bolt+routing";
+
public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings routingSettings,
RetrySettings retrySettings, Config config )
{
@@ -86,10 +88,10 @@ private Driver createDriver( URI uri, BoltServerAddress address, ConnectionPool
String scheme = uri.getScheme().toLowerCase();
switch ( scheme )
{
- case "bolt":
+ case BOLT_URI_SCHEME:
assertNoRoutingContext( uri, routingSettings );
return createDirectDriver( address, connectionPool, config, securityPlan, retryLogic );
- case "bolt+routing":
+ case BOLT_ROUTING_URI_SCHEME:
return createRoutingDriver( address, connectionPool, config, routingSettings, securityPlan, retryLogic );
default:
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
diff --git a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java
index 845919544a..2536736638 100644
--- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java
+++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java
@@ -23,6 +23,9 @@
import org.neo4j.driver.internal.DriverFactory;
import org.neo4j.driver.internal.cluster.RoutingSettings;
import org.neo4j.driver.internal.retry.RetrySettings;
+import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
+
+import static org.neo4j.driver.internal.DriverFactory.BOLT_ROUTING_URI_SCHEME;
/**
* Creates {@link Driver drivers}, optionally letting you {@link #driver(URI, Config)} to configure them.
@@ -131,4 +134,46 @@ public static Driver driver( URI uri, AuthToken authToken, Config config )
return new DriverFactory().newInstance( uri, authToken, routingSettings, retrySettings, config );
}
+
+ /**
+ * Try to create a bolt+routing driver from the first available address.
+ * This is wrapper for the {@link #driver} method that finds the first
+ * server to respond positively.
+ *
+ * @param routingUris an {@link Iterable} of server {@link URI}s for Neo4j instances. All given URIs should
+ * have 'bolt+routing' scheme.
+ * @param authToken authentication to use, see {@link AuthTokens}
+ * @param config user defined configuration
+ * @return a new driver instance
+ */
+ public static Driver routingDriver( Iterable routingUris, AuthToken authToken, Config config )
+ {
+ assertRoutingUris( routingUris );
+
+ for ( URI uri : routingUris )
+ {
+ try
+ {
+ return driver( uri, authToken, config );
+ }
+ catch ( ServiceUnavailableException e )
+ {
+ // try the next one
+ }
+ }
+
+ throw new ServiceUnavailableException( "Failed to discover an available server" );
+ }
+
+ private static void assertRoutingUris( Iterable uris )
+ {
+ for ( URI uri : uris )
+ {
+ if ( !BOLT_ROUTING_URI_SCHEME.equals( uri.getScheme() ) )
+ {
+ throw new IllegalArgumentException(
+ "Illegal URI scheme, expected '" + BOLT_ROUTING_URI_SCHEME + "' in '" + uri + "'" );
+ }
+ }
+ }
}
diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/CausalClusteringIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/CausalClusteringIT.java
index 0b502985e7..42ab5ed4fe 100644
--- a/driver/src/test/java/org/neo4j/driver/v1/integration/CausalClusteringIT.java
+++ b/driver/src/test/java/org/neo4j/driver/v1/integration/CausalClusteringIT.java
@@ -22,6 +22,7 @@
import org.junit.Test;
import java.net.URI;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
@@ -66,6 +67,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING;
import static org.neo4j.driver.v1.Values.parameters;
public class CausalClusteringIT
@@ -85,6 +87,16 @@ public void shouldExecuteReadAndWritesWhenDriverSuppliedWithAddressOfLeader() th
assertEquals( 1, count );
}
+ @Test
+ public void shouldExecuteReadAndWritesWhenRouterIsDiscovered() throws Exception
+ {
+ Cluster cluster = clusterRule.getCluster();
+
+ int count = executeWriteAndReadThroughBoltOnFirstAvailableAddress( cluster.anyReadReplica(), cluster.leader() );
+
+ assertEquals( 1, count );
+ }
+
@Test
public void shouldExecuteReadAndWritesWhenDriverSuppliedWithAddressOfFollower() throws Exception
{
@@ -446,6 +458,19 @@ private int executeWriteAndReadThroughBolt( ClusterMember member ) throws Timeou
}
}
+ private int executeWriteAndReadThroughBoltOnFirstAvailableAddress( ClusterMember... members ) throws TimeoutException, InterruptedException
+ {
+ List addresses = new ArrayList<>( members.length );
+ for ( ClusterMember member : members )
+ {
+ addresses.add( member.getRoutingUri() );
+ }
+ try ( Driver driver = discoverDriver( addresses ) )
+ {
+ return inExpirableSession( driver, createWritableSession( null ), executeWriteAndRead() );
+ }
+ }
+
private Function createSession()
{
return new Function()
@@ -592,6 +617,15 @@ public Logger getLog( String name )
return GraphDatabase.driver( boltUri, clusterRule.getDefaultAuthToken(), config );
}
+ private Driver discoverDriver( List routingUris )
+ {
+ Config config = Config.build()
+ .withLogging( DEV_NULL_LOGGING )
+ .toConfig();
+
+ return GraphDatabase.routingDriver( routingUris, clusterRule.getDefaultAuthToken(), config );
+ }
+
private static void createNodesInDifferentThreads( int count, final Driver driver ) throws Exception
{
final CountDownLatch beforeRunLatch = new CountDownLatch( count );
diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/cc/LocalOrRemoteClusterRule.java b/driver/src/test/java/org/neo4j/driver/v1/util/cc/LocalOrRemoteClusterRule.java
index 68e51bb2b4..be28cc49b5 100644
--- a/driver/src/test/java/org/neo4j/driver/v1/util/cc/LocalOrRemoteClusterRule.java
+++ b/driver/src/test/java/org/neo4j/driver/v1/util/cc/LocalOrRemoteClusterRule.java
@@ -25,6 +25,8 @@
import org.neo4j.driver.v1.AuthToken;
import org.neo4j.driver.v1.AuthTokens;
+import static org.neo4j.driver.internal.DriverFactory.BOLT_ROUTING_URI_SCHEME;
+
public class LocalOrRemoteClusterRule extends ExternalResource
{
private static final String CLUSTER_URI_SYSTEM_PROPERTY_NAME = "externalClusterUri";
@@ -88,7 +90,7 @@ private static void assertValidSystemPropertiesDefined()
"Both cluster uri and 'neo4j' user password system properties should be set. " +
"Uri: '" + uri + "', Password: '" + password + "'" );
}
- if ( uri != null && !"bolt+routing".equals( uri.getScheme() ) )
+ if ( uri != null && !BOLT_ROUTING_URI_SCHEME.equals( uri.getScheme() ) )
{
throw new IllegalStateException( "CLuster uri should have bolt+routing scheme: '" + uri + "'" );
}