Repository: neo4j-contrib/neo4j-jdbc (java)

Issue:
[4.x] BoltDriver does not support `bolt+s` and `bolt+ssc`
This is from a Liquigraph example, using Hikari pool:
```java
Caused by: java.lang.RuntimeException: Driver org.neo4j.jdbc.bolt.BoltDriver claims to not accept jdbcUrl, jdbc:neo4j:bolt+s://HOST

	at com.zaxxer.hikari.util.DriverDataSource.<init>(DriverDataSource.java:110) ~[HikariCP-3.4.5.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.initializeDataSource(PoolBase.java:325) ~[HikariCP-3.4.5.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.<init>(PoolBase.java:114) ~[HikariCP-3.4.5.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:108) ~[HikariCP-3.4.5.jar:na]
```

Same happens with `bolt+ssc`.

I need to double check, but I think the `trust.strategy*` and `encryption` need to be **not** used when using any of the `neo4j` scheme.

Pull request description:
fixes #249: [4.x] BoltDriver does not support bolt+s and bolt+ssc
Implemented the support for `+s` and `+ssc` for both `neo4j` and `bolt` scheme

Patch:
diff --git a/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/bolt/BoltDriver.java b/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/bolt/BoltDriver.java
index 7ecf419b..aab66ba1 100644
--- a/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/bolt/BoltDriver.java
+++ b/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/bolt/BoltDriver.java
@@ -40,7 +40,7 @@ import java.util.Properties;
  */
 public class BoltDriver extends BoltNeo4jDriverImpl {
 
-	public static final String JDBC_BOLT_PREFIX = "bolt";
+	public static final String JDBC_BOLT_PREFIX = "^bolt(\\+s|\\+ssc)?$";
 	private static final BoltDriverCache cache = new BoltDriverCache(params ->
 	{
 		return GraphDatabase.driver(params.getRoutingUris().get(0), params.getAuthToken(), params.getConfig());
diff --git a/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriver.java b/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriver.java
index d54e11e9..b197aff6 100644
--- a/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriver.java
+++ b/neo4j-jdbc-bolt/src/main/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriver.java
@@ -40,8 +40,7 @@ import java.util.Properties;
  */
 public class BoltRoutingNeo4jDriver extends BoltNeo4jDriverImpl {
 
-//    public static final String JDBC_BOLT_ROUTING_PREFIX = "bolt\\+routing";
-    public static final String JDBC_BOLT_ROUTING_PREFIX = "neo4j";
+    public static final String JDBC_BOLT_ROUTING_PREFIX = "^neo4j(\\+s|\\+ssc)?$";
 
     public static final String ROUTING_CONTEXT = "routing";
     public static final String ALTERNATIVE_SERVERS = "servers";
@@ -72,6 +71,11 @@ public class BoltRoutingNeo4jDriver extends BoltNeo4jDriverImpl {
         super(prefix);
     }
 
+    private boolean isSchemaMatchPrefix(String url) {
+        URI uri = URI.create(url);
+        return uri.getScheme().matches(this.getPrefix());
+    }
+
     @Override
     protected Driver getDriver(List<URI> routingUris, Config config, AuthToken authToken, Properties info) throws URISyntaxException {
         return cache.getDriver(routingUris, config, authToken, info);
@@ -80,8 +84,8 @@ public class BoltRoutingNeo4jDriver extends BoltNeo4jDriverImpl {
     @Override
     protected Properties getRoutingContext(String url, Properties properties) {
         Properties props = new Properties();
-        if (url.matches("^" + this.getPrefix() + ".*") && properties.containsKey(ROUTING_CONTEXT)) {
-            List<String> routingParams = null;
+        if (isSchemaMatchPrefix(url) && properties.containsKey(ROUTING_CONTEXT)) {
+            List<String> routingParams;
             if (properties.get(ROUTING_CONTEXT) instanceof String) {
                 routingParams = Arrays.asList(properties.getProperty(ROUTING_CONTEXT));
             } else {
@@ -100,7 +104,7 @@ public class BoltRoutingNeo4jDriver extends BoltNeo4jDriverImpl {
     @Override
     protected String addRoutingPolicy(String url, Properties properties) {
         String boltUrl = url;
-        if (boltUrl.matches("^" + this.getPrefix() + ".*") && properties.containsKey(ROUTING_CONTEXT)) {
+        if (isSchemaMatchPrefix(url) && properties.containsKey(ROUTING_CONTEXT)) {
             boltUrl += "?" + properties.getProperty(ROUTING_CONTEXT).replaceAll(LIST_SEPARATOR, CUSTOM_ROUTING_POLICY_SEPARATOR);
         }
         return boltUrl;
@@ -123,4 +127,9 @@ public class BoltRoutingNeo4jDriver extends BoltNeo4jDriverImpl {
         }
         return routingUris;
     }
+
+    // visible for testing
+    protected static void clearCache() {
+        cache.clear();
+    }
 }


Test patch:
diff --git a/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/bolt/BoltNeo4jDriverUrlTest.java b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/bolt/BoltNeo4jDriverUrlTest.java
new file mode 100644
index 00000000..05cc59b0
--- /dev/null
+++ b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/bolt/BoltNeo4jDriverUrlTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 LARUS Business Automation [http://www.larus-ba.it]
+ * <p>
+ * This file is part of the "LARUS Integration Framework for Neo4j".
+ * <p>
+ * The "LARUS Integration Framework for Neo4j" is licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * <p>
+ * Created on 23/02/16
+ */
+package org.neo4j.jdbc.bolt;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+import org.neo4j.driver.AuthTokens;
+import org.neo4j.driver.Config;
+import org.neo4j.driver.GraphDatabase;
+import org.neo4j.jdbc.Neo4jDriver;
+import org.neo4j.jdbc.bolt.utils.Mocker;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+
+/**
+ * @author AgileLARUS
+ * @since 3.0.0
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({GraphDatabase.class, Config.TrustStrategy.class, AuthTokens.class})
+@PowerMockRunnerDelegate(Parameterized.class)
+public class BoltNeo4jDriverUrlTest {
+
+	@Rule public ExpectedException expectedEx = ExpectedException.none();
+
+	@Parameterized.Parameter
+	public String completeValidUrl;
+
+	private static org.neo4j.driver.Driver mockedDriver;
+
+	@Parameterized.Parameters
+	public static Iterable<? extends Object> data() {
+		return Arrays.asList("jdbc:neo4j:bolt://test", "jdbc:neo4j:bolt+s://test", "jdbc:neo4j:bolt+ssc://test");
+	}
+
+	@BeforeClass
+	public static void initialize() {
+		mockedDriver = Mocker.mockDriver();
+	}
+
+	@Before
+	public void prepare() {
+		BoltDriver.clearCache();
+	}
+
+	private String getBoltUrl() {
+		return completeValidUrl.replace("jdbc:neo4j:", "");
+	}
+
+	private String getPrefix() {
+		return completeValidUrl.split("://")[0];
+	}
+
+	@Test public void shouldConnectCreateConnection() throws SQLException, URISyntaxException {
+		PowerMockito.mockStatic(GraphDatabase.class);
+		Mockito.when(GraphDatabase.driver(Mockito.eq(new URI(getBoltUrl())), Mockito.eq(AuthTokens.none()), any(Config.class))).thenReturn(mockedDriver);
+
+		Neo4jDriver driver = new BoltDriver();
+		Connection connection = driver.connect(completeValidUrl, null);
+		assertNotNull(connection);
+	}
+
+	@Test public void shouldAcceptURL() throws SQLException {
+		Neo4jDriver driver = new BoltDriver();
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://192.168.0.1:7687"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://192.168.0.1:7687/"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?routing:policy=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687,localhost:7688,localhost:7689/?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?noSsl&routing:region=europe&country=it&routing:servers=localhost:7688,localhost:7689"));
+	}
+}
diff --git a/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverTest.java b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverTest.java
index 628b4b9d..53c4c8de 100644
--- a/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverTest.java
+++ b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverTest.java
@@ -45,24 +45,6 @@ public class BoltRoutingNeo4jDriverTest {
 
 	@Rule public ExpectedException expectedEx = ExpectedException.none();
 
-	/*------------------------------*/
-	/*          acceptsURL          */
-	/*------------------------------*/
-	@Test public void shouldAcceptURL() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, SQLException {
-		Neo4jDriver driver = new BoltRoutingNeo4jDriver();
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687/"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://192.168.0.1:7687"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://192.168.0.1:7687/"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687?routing:policy=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687?routing:region=europe&country=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687/?routing:region=europe&country=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687?noSsl&routing:region=europe&country=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687/?noSsl&routing:region=europe&country=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687,localhost:7688,localhost:7689/?noSsl&routing:region=europe&country=it"));
-		assertTrue(driver.acceptsURL("jdbc:neo4j:neo4j://localhost:7687/?noSsl&routing:region=europe&country=it&routing:servers=localhost:7688,localhost:7689"));
-	}
-
 	@Test public void shoulNotAcceptURL() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, SQLException {
 		Neo4jDriver driver = new BoltRoutingNeo4jDriver();
 		assertFalse(driver.acceptsURL("jdbc:neo4j:http://localhost:7687"));
diff --git a/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverUrlTest.java b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverUrlTest.java
new file mode 100644
index 00000000..7607ee6c
--- /dev/null
+++ b/neo4j-jdbc-bolt/src/test/java/org/neo4j/jdbc/boltrouting/BoltRoutingNeo4jDriverUrlTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 LARUS Business Automation [http://www.larus-ba.it]
+ * <p>
+ * This file is part of the "LARUS Integration Framework for Neo4j".
+ * <p>
+ * The "LARUS Integration Framework for Neo4j" is licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * <p>
+ * Created on 23/02/16
+ */
+package org.neo4j.jdbc.boltrouting;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+import org.neo4j.driver.AuthTokens;
+import org.neo4j.driver.Config;
+import org.neo4j.driver.GraphDatabase;
+import org.neo4j.jdbc.Neo4jDriver;
+import org.neo4j.jdbc.bolt.utils.Mocker;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+
+/**
+ * @author AgileLARUS
+ * @since 3.0.0
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({GraphDatabase.class, Config.TrustStrategy.class, AuthTokens.class})
+@PowerMockRunnerDelegate(Parameterized.class)
+public class BoltRoutingNeo4jDriverUrlTest {
+
+	@Rule public ExpectedException expectedEx = ExpectedException.none();
+
+	@Parameterized.Parameter
+	public String completeValidUrl;
+
+	private static org.neo4j.driver.Driver mockedDriver;
+
+	@Parameterized.Parameters
+	public static Iterable<? extends Object> data() {
+		return Arrays.asList("jdbc:neo4j:neo4j://test", "jdbc:neo4j:neo4j+s://test", "jdbc:neo4j:neo4j+ssc://test");
+	}
+
+	@BeforeClass
+	public static void initialize() {
+		mockedDriver = Mocker.mockDriver();
+	}
+
+	@Before
+	public void prepare() {
+		BoltRoutingNeo4jDriver.clearCache();
+	}
+
+	private String getBoltUrl() {
+		return completeValidUrl.replace("jdbc:neo4j:", "");
+	}
+
+	private String getPrefix() {
+		return completeValidUrl.split("://")[0];
+	}
+
+	@Test public void shouldConnectCreateConnection() throws SQLException, URISyntaxException {
+		PowerMockito.mockStatic(GraphDatabase.class);
+		Mockito.when(GraphDatabase.routingDriver(Mockito.eq(Arrays.asList(new URI(getBoltUrl()))), Mockito.eq(AuthTokens.none()), any(Config.class))).thenReturn(mockedDriver);
+
+		Neo4jDriver driver = new BoltRoutingNeo4jDriver();
+		Connection connection = driver.connect(completeValidUrl, null);
+		assertNotNull(connection);
+	}
+
+	@Test public void shouldAcceptURL() throws SQLException {
+		Neo4jDriver driver = new BoltRoutingNeo4jDriver();
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://192.168.0.1:7687"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://192.168.0.1:7687/"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?routing:policy=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687,localhost:7688,localhost:7689/?noSsl&routing:region=europe&country=it"));
+		assertTrue(driver.acceptsURL(getPrefix() + "://localhost:7687/?noSsl&routing:region=europe&country=it&routing:servers=localhost:7688,localhost:7689"));
+	}
+
+
+}