From 862f263a671af0346b34d6cb44f85816a426af3c Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 11 Jul 2016 13:38:06 +0100 Subject: [PATCH 01/10] Relegated Config to helper class only --- .../neo4j/driver/internal/InternalDriver.java | 30 +++--- .../connector/socket/SSLContextFactory.java | 81 ---------------- .../connector/socket/SocketClient.java | 39 ++------ .../connector/socket/SocketConnection.java | 9 +- .../connector/socket/SocketConnector.java | 14 ++- .../internal/pool/InternalConnectionPool.java | 28 +++--- .../driver/internal/pool/PoolSettings.java | 58 ++++++++++++ .../pool/PooledConnectionReleaseConsumer.java | 5 +- .../{auth => security}/InternalAuthToken.java | 5 +- .../internal/security/SecurityPlan.java | 94 +++++++++++++++++++ .../socket => security}/TLSSocketChannel.java | 25 +++-- .../TrustOnFirstUseTrustManager.java | 2 +- .../neo4j/driver/internal/spi/Connector.java | 10 +- .../java/org/neo4j/driver/v1/AuthTokens.java | 4 +- .../main/java/org/neo4j/driver/v1/Config.java | 5 +- .../main/java/org/neo4j/driver/v1/Driver.java | 1 - .../org/neo4j/driver/v1/GraphDatabase.java | 70 +++++++++++++- .../connector/socket/SocketClientTest.java | 7 +- .../pool/ConnectionInvalidationTest.java | 21 +++-- .../pool/InternalConnectionPoolTest.java | 12 ++- .../internal/pool/PooledConnectionTest.java | 10 +- .../TrustOnFirstUseTrustManagerTest.java | 5 +- .../driver/v1/integration/CredentialsIT.java | 2 +- .../driver/v1/integration/SocketClientIT.java | 10 +- .../TLSSocketChannelFragmentationIT.java | 2 +- .../v1/integration/TLSSocketChannelIT.java | 38 ++++---- .../neo4j/driver/v1/tck/DriverAuthSteps.java | 2 +- 27 files changed, 365 insertions(+), 224 deletions(-) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/connector/socket/SSLContextFactory.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java rename driver/src/main/java/org/neo4j/driver/internal/{auth => security}/InternalAuthToken.java (97%) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => security}/TLSSocketChannel.java (95%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => security}/TrustOnFirstUseTrustManager.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => security}/TrustOnFirstUseTrustManagerTest.java (95%) diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java index 6a2f0b96db..7f23eea78f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java @@ -21,38 +21,34 @@ import java.net.URI; import org.neo4j.driver.internal.pool.InternalConnectionPool; +import org.neo4j.driver.internal.pool.PoolSettings; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; -import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; -import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED; -import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL; - public class InternalDriver implements Driver { - private final ConnectionPool connections; private final URI url; - private final Config config; + private final SecurityPlan securityPlan; + private final Logging logging; + private final ConnectionPool connections; - public InternalDriver( URI url, AuthToken authToken, Config config ) + public InternalDriver( URI url, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { this.url = url; - this.connections = new InternalConnectionPool( config, authToken ); - this.config = config; + this.securityPlan = securityPlan; + this.logging = logging; + this.connections = new InternalConnectionPool( securityPlan, poolSettings, logging ); } @Override public boolean isEncrypted() { - - Config.EncryptionLevel encryptionLevel = config.encryptionLevel(); - return encryptionLevel.equals( REQUIRED ) || - ( encryptionLevel.equals( REQUIRED_NON_LOCAL ) && !isLocalHost( url.getHost() ) ); + return securityPlan.requiresEncryption(); } /** @@ -63,12 +59,12 @@ public boolean isEncrypted() @Override public Session session() { - return new InternalSession( connections.acquire( url ), config.logging().getLog( "session" ) ); + return new InternalSession( connections.acquire( url ), logging.getLog( "session" ) ); } /** * Close all the resources assigned to this driver - * @throws Exception any error that might happen when releasing all resources + * @throws Neo4jException any error that might happen when releasing all resources */ public void close() throws Neo4jException { diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SSLContextFactory.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SSLContextFactory.java deleted file mode 100644 index 48d7bfa429..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SSLContextFactory.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal.connector.socket; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - -import org.neo4j.driver.v1.Config; -import org.neo4j.driver.v1.exceptions.ClientException; -import org.neo4j.driver.v1.Logger; - -import static org.neo4j.driver.internal.util.CertificateTool.loadX509Cert; - -class SSLContextFactory -{ - private final String host; - private final int port; - private final Config.TrustStrategy authConfig; - private final Logger logger; - - SSLContextFactory( String host, int port, Config.TrustStrategy authConfig, Logger logger ) - { - this.host = host; - this.port = port; - this.authConfig = authConfig; - this.logger = logger; - } - - public SSLContext create() - throws GeneralSecurityException, IOException - { - SSLContext sslContext = SSLContext.getInstance( "TLS" ); - TrustManager[] trustManagers; - - switch ( authConfig.strategy() ) { - case TRUST_SIGNED_CERTIFICATES: - // A certificate file is specified so we will load the certificates in the file - // Init a in memory TrustedKeyStore - KeyStore trustedKeyStore = KeyStore.getInstance( "JKS" ); - trustedKeyStore.load( null, null ); - - // Load the certs from the file - loadX509Cert( authConfig.certFile(), trustedKeyStore ); - - // Create TrustManager from TrustedKeyStore - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( "SunX509" ); - trustManagerFactory.init( trustedKeyStore ); - trustManagers = trustManagerFactory.getTrustManagers(); - break; - case TRUST_ON_FIRST_USE: - trustManagers = new TrustManager[]{new TrustOnFirstUseTrustManager( host, port, authConfig.certFile(), logger )}; - break; - default: - throw new ClientException( "Unknown TLS authentication strategy: " + authConfig.strategy().name() ); - } - - sslContext.init( new KeyManager[0], trustManagers, null ); - return sslContext; - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java index 758f6b4c29..87e8fa7d58 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java @@ -30,15 +30,15 @@ import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.security.TLSSocketChannel; import org.neo4j.driver.v1.Logger; -import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; import static java.lang.String.format; import static java.nio.ByteOrder.BIG_ENDIAN; import static org.neo4j.driver.internal.connector.socket.SocketUtils.blockingRead; import static org.neo4j.driver.internal.connector.socket.SocketUtils.blockingWrite; -import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; public class SocketClient { @@ -50,8 +50,8 @@ public class SocketClient private final String host; private final int port; + private final SecurityPlan securityPlan; private final Logger logger; - protected final Config config; private SocketProtocol protocol; private MessageFormat.Reader reader; @@ -59,11 +59,11 @@ public class SocketClient private ByteChannel channel; - public SocketClient( String host, int port, Config config, Logger logger ) + public SocketClient( String host, int port, SecurityPlan securityPlan, Logger logger ) { this.host = host; this.port = port; - this.config = config; + this.securityPlan = securityPlan; this.logger = logger; this.channel = null; } @@ -73,7 +73,7 @@ public void start() try { logger.debug( "~~ [CONNECT] %s:%d.", host, port ); - channel = ChannelFactory.create( host, port, config, logger ); + channel = ChannelFactory.create( host, port, securityPlan, logger ); protocol = negotiateProtocol(); reader = protocol.reader(); writer = protocol.writer(); @@ -235,7 +235,7 @@ public String toString() private static class ChannelFactory { - public static ByteChannel create( String host, int port, Config config, Logger logger ) + public static ByteChannel create( String host, int port, SecurityPlan securityPlan, Logger logger ) throws IOException, GeneralSecurityException { SocketChannel soChannel = SocketChannel.open(); @@ -245,32 +245,13 @@ public static ByteChannel create( String host, int port, Config config, Logger l ByteChannel channel; - switch ( config.encryptionLevel() ) + if (securityPlan.requiresEncryption()) { - case REQUIRED: - { - channel = new TLSSocketChannel( host, port, soChannel, logger, config.trustStrategy() ); - break; - } - case REQUIRED_NON_LOCAL: - { - if ( isLocalHost( host ) ) - { - channel = soChannel; - } - else - { - channel = new TLSSocketChannel( host, port, soChannel, logger, config.trustStrategy() ); - } - break; + channel = new TLSSocketChannel( host, port, securityPlan, soChannel, logger ); } - case NONE: + else { channel = soChannel; - break; - } - default: - throw new ClientException( "Unknown TLS Level: " + config.encryptionLevel() ); } if ( logger.isTraceEnabled() ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java index 39b14f97ac..986016d8cd 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java @@ -29,10 +29,11 @@ import org.neo4j.driver.internal.messaging.PullAllMessage; import org.neo4j.driver.internal.messaging.ResetMessage; import org.neo4j.driver.internal.messaging.RunMessage; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.spi.StreamCollector; -import org.neo4j.driver.v1.Config; +import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; @@ -46,9 +47,9 @@ public class SocketConnection implements Connection private final SocketClient socket; - public SocketConnection( String host, int port, Config config ) + public SocketConnection( String host, int port, SecurityPlan securityPlan, Logging logging ) { - Logger logger = config.logging().getLog( String.valueOf( System.currentTimeMillis() ) ); + Logger logger = logging.getLog( String.valueOf( System.currentTimeMillis() ) ); if( logger.isDebugEnabled() ) { @@ -59,7 +60,7 @@ public SocketConnection( String host, int port, Config config ) this.responseHandler = new SocketResponseHandler(); } - this.socket = new SocketClient( host, port, config, logger ); + this.socket = new SocketClient( host, port, securityPlan, logger ); socket.start(); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java index a5c2f3e7f6..ece34b368e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java @@ -24,14 +24,12 @@ import java.util.Map; import org.neo4j.driver.internal.Version; -import org.neo4j.driver.internal.auth.InternalAuthToken; import org.neo4j.driver.internal.connector.ConcurrencyGuardingConnection; +import org.neo4j.driver.internal.security.InternalAuthToken; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; -import org.neo4j.driver.v1.Config; -import org.neo4j.driver.v1.Value; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.AuthTokens; +import org.neo4j.driver.v1.*; import org.neo4j.driver.v1.exceptions.ClientException; public class SocketConnector implements Connector @@ -46,15 +44,15 @@ public boolean supports( String scheme ) } @Override - public Connection connect( URI sessionURI, Config config, AuthToken authToken ) throws ClientException + public Connection connect( URI sessionURI, SecurityPlan securityPlan, Logging logging ) throws ClientException { int port = sessionURI.getPort() == -1 ? DEFAULT_PORT : sessionURI.getPort(); - Connection conn = new SocketConnection( sessionURI.getHost(), port, config ); + Connection conn = new SocketConnection( sessionURI.getHost(), port, securityPlan, logging ); // Because SocketConnection is not thread safe, wrap it in this guard // to ensure concurrent access leads causes application errors conn = new ConcurrencyGuardingConnection( conn ); - conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( authToken ) ); + conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( securityPlan.authToken() ) ); return conn; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java index d7744bdacc..b77e320687 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java @@ -30,12 +30,12 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.neo4j.driver.internal.connector.socket.SocketConnector; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.Config; +import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; @@ -65,24 +65,26 @@ public class InternalConnectionPool implements ConnectionPool */ private final ConcurrentHashMap> pools = new ConcurrentHashMap<>(); - private final AuthToken authToken; + private final SecurityPlan securityPlan; private final Clock clock; - private final Config config; + private final PoolSettings poolSettings; + private final Logging logging; /** Shutdown flag */ private final AtomicBoolean stopped = new AtomicBoolean( false ); - public InternalConnectionPool( Config config, AuthToken authToken ) + public InternalConnectionPool( SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { - this( loadConnectors(), Clock.SYSTEM, config, authToken); + this( loadConnectors(), Clock.SYSTEM, securityPlan, poolSettings, logging ); } - public InternalConnectionPool( Collection conns, Clock clock, Config config, - AuthToken authToken ) + public InternalConnectionPool( Collection conns, Clock clock, SecurityPlan securityPlan, + PoolSettings poolSettings, Logging logging ) { - this.authToken = authToken; - this.config = config; + this.securityPlan = securityPlan; this.clock = clock; + this.poolSettings = poolSettings; + this.logging = logging; for ( Connector connector : conns ) { for ( String s : connector.supportedSchemes() ) @@ -110,8 +112,8 @@ public Connection acquire( URI sessionURI ) format( "Unsupported URI scheme: '%s' in url: '%s'. Supported transports are: '%s'.", sessionURI.getScheme(), sessionURI, connectorSchemes() ) ); } - conn = new PooledConnection(connector.connect( sessionURI, config, authToken ), new - PooledConnectionReleaseConsumer( connections, stopped, config ), clock); + conn = new PooledConnection(connector.connect( sessionURI, securityPlan, logging ), new + PooledConnectionReleaseConsumer( connections, stopped, poolSettings ), clock); } conn.updateUsageTimestamp(); return conn; @@ -122,7 +124,7 @@ private BlockingQueue pool( URI sessionURI ) BlockingQueue pool = pools.get( sessionURI ); if ( pool == null ) { - pool = new LinkedBlockingQueue<>(config.maxIdleConnectionPoolSize()); + pool = new LinkedBlockingQueue<>(poolSettings.maxIdleConnectionPoolSize()); if ( pools.putIfAbsent( sessionURI, pool ) != null ) { // We lost a race to create the pool, dispose of the one we created, and recurse diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java b/driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java new file mode 100644 index 0000000000..1a4dde42eb --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.internal.pool; + +public class PoolSettings +{ + public static PoolSettings defaultSettings() + { + return new PoolSettings( DEFAULT_MAX_IDLE_CONNECTION_POOL_SIZE, DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST ); + } + + public static final int DEFAULT_MAX_IDLE_CONNECTION_POOL_SIZE = 10; + public static final long DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST = 200; + + /** + * Maximum number of idle connections per pool. + */ + private final int maxIdleConnectionPoolSize; + + /** + * Connections that have been idle longer than this threshold will have a ping test performed on them. + */ + private final long idleTimeBeforeConnectionTest; + + public PoolSettings( int maxIdleConnectionPoolSize, long idleTimeBeforeConnectionTest ) + { + this.maxIdleConnectionPoolSize = maxIdleConnectionPoolSize; + this.idleTimeBeforeConnectionTest = idleTimeBeforeConnectionTest; + } + + public int maxIdleConnectionPoolSize() + { + return maxIdleConnectionPoolSize; + } + + public long idleTimeBeforeConnectionTest() + { + return idleTimeBeforeConnectionTest; + } + +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java b/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java index 9292a04e83..a3c002b084 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java @@ -25,7 +25,6 @@ import org.neo4j.driver.internal.spi.StreamCollector; import org.neo4j.driver.internal.util.Consumer; -import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.Value; /** @@ -40,11 +39,11 @@ class PooledConnectionReleaseConsumer implements Consumer private final AtomicBoolean driverStopped; PooledConnectionReleaseConsumer( BlockingQueue connections, AtomicBoolean driverStopped, - Config config ) + PoolSettings poolSettings) { this.connections = connections; this.driverStopped = driverStopped; - this.minIdleBeforeConnectionTest = config.idleTimeBeforeConnectionTest(); + this.minIdleBeforeConnectionTest = poolSettings.idleTimeBeforeConnectionTest(); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java b/driver/src/main/java/org/neo4j/driver/internal/security/InternalAuthToken.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java rename to driver/src/main/java/org/neo4j/driver/internal/security/InternalAuthToken.java index 1de7284bc9..98583cf998 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/InternalAuthToken.java @@ -16,13 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.auth; -import java.util.Map; +package org.neo4j.driver.internal.security; import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.Value; +import java.util.Map; + /** * A simple common token for authentication schemes that easily convert to * an auth token map diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java new file mode 100644 index 0000000000..bf0ca0d3d4 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.internal.security; + +import org.neo4j.driver.v1.*; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import static org.neo4j.driver.internal.util.CertificateTool.loadX509Cert; + +/** + * A SecurityPlan consists of authentication information, + * an encryption on/off flag and an array of TrustManager + * instances that can be used to initialize an SSL context. + */ +public class SecurityPlan +{ + public static SecurityPlan forSignedCertificates( AuthToken authToken, File certFile ) + throws GeneralSecurityException, IOException + { + // A certificate file is specified so we will load the certificates in the file + // Init a in memory TrustedKeyStore + KeyStore trustedKeyStore = KeyStore.getInstance( "JKS" ); + trustedKeyStore.load( null, null ); + + // Load the certs from the file + loadX509Cert( certFile, trustedKeyStore ); + + // Create TrustManager from TrustedKeyStore + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( "SunX509" ); + trustManagerFactory.init( trustedKeyStore ); + return new SecurityPlan( authToken, true, trustManagerFactory.getTrustManagers() ); + } + + public static SecurityPlan forTrustOnFirstUse( AuthToken authToken, File knownHosts, String host, int port, Logger logger ) + throws IOException + { + return new SecurityPlan( authToken, true, new TrustOnFirstUseTrustManager( host, port, knownHosts, logger ) ); + } + + public static SecurityPlan insecure() + { + return new SecurityPlan( AuthTokens.none(), false ); + } + + private final AuthToken authToken; + private final boolean requiresEncryption; + private final TrustManager[] trustManagers; + + public SecurityPlan( AuthToken authToken, boolean requiresEncryption, TrustManager... trustManagers ) + { + this.authToken = authToken; + this.requiresEncryption = requiresEncryption; + this.trustManagers = trustManagers; + } + + public AuthToken authToken() + { + return authToken; + } + + public boolean requiresEncryption() + { + return requiresEncryption; + } + + public TrustManager[] trustManagers() + { + return trustManagers; + } + +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/TLSSocketChannel.java b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java similarity index 95% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/TLSSocketChannel.java rename to driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java index eb9cadbc69..a71ce037f2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/TLSSocketChannel.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java @@ -16,12 +16,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.security; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.security.GeneralSecurityException; +import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -30,7 +31,6 @@ import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.util.BytePrinter; -import org.neo4j.driver.v1.Config.TrustStrategy; import org.neo4j.driver.v1.exceptions.ClientException; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED; @@ -63,12 +63,10 @@ public class TLSSocketChannel implements ByteChannel private static final ByteBuffer DUMMY_BUFFER = ByteBuffer.allocateDirect( 0 ); - public TLSSocketChannel( String host, int port, ByteChannel channel, Logger logger, - TrustStrategy trustStrategy ) + public TLSSocketChannel( String host, int port, SecurityPlan securityPlan, ByteChannel channel, Logger logger ) throws GeneralSecurityException, IOException { - this(channel, logger, - createSSLEngine( host, port, new SSLContextFactory( host, port, trustStrategy, logger ).create() ) ); + this(channel, logger, createSSLEngine( host, port, createSSLContext( securityPlan ) ) ); } @@ -339,6 +337,21 @@ static int bufferCopy( ByteBuffer from, ByteBuffer to ) return maxTransfer; } + /** + * Create an SSLContext based on a given SecurityPlan. + * + * @param securityPlan + * @return + * @throws GeneralSecurityException + * @throws IOException + */ + private static SSLContext createSSLContext( SecurityPlan securityPlan ) throws GeneralSecurityException, IOException + { + SSLContext sslContext = SSLContext.getInstance( "TLS" ); + sslContext.init( new KeyManager[0], securityPlan.trustManagers(), null ); + return sslContext; + } + /** * Create SSLEngine with the SSLContext just created. * @param host diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManager.java b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManager.java rename to driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java index dcafc2b7b5..e55af918e6 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManager.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.security; import java.io.BufferedReader; import java.io.BufferedWriter; diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java index cea1c7f55c..92b074813e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java @@ -21,8 +21,8 @@ import java.net.URI; import java.util.Collection; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.Config; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.exceptions.ClientException; /** @@ -43,11 +43,11 @@ public interface Connector * Establish a connection to a remote listener and attach to the session identified. * * @param sessionURL a URL identifying a remote session - * @param config a configuration for this connection - * @param authToken + * @param securityPlan a security plan for this connection + * @param logging * @return a Connection object */ - Connection connect( URI sessionURL, Config config, AuthToken authToken ) throws ClientException; + Connection connect( URI sessionURL, SecurityPlan securityPlan, Logging logging ) throws ClientException; /** List names of supported schemes, used for error messages and similar signaling to end users. */ Collection supportedSchemes(); diff --git a/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java b/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java index f2cf823d18..edef6b9e92 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java +++ b/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java @@ -18,7 +18,7 @@ */ package org.neo4j.driver.v1; -import org.neo4j.driver.internal.auth.InternalAuthToken; +import org.neo4j.driver.internal.security.InternalAuthToken; import static org.neo4j.driver.v1.Values.parameters; @@ -54,6 +54,6 @@ public static AuthToken basic( String username, String password ) */ public static AuthToken none() { - return new InternalAuthToken( parameters("scheme", "none" ).asMap( Values.ofValue() ) ); + return new InternalAuthToken( parameters( "scheme", "none" ).asMap( Values.ofValue() ) ); } } diff --git a/driver/src/main/java/org/neo4j/driver/v1/Config.java b/driver/src/main/java/org/neo4j/driver/v1/Config.java index ab2b88e73b..da2c8fceec 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Config.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Config.java @@ -22,6 +22,7 @@ import java.util.logging.Level; import org.neo4j.driver.internal.logging.JULogging; +import org.neo4j.driver.internal.pool.PoolSettings; import org.neo4j.driver.v1.util.Immutable; import static java.lang.System.getProperty; @@ -151,8 +152,8 @@ public static class ConfigBuilder { private Logging logging = new JULogging( Level.INFO ); private int connectionPoolSize = 50; - private int maxIdleConnectionPoolSize = 10; - private long idleTimeBeforeConnectionTest = 200; + private int maxIdleConnectionPoolSize = PoolSettings.DEFAULT_MAX_IDLE_CONNECTION_POOL_SIZE; + private long idleTimeBeforeConnectionTest = PoolSettings.DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST; private EncryptionLevel encryptionLevel = EncryptionLevel.REQUIRED_NON_LOCAL; private TrustStrategy trustStrategy = trustOnFirstUse( new File( getProperty( "user.home" ), ".neo4j" + File.separator + "known_hosts" ) ); diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index facb6dd9a0..b158a179fa 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -20,7 +20,6 @@ import java.net.URI; -import org.neo4j.driver.v1.Config.EncryptionLevel; import org.neo4j.driver.v1.exceptions.Neo4jException; /** 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 1cf3f629be..ca469e3c2f 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -18,9 +18,18 @@ */ package org.neo4j.driver.v1; +import java.io.IOException; import java.net.URI; +import java.security.GeneralSecurityException; import org.neo4j.driver.internal.InternalDriver; +import org.neo4j.driver.internal.pool.PoolSettings; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.v1.exceptions.ClientException; + +import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; +import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED; +import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL; /** * Creates {@link Driver drivers}, optionally letting you {@link #driver(URI, Config)} to configure them. @@ -122,9 +131,64 @@ public static Driver driver( String url, AuthToken authToken, Config config ) */ public static Driver driver( URI url, AuthToken authToken, Config config ) { - AuthToken tokenToUse = authToken != null ? authToken: AuthTokens.none(); - Config configToUse = config != null ? config: Config.defaultConfig(); + // Fill in defaults + if (authToken == null) + { + authToken = AuthTokens.none(); + } + if (config == null) + { + config = Config.defaultConfig(); + } + + // Construct security plan + SecurityPlan securityPlan; + try + { + securityPlan = createSecurityPlan( url, authToken, config ); + } + catch ( GeneralSecurityException | IOException ex ) + { + throw new ClientException( "Unable to establish SSL parameters", ex ); + } + + // Establish pool settings + PoolSettings poolSettings = new PoolSettings( + config.maxIdleConnectionPoolSize(), + config.idleTimeBeforeConnectionTest() ); - return new InternalDriver( url, tokenToUse, configToUse ); + // Finally, construct the driver proper + return new InternalDriver( url, securityPlan, poolSettings, config.logging() ); } + + /* + * Establish a complete SecurityPlan based on the details provided for + * driver construction. + */ + private static SecurityPlan createSecurityPlan( URI url, AuthToken authToken, Config config ) + throws GeneralSecurityException, IOException + { + Config.EncryptionLevel encryptionLevel = config.encryptionLevel(); + boolean requiresEncryption = encryptionLevel.equals( REQUIRED ) || + (encryptionLevel.equals( REQUIRED_NON_LOCAL ) && !isLocalHost( url.getHost() )); + + if ( requiresEncryption ) + { + switch ( config.trustStrategy().strategy() ) + { + case TRUST_SIGNED_CERTIFICATES: + return SecurityPlan.forSignedCertificates( authToken, config.trustStrategy().certFile() ); + case TRUST_ON_FIRST_USE: + return SecurityPlan.forTrustOnFirstUse( authToken, config.trustStrategy().certFile(), + url.getHost(), url.getPort(), config.logging().getLog( "session" ) ); + default: + throw new ClientException( "Unknown TLS authentication strategy: " + config.trustStrategy().strategy().name() ); + } + } + else + { + return new SecurityPlan( authToken, false ); + } + } + } diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java index db7b5cd553..cb91352bd8 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java @@ -26,6 +26,7 @@ import org.junit.rules.ExpectedException; import org.neo4j.driver.internal.logging.DevNullLogger; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; @@ -43,10 +44,8 @@ public void testNetworkTimeout() throws Throwable // Given a server that will never reply ServerSocket server = new ServerSocket( 0 ); - // And given we've configured a client with network timeout - int networkTimeout = 100; - SocketClient client = new SocketClient( "localhost", server.getLocalPort(), - Config.defaultConfig(), new DevNullLogger() ); + SecurityPlan securityPlan = SecurityPlan.insecure(); + SocketClient client = new SocketClient( "localhost", server.getLocalPort(), securityPlan, new DevNullLogger() ); // Expect exception.expect( ClientException.class ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java index c164dcb578..aaafbf8f05 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java @@ -66,14 +66,14 @@ public void shouldInvalidateConnectionThatIsOld() throws Throwable // Given a connection that's broken Mockito.doThrow( new ClientException( "That didn't work" ) ) .when( delegate ).run( anyString(), anyMap(), any( StreamCollector.class ) ); - Config config = Config.defaultConfig(); - when( clock.millis() ).thenReturn( 0L, config.idleTimeBeforeConnectionTest() + 1L ); + PoolSettings poolSettings = PoolSettings.defaultSettings(); + when( clock.millis() ).thenReturn( 0L, poolSettings.idleTimeBeforeConnectionTest() + 1L ); PooledConnection conn = new PooledConnection( delegate, Consumers.noOp(), clock ); // When/Then BlockingQueue queue = mock( BlockingQueue.class ); PooledConnectionReleaseConsumer consumer = - new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), config ); + new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), poolSettings ); consumer.accept( conn ); verify( queue, never() ).add( conn ); @@ -87,13 +87,14 @@ public void shouldNotInvalidateConnectionThatIsNotOld() throws Throwable Mockito.doThrow( new ClientException( "That didn't work" ) ) .when( delegate ).run( anyString(), anyMap(), any( StreamCollector.class ) ); Config config = Config.defaultConfig(); - when( clock.millis() ).thenReturn( 0L, config.idleTimeBeforeConnectionTest() - 1L ); + PoolSettings poolSettings = PoolSettings.defaultSettings(); + when( clock.millis() ).thenReturn( 0L, poolSettings.idleTimeBeforeConnectionTest() - 1L ); PooledConnection conn = new PooledConnection( delegate, Consumers.noOp(), clock ); // When/Then BlockingQueue queue = mock( BlockingQueue.class ); PooledConnectionReleaseConsumer consumer = - new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), config ); + new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), poolSettings ); consumer.accept( conn ); verify( queue ).offer( conn ); @@ -104,13 +105,13 @@ public void shouldInvalidConnectionIfFailedToReset() throws Throwable { // Given a connection that's broken Mockito.doThrow( new ClientException( "That didn't work" ) ).when( delegate ).reset( any( StreamCollector.class ) ); - Config config = Config.defaultConfig(); + PoolSettings poolSettings = PoolSettings.defaultSettings(); PooledConnection conn = new PooledConnection( delegate, Consumers.noOp(), clock ); // When/Then BlockingQueue queue = mock( BlockingQueue.class ); PooledConnectionReleaseConsumer consumer = - new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), config ); + new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), poolSettings ); consumer.accept( conn ); verify( queue, never() ).add( conn ); @@ -157,9 +158,10 @@ private void assertUnrecoverable( Neo4jException exception ) // Then assertTrue( conn.hasUnrecoverableErrors() ); + PoolSettings poolSettings = PoolSettings.defaultSettings(); BlockingQueue queue = mock( BlockingQueue.class ); PooledConnectionReleaseConsumer consumer = - new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), Config.defaultConfig() ); + new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), poolSettings ); consumer.accept( conn ); verify( queue, never() ).offer( conn ); @@ -183,9 +185,10 @@ private void assertRecoverable( Neo4jException exception ) // Then assertFalse( conn.hasUnrecoverableErrors() ); + PoolSettings poolSettings = PoolSettings.defaultSettings(); BlockingQueue queue = mock( BlockingQueue.class ); PooledConnectionReleaseConsumer consumer = - new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), Config.defaultConfig() ); + new PooledConnectionReleaseConsumer( queue, new AtomicBoolean( false ), poolSettings ); consumer.accept( conn ); verify( queue ).offer( conn ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java index 897ec18f3f..2a8fa7c5b6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java @@ -23,12 +23,14 @@ import java.net.URI; import java.util.Collections; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Config; +import org.neo4j.driver.v1.Logging; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.equalTo; @@ -50,8 +52,10 @@ public void shouldAcquireAndRelease() throws Throwable URI uri = URI.create( "bolt://asd" ); Connector connector = connector( "bolt" ); Config config = Config.defaultConfig(); - InternalConnectionPool pool = new InternalConnectionPool( singletonList( connector ), - Clock.SYSTEM, config, AuthTokens.none()); + SecurityPlan securityPlan = SecurityPlan.insecure(); + PoolSettings poolSettings = PoolSettings.defaultSettings(); + InternalConnectionPool pool = new InternalConnectionPool( + singletonList( connector ), Clock.SYSTEM, securityPlan, poolSettings, config.logging() ); Connection conn = pool.acquire( uri ); conn.close(); @@ -60,7 +64,7 @@ public void shouldAcquireAndRelease() throws Throwable Connection acquired = pool.acquire( uri ); // Then - verify( connector, times( 1 ) ).connect( uri, config, AuthTokens.none() ); + verify( connector, times( 1 ) ).connect( uri, securityPlan, config.logging() ); assertThat( acquired, equalTo(conn) ); } @@ -68,7 +72,7 @@ private Connector connector( String scheme ) { Connector mock = mock( Connector.class ); when( mock.supportedSchemes() ).thenReturn( Collections.singletonList( scheme ) ); - when( mock.connect( any( URI.class ), any( Config.class ), any( AuthToken.class ) ) ).thenReturn( mock( + when( mock.connect( any( URI.class ), any( SecurityPlan.class ), any( Logging.class ) ) ).thenReturn( mock( Connection.class ) ); return mock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java index cf0f772686..abce31e5aa 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java @@ -45,7 +45,7 @@ public void shouldDisposeConnectionIfNotValidConnection() throws Throwable Connection conn = mock( Connection.class ); PooledConnectionReleaseConsumer releaseConsumer = new PooledConnectionReleaseConsumer( pool, - new AtomicBoolean( false ), Config.defaultConfig() /*Does not matter what config for this test*/ ) + new AtomicBoolean( false ), PoolSettings.defaultSettings() /*Does not matter what config for this test*/ ) { @Override boolean validConnection( PooledConnection conn ) @@ -81,7 +81,7 @@ public void shouldReturnToThePoolIfIsValidConnectionAndIdlePoolIsNotFull() throw Connection conn = mock( Connection.class ); PooledConnectionReleaseConsumer releaseConsumer = new PooledConnectionReleaseConsumer( pool, - new AtomicBoolean( false ), Config.defaultConfig() /*Does not matter what config for this test*/ ) + new AtomicBoolean( false ), PoolSettings.defaultSettings() /*Does not matter what config for this test*/ ) { @Override boolean validConnection( PooledConnection conn ) @@ -118,7 +118,7 @@ public void shouldDisposeConnectionIfValidConnectionAndIdlePoolIsFull() throws T Connection conn = mock( Connection.class ); PooledConnectionReleaseConsumer releaseConsumer = new PooledConnectionReleaseConsumer( pool, - new AtomicBoolean( false ), Config.defaultConfig() /*Does not matter what config for this test*/ ) + new AtomicBoolean( false ), PoolSettings.defaultSettings() /*Does not matter what config for this test*/ ) { @Override boolean validConnection( PooledConnection conn ) @@ -162,7 +162,7 @@ public void shouldDisposeConnectionIfPoolAlreadyClosed() throws Throwable Connection conn = mock( Connection.class ); PooledConnectionReleaseConsumer releaseConsumer = new PooledConnectionReleaseConsumer( pool, - new AtomicBoolean( true ), Config.defaultConfig() /*Does not matter what config for this test*/ ); + new AtomicBoolean( true ), PoolSettings.defaultSettings() /*Does not matter what config for this test*/ ); PooledConnection pooledConnection = new PooledConnection( conn, releaseConsumer, Clock.SYSTEM ) { @@ -202,7 +202,7 @@ public boolean offer(PooledConnection conn) Connection conn = mock( Connection.class ); PooledConnectionReleaseConsumer releaseConsumer = new PooledConnectionReleaseConsumer( pool, - stopped , Config.defaultConfig() /*Does not matter what config for this test*/ ); + stopped , PoolSettings.defaultSettings() /*Does not matter what config for this test*/ ); PooledConnection pooledConnection = new PooledConnection( conn, releaseConsumer, Clock.SYSTEM ) { diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManagerTest.java b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManagerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java index 813a8d78ed..3b74649c69 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/TrustOnFirstUseTrustManagerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.security; import org.junit.After; import org.junit.Before; @@ -30,6 +30,7 @@ import java.security.cert.X509Certificate; import java.util.Scanner; +import org.neo4j.driver.internal.security.TrustOnFirstUseTrustManager; import org.neo4j.driver.v1.Logger; import static org.junit.Assert.assertEquals; @@ -39,7 +40,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.connector.socket.TrustOnFirstUseTrustManager.fingerprint; +import static org.neo4j.driver.internal.security.TrustOnFirstUseTrustManager.fingerprint; public class TrustOnFirstUseTrustManagerTest { diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java index 65a1d452ff..f6e157b834 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java @@ -23,7 +23,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.neo4j.driver.internal.auth.InternalAuthToken; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.driver.v1.Session; diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java index 363086e37d..927cea0362 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java @@ -23,7 +23,9 @@ import org.junit.Rule; import org.junit.Test; +import java.io.IOException; import java.net.URI; +import java.security.GeneralSecurityException; import java.util.LinkedList; import java.util.Queue; @@ -32,6 +34,8 @@ import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.internal.messaging.InitMessage; import org.neo4j.driver.internal.messaging.Message; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.util.TestNeo4j; @@ -55,11 +59,11 @@ public class SocketClientIT private SocketClient client = null; @Before - public void setup() + public void setup() throws GeneralSecurityException, IOException { URI url = URI.create( neo4j.address() ); - client = new SocketClient( url.getHost(), url.getPort(), Config.defaultConfig(), - new DevNullLogger() ); + SecurityPlan securityPlan = SecurityPlan.insecure(); + client = new SocketClient( url.getHost(), url.getPort(), securityPlan, new DevNullLogger() ); } @After diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelFragmentationIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelFragmentationIT.java index 5773ac3abd..bcb2ae340b 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelFragmentationIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelFragmentationIT.java @@ -44,7 +44,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import org.neo4j.driver.internal.connector.socket.TLSSocketChannel; +import org.neo4j.driver.internal.security.TLSSocketChannel; import org.neo4j.driver.internal.logging.DevNullLogger; import static org.hamcrest.core.IsEqual.equalTo; diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java index a1b9df711b..0a8c85fab9 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java @@ -34,13 +34,11 @@ import java.security.cert.X509Certificate; import javax.net.ssl.SSLHandshakeException; -import org.neo4j.driver.internal.connector.socket.TLSSocketChannel; -import org.neo4j.driver.v1.Logger; +import org.neo4j.driver.internal.logging.DevNullLogger; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.security.TLSSocketChannel; +import org.neo4j.driver.v1.*; import org.neo4j.driver.internal.util.CertificateTool; -import org.neo4j.driver.v1.Config; -import org.neo4j.driver.v1.Driver; -import org.neo4j.driver.v1.GraphDatabase; -import org.neo4j.driver.v1.StatementResult; import org.neo4j.driver.v1.util.CertificateToolTest; import org.neo4j.driver.v1.util.Neo4jRunner; import org.neo4j.driver.v1.util.Neo4jSettings; @@ -52,7 +50,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.neo4j.driver.internal.connector.socket.TrustOnFirstUseTrustManager.fingerprint; +import static org.neo4j.driver.internal.security.TrustOnFirstUseTrustManager.fingerprint; public class TLSSocketChannelIT { @@ -86,8 +84,10 @@ private void performTLSHandshakeUsingKnownCerts( File knownCerts ) throws Throwa channel.connect( new InetSocketAddress( "localhost", 7687 ) ); // When + URI url = URI.create( "localhost:7687" ); + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, url.getHost(), url.getPort(), new DevNullLogger() ); TLSSocketChannel sslChannel = - new TLSSocketChannel( "localhost", 7687, channel, logger, Config.TrustStrategy.trustOnFirstUse( knownCerts ) ); + new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger ); sslChannel.close(); // Then @@ -127,9 +127,11 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable channel.connect( new InetSocketAddress( "localhost", 7687 ) ); // When + URI url = URI.create( "localhost:7687" ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), rootCert ); TLSSocketChannel sslChannel = - new TLSSocketChannel( "localhost", 7687, channel, logger, - Config.TrustStrategy.trustSignedBy( rootCert ) ); + new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger + ); sslChannel.close(); // Then @@ -155,11 +157,12 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa createFakeServerCertPairInKnownCerts( "localhost", 7687, knownCerts ); // When & Then + URI url = URI.create( "localhost:7687" ); + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, url.getHost(), url.getPort(), new DevNullLogger() ); TLSSocketChannel sslChannel = null; try { - sslChannel = new TLSSocketChannel( "localhost", 7687, channel, mock( Logger.class ), - Config.TrustStrategy.trustOnFirstUse( knownCerts ) ); + sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, mock( Logger.class ) ); sslChannel.close(); } catch ( SSLHandshakeException e ) @@ -208,11 +211,12 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr CertificateTool.saveX509Cert( aRandomCert, trustedCertFile ); // When & Then + URI url = URI.create( "localhost:7687" ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), trustedCertFile ); TLSSocketChannel sslChannel = null; try { - sslChannel = new TLSSocketChannel( "localhost", 7687, channel, mock( Logger.class ), - Config.TrustStrategy.trustSignedBy( trustedCertFile ) ); + sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, mock( Logger.class ) ); sslChannel.close(); } catch ( SSLHandshakeException e ) @@ -239,9 +243,9 @@ public void shouldPerformTLSHandshakeWithTheSameTrustedServerCert() throws Throw channel.connect( new InetSocketAddress( "localhost", 7687 ) ); // When - TLSSocketChannel sslChannel = new TLSSocketChannel( "localhost", 7687, channel, logger, - Config.TrustStrategy.trustSignedBy( - Neo4jSettings.DEFAULT_TLS_CERT_FILE ) ); + URI url = URI.create( "localhost:7687" ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), Neo4jSettings.DEFAULT_TLS_CERT_FILE ); + TLSSocketChannel sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger ); sslChannel.close(); // Then diff --git a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java index d126f61d63..9dd41ccf5c 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java +++ b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java @@ -29,7 +29,7 @@ import java.io.IOException; import java.nio.file.Files; -import org.neo4j.driver.internal.auth.InternalAuthToken; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.driver.v1.Session; From b5b81a4680c821c85d62f7d5d6094f316793dbd6 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 11 Jul 2016 16:45:25 +0100 Subject: [PATCH 02/10] BoltServerAddress --- ...{InternalDriver.java => DirectDriver.java} | 17 ++-- .../connector/socket/SocketClient.java | 25 +++-- .../connector/socket/SocketConnection.java | 5 +- .../connector/socket/SocketConnector.java | 8 +- .../internal/pool/InternalConnectionPool.java | 73 +++----------- .../internal/security/SecurityPlan.java | 5 +- .../internal/security/TLSSocketChannel.java | 12 +-- .../security/TrustOnFirstUseTrustManager.java | 5 +- .../driver/internal/spi/ConnectionPool.java | 5 +- .../neo4j/driver/internal/spi/Connector.java | 6 +- .../driver/internal/util/AddressUtil.java | 47 ---------- .../internal/util/BoltServerAddress.java | 94 +++++++++++++++++++ .../org/neo4j/driver/v1/GraphDatabase.java | 68 ++++++++------ .../connector/socket/SocketClientTest.java | 4 +- .../pool/InternalConnectionPoolTest.java | 13 +-- .../TrustOnFirstUseTrustManagerTest.java | 7 +- ...ilTest.java => BoltServerAddressTest.java} | 16 ++-- .../v1/integration/ConnectionPoolIT.java | 2 +- .../driver/v1/integration/CredentialsIT.java | 6 +- .../driver/v1/integration/EncryptionIT.java | 9 +- .../driver/v1/integration/LoadCSVIT.java | 2 +- .../driver/v1/integration/LoggingIT.java | 2 +- .../driver/v1/integration/ServerKilledIT.java | 2 +- .../driver/v1/integration/SessionIT.java | 6 +- .../driver/v1/integration/SocketClientIT.java | 6 +- .../v1/integration/TLSSocketChannelIT.java | 41 ++++---- .../v1/stress/SessionPoolingStressIT.java | 2 +- .../neo4j/driver/v1/tck/DriverAuthSteps.java | 4 +- .../v1/tck/DriverSecurityComplianceSteps.java | 12 +-- .../org/neo4j/driver/v1/util/Neo4jRunner.java | 10 +- .../org/neo4j/driver/v1/util/TestNeo4j.java | 12 ++- 31 files changed, 270 insertions(+), 256 deletions(-) rename driver/src/main/java/org/neo4j/driver/internal/{InternalDriver.java => DirectDriver.java} (77%) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/util/AddressUtil.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java rename driver/src/test/java/org/neo4j/driver/internal/util/{AddressUtilTest.java => BoltServerAddressTest.java} (61%) diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java similarity index 77% rename from driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java rename to driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java index 7f23eea78f..c2b51f8034 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java @@ -18,31 +18,32 @@ */ package org.neo4j.driver.internal; -import java.net.URI; - +import org.neo4j.driver.internal.connector.socket.SocketConnector; import org.neo4j.driver.internal.pool.InternalConnectionPool; import org.neo4j.driver.internal.pool.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; +import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; -public class InternalDriver implements Driver +public class DirectDriver implements Driver { - private final URI url; + private final BoltServerAddress address; private final SecurityPlan securityPlan; private final Logging logging; private final ConnectionPool connections; - public InternalDriver( URI url, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) + public DirectDriver( BoltServerAddress address, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { - this.url = url; + this.address = address; this.securityPlan = securityPlan; this.logging = logging; - this.connections = new InternalConnectionPool( securityPlan, poolSettings, logging ); + this.connections = new InternalConnectionPool( new SocketConnector(), Clock.SYSTEM, securityPlan, poolSettings, logging ); } @Override @@ -59,7 +60,7 @@ public boolean isEncrypted() @Override public Session session() { - return new InternalSession( connections.acquire( url ), logging.getLog( "session" ) ); + return new InternalSession( connections.acquire( address ), logging.getLog( "session" ) ); } /** diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java index 87e8fa7d58..e998838c4b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java @@ -32,6 +32,7 @@ import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.security.TLSSocketChannel; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.v1.exceptions.ClientException; @@ -48,8 +49,7 @@ public class SocketClient private static final int NO_VERSION = 0; private static final int[] SUPPORTED_VERSIONS = new int[]{VERSION1, NO_VERSION, NO_VERSION, NO_VERSION}; - private final String host; - private final int port; + private final BoltServerAddress address; private final SecurityPlan securityPlan; private final Logger logger; @@ -59,10 +59,9 @@ public class SocketClient private ByteChannel channel; - public SocketClient( String host, int port, SecurityPlan securityPlan, Logger logger ) + public SocketClient( BoltServerAddress address, SecurityPlan securityPlan, Logger logger ) { - this.host = host; - this.port = port; + this.address = address; this.securityPlan = securityPlan; this.logger = logger; this.channel = null; @@ -72,8 +71,8 @@ public void start() { try { - logger.debug( "~~ [CONNECT] %s:%d.", host, port ); - channel = ChannelFactory.create( host, port, securityPlan, logger ); + logger.debug( "~~ [CONNECT] %s", address ); + channel = ChannelFactory.create( address, securityPlan, logger ); protocol = negotiateProtocol(); reader = protocol.reader(); writer = protocol.writer(); @@ -81,8 +80,8 @@ public void start() catch ( ConnectException e ) { throw new ClientException( format( - "Unable to connect to '%s' on port %s, ensure the database is running and that there is a " + - "working network connection to it.", host, port ) ); + "Unable to connect to %s, ensure the database is running and that there is a " + + "working network connection to it.", address ) ); } catch ( IOException e ) { @@ -197,7 +196,7 @@ private SocketProtocol negotiateProtocol() throws IOException { throw new ClientException( format( "Failed to establish connection with server. Make sure that you have a server with bolt " + - "enabled on %s:%d", host, port ) ); + "enabled on %s", address ) ); } else { @@ -235,19 +234,19 @@ public String toString() private static class ChannelFactory { - public static ByteChannel create( String host, int port, SecurityPlan securityPlan, Logger logger ) + public static ByteChannel create( BoltServerAddress address, SecurityPlan securityPlan, Logger logger ) throws IOException, GeneralSecurityException { SocketChannel soChannel = SocketChannel.open(); soChannel.setOption( StandardSocketOptions.SO_REUSEADDR, true ); soChannel.setOption( StandardSocketOptions.SO_KEEPALIVE, true ); - soChannel.connect( new InetSocketAddress( host, port ) ); + soChannel.connect( new InetSocketAddress( address.host(), address.port() ) ); ByteChannel channel; if (securityPlan.requiresEncryption()) { - channel = new TLSSocketChannel( host, port, securityPlan, soChannel, logger ); + channel = new TLSSocketChannel( address, securityPlan, soChannel, logger ); } else { diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java index 986016d8cd..fc9b1342b3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java @@ -31,6 +31,7 @@ import org.neo4j.driver.internal.messaging.RunMessage; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.spi.StreamCollector; import org.neo4j.driver.v1.Logging; @@ -47,7 +48,7 @@ public class SocketConnection implements Connection private final SocketClient socket; - public SocketConnection( String host, int port, SecurityPlan securityPlan, Logging logging ) + public SocketConnection( BoltServerAddress address, SecurityPlan securityPlan, Logging logging ) { Logger logger = logging.getLog( String.valueOf( System.currentTimeMillis() ) ); @@ -60,7 +61,7 @@ public SocketConnection( String host, int port, SecurityPlan securityPlan, Loggi this.responseHandler = new SocketResponseHandler(); } - this.socket = new SocketClient( host, port, securityPlan, logger ); + this.socket = new SocketClient( address, securityPlan, logger ); socket.start(); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java index ece34b368e..6407570a62 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java @@ -18,7 +18,6 @@ */ package org.neo4j.driver.internal.connector.socket; -import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -29,13 +28,13 @@ import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.*; import org.neo4j.driver.v1.exceptions.ClientException; public class SocketConnector implements Connector { public static final String SCHEME = "bolt"; - public static final int DEFAULT_PORT = 7687; @Override public boolean supports( String scheme ) @@ -44,10 +43,9 @@ public boolean supports( String scheme ) } @Override - public Connection connect( URI sessionURI, SecurityPlan securityPlan, Logging logging ) throws ClientException + public Connection connect( BoltServerAddress address, SecurityPlan securityPlan, Logging logging ) throws ClientException { - int port = sessionURI.getPort() == -1 ? DEFAULT_PORT : sessionURI.getPort(); - Connection conn = new SocketConnection( sessionURI.getHost(), port, securityPlan, logging ); + Connection conn = new SocketConnection( address, securityPlan, logging ); // Because SocketConnection is not thread safe, wrap it in this guard // to ensure concurrent access leads causes application errors diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java index b77e320687..9a52dd17e2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java @@ -20,27 +20,20 @@ import java.net.URI; import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.ServiceLoader; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.driver.internal.connector.socket.SocketConnector; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.spi.Connector; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.Logging; -import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; -import static java.lang.String.format; - /** * The pool is designed to buffer certain amount of free sessions into session pool. When closing a session, we first * try to return the session into the session pool, however if we failed to return it back, either because the pool @@ -55,16 +48,13 @@ */ public class InternalConnectionPool implements ConnectionPool { - /** - * Map of scheme -> connector, this is what we use to establish new connections. - */ - private final ConcurrentHashMap connectors = new ConcurrentHashMap<>(); /** * Pools, organized by URL. */ - private final ConcurrentHashMap> pools = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> pools = new ConcurrentHashMap<>(); + private final Connector connector; private final SecurityPlan securityPlan; private final Clock clock; private final PoolSettings poolSettings; @@ -73,84 +63,49 @@ public class InternalConnectionPool implements ConnectionPool /** Shutdown flag */ private final AtomicBoolean stopped = new AtomicBoolean( false ); - public InternalConnectionPool( SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) - { - this( loadConnectors(), Clock.SYSTEM, securityPlan, poolSettings, logging ); - } - - public InternalConnectionPool( Collection conns, Clock clock, SecurityPlan securityPlan, + public InternalConnectionPool( Connector connector, Clock clock, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { this.securityPlan = securityPlan; this.clock = clock; this.poolSettings = poolSettings; this.logging = logging; - for ( Connector connector : conns ) - { - for ( String s : connector.supportedSchemes() ) - { - this.connectors.put( s, connector ); - } - } + this.connector = connector; } @Override - public Connection acquire( URI sessionURI ) + public Connection acquire( BoltServerAddress address ) { if ( stopped.get() ) { throw new IllegalStateException( "Pool has been closed, cannot acquire new values." ); } - BlockingQueue connections = pool( sessionURI ); + BlockingQueue connections = pool( address ); PooledConnection conn = connections.poll(); if ( conn == null ) { - Connector connector = connectors.get( sessionURI.getScheme() ); - if ( connector == null ) - { - throw new ClientException( - format( "Unsupported URI scheme: '%s' in url: '%s'. Supported transports are: '%s'.", - sessionURI.getScheme(), sessionURI, connectorSchemes() ) ); - } - conn = new PooledConnection(connector.connect( sessionURI, securityPlan, logging ), new + conn = new PooledConnection(connector.connect( address, securityPlan, logging ), new PooledConnectionReleaseConsumer( connections, stopped, poolSettings ), clock); } conn.updateUsageTimestamp(); return conn; } - private BlockingQueue pool( URI sessionURI ) + private BlockingQueue pool( BoltServerAddress address ) { - BlockingQueue pool = pools.get( sessionURI ); + BlockingQueue pool = pools.get( address ); if ( pool == null ) { pool = new LinkedBlockingQueue<>(poolSettings.maxIdleConnectionPoolSize()); - if ( pools.putIfAbsent( sessionURI, pool ) != null ) + if ( pools.putIfAbsent( address, pool ) != null ) { // We lost a race to create the pool, dispose of the one we created, and recurse - return pool( sessionURI ); + return pool( address ); } } return pool; } - private static Collection loadConnectors() - { - List connectors = new LinkedList<>(); - - // Hard code socket connector - Connector conn = new SocketConnector(); - connectors.add( conn ); - - // Load custom loadConnectors via JSL - ServiceLoader load = ServiceLoader.load( Connector.class ); - for ( Connector connector : load ) - { - connectors.add( connector ); - } - return connectors; - } - @Override public void close() throws Neo4jException { @@ -176,8 +131,4 @@ public void close() throws Neo4jException pools.clear(); } - private String connectorSchemes() - { - return Arrays.toString( connectors.keySet().toArray( new String[connectors.keySet().size()] ) ); - } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java index bf0ca0d3d4..0c14cf5ba4 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java @@ -19,6 +19,7 @@ package org.neo4j.driver.internal.security; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.*; import javax.net.ssl.TrustManager; @@ -54,10 +55,10 @@ public static SecurityPlan forSignedCertificates( AuthToken authToken, File cert return new SecurityPlan( authToken, true, trustManagerFactory.getTrustManagers() ); } - public static SecurityPlan forTrustOnFirstUse( AuthToken authToken, File knownHosts, String host, int port, Logger logger ) + public static SecurityPlan forTrustOnFirstUse( AuthToken authToken, File knownHosts, BoltServerAddress address, Logger logger ) throws IOException { - return new SecurityPlan( authToken, true, new TrustOnFirstUseTrustManager( host, port, knownHosts, logger ) ); + return new SecurityPlan( authToken, true, new TrustOnFirstUseTrustManager( address, knownHosts, logger ) ); } public static SecurityPlan insecure() diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java index a71ce037f2..e196986201 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java @@ -29,6 +29,7 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.util.BytePrinter; import org.neo4j.driver.v1.exceptions.ClientException; @@ -63,10 +64,10 @@ public class TLSSocketChannel implements ByteChannel private static final ByteBuffer DUMMY_BUFFER = ByteBuffer.allocateDirect( 0 ); - public TLSSocketChannel( String host, int port, SecurityPlan securityPlan, ByteChannel channel, Logger logger ) + public TLSSocketChannel( BoltServerAddress address, SecurityPlan securityPlan, ByteChannel channel, Logger logger ) throws GeneralSecurityException, IOException { - this(channel, logger, createSSLEngine( host, port, createSSLContext( securityPlan ) ) ); + this(channel, logger, createSSLEngine( address, createSSLContext( securityPlan ) ) ); } @@ -354,13 +355,12 @@ private static SSLContext createSSLContext( SecurityPlan securityPlan ) throws G /** * Create SSLEngine with the SSLContext just created. - * @param host - * @param port + * @param address * @param sslContext */ - private static SSLEngine createSSLEngine( String host, int port, SSLContext sslContext ) + private static SSLEngine createSSLEngine( BoltServerAddress address, SSLContext sslContext ) { - SSLEngine sslEngine = sslContext.createSSLEngine( host, port ); + SSLEngine sslEngine = sslContext.createSSLEngine( address.host(), address.port() ); sslEngine.setUseClientMode( true ); return sslEngine; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java index e55af918e6..24ce7f20be 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java @@ -30,6 +30,7 @@ import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.util.BytePrinter; @@ -57,10 +58,10 @@ public class TrustOnFirstUseTrustManager implements X509TrustManager /** The known certificate we've registered for this server */ private String fingerprint; - TrustOnFirstUseTrustManager( String host, int port, File knownHosts, Logger logger ) throws IOException + TrustOnFirstUseTrustManager( BoltServerAddress address, File knownHosts, Logger logger ) throws IOException { this.logger = logger; - this.serverId = host + ":" + port; + this.serverId = address.toString(); this.knownHosts = knownHosts; load(); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java index 742d19e7aa..cf3184e594 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java @@ -19,13 +19,14 @@ package org.neo4j.driver.internal.spi; -import java.net.URI; +import org.neo4j.driver.internal.util.BoltServerAddress; public interface ConnectionPool extends AutoCloseable { /** * Acquire a connection - if a live connection exists in the pool, it will be used, otherwise a new connection * is created with an applicable {@link Connector}. + * @param address */ - Connection acquire( URI sessionURI ); + Connection acquire( BoltServerAddress address ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java index 92b074813e..34ba6dbd85 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java @@ -18,10 +18,10 @@ */ package org.neo4j.driver.internal.spi; -import java.net.URI; import java.util.Collection; import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.exceptions.ClientException; @@ -42,12 +42,12 @@ public interface Connector /** * Establish a connection to a remote listener and attach to the session identified. * - * @param sessionURL a URL identifying a remote session + * @param address a URL identifying a remote session * @param securityPlan a security plan for this connection * @param logging * @return a Connection object */ - Connection connect( URI sessionURL, SecurityPlan securityPlan, Logging logging ) throws ClientException; + Connection connect( BoltServerAddress address, SecurityPlan securityPlan, Logging logging ) throws ClientException; /** List names of supported schemes, used for error messages and similar signaling to end users. */ Collection supportedSchemes(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/AddressUtil.java b/driver/src/main/java/org/neo4j/driver/internal/util/AddressUtil.java deleted file mode 100644 index 8e0e0b0eb2..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/util/AddressUtil.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -package org.neo4j.driver.internal.util; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -public class AddressUtil -{ - /** - * Return true if the host provided matches "localhost" or "127.x.x.x". - * - * @param host the host name to test - * @return true if localhost, false otherwise - */ - public static boolean isLocalHost( String host ) - { - try - { - // confirmed to work as desired with both "localhost" and "127.x.x.x" - return InetAddress.getByName( host ).isLoopbackAddress(); - } - catch ( UnknownHostException e ) - { - // if it's unknown, it's not local so we can safely return false - return false; - } - } - -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java b/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java new file mode 100644 index 0000000000..7b0c3b8b9b --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.internal.util; + +import java.net.*; + +import static java.lang.String.format; + +/** + * Holds a host and port pair that denotes a Bolt server address. + */ +public class BoltServerAddress +{ + public static final int DEFAULT_PORT = 7687; + public static final BoltServerAddress LOCAL_DEFAULT = new BoltServerAddress( "localhost", DEFAULT_PORT ); + + public static BoltServerAddress from( URI uri ) + { + int port = uri.getPort(); + if ( port == -1 ) + { + port = DEFAULT_PORT; + } + return new BoltServerAddress( uri.getHost(), port ); + } + + private final String host; + private final int port; + + public BoltServerAddress( String host, int port ) + { + this.host = host; + this.port = port; + } + + public BoltServerAddress( String host ) + { + this( host, DEFAULT_PORT ); + } + + @Override + public String toString() + { + return format( "%s:%d", host, port ); + } + + public String host() + { + return host; + } + + public int port() + { + return port; + } + + /** + * Determine whether or not this address refers to the local machine. This + * will generally be true for "localhost" or "127.x.x.x". + * + * @return true if local, false otherwise + */ + public boolean isLocal() + { + try + { + // confirmed to work as desired with both "localhost" and "127.x.x.x" + return InetAddress.getByName( host ).isLoopbackAddress(); + } + catch ( UnknownHostException e ) + { + // if it's unknown, it's not local so we can safely return false + return false; + } + } + +} 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 ca469e3c2f..df7a39f4b8 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -22,12 +22,13 @@ import java.net.URI; import java.security.GeneralSecurityException; -import org.neo4j.driver.internal.InternalDriver; +import org.neo4j.driver.internal.DirectDriver; import org.neo4j.driver.internal.pool.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.exceptions.ClientException; -import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; +import static java.lang.String.format; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL; @@ -41,97 +42,98 @@ public class GraphDatabase /** * Return a driver for a Neo4j instance with the default configuration settings * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @return a new driver to the database instance specified by the URL */ - public static Driver driver( String url ) + public static Driver driver( String uri ) { - return driver( url, Config.defaultConfig() ); + return driver( uri, Config.defaultConfig() ); } /** * Return a driver for a Neo4j instance with the default configuration settings * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @return a new driver to the database instance specified by the URL */ - public static Driver driver( URI url ) + public static Driver driver( URI uri ) { - return driver( url, Config.defaultConfig() ); + return driver( uri, Config.defaultConfig() ); } /** * Return a driver for a Neo4j instance with custom configuration. * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param config user defined configuration * @return a new driver to the database instance specified by the URL */ - public static Driver driver( URI url, Config config ) + public static Driver driver( URI uri, Config config ) { - return driver( url, AuthTokens.none(), config ); + return driver( uri, AuthTokens.none(), config ); } /** * Return a driver for a Neo4j instance with custom configuration. * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param config user defined configuration * @return a new driver to the database instance specified by the URL */ - public static Driver driver( String url, Config config ) + public static Driver driver( String uri, Config config ) { - return driver( URI.create( url ), config ); + return driver( URI.create( uri ), config ); } /** * Return a driver for a Neo4j instance with the default configuration settings * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param authToken authentication to use, see {@link AuthTokens} * @return a new driver to the database instance specified by the URL */ - public static Driver driver( String url, AuthToken authToken ) + public static Driver driver( String uri, AuthToken authToken ) { - return driver( url, authToken, Config.defaultConfig() ); + return driver( uri, authToken, Config.defaultConfig() ); } /** * Return a driver for a Neo4j instance with the default configuration settings * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param authToken authentication to use, see {@link AuthTokens} * @return a new driver to the database instance specified by the URL */ - public static Driver driver( URI url, AuthToken authToken ) + public static Driver driver( URI uri, AuthToken authToken ) { - return driver( url, authToken, Config.defaultConfig() ); + return driver( uri, authToken, Config.defaultConfig() ); } /** * Return a driver for a Neo4j instance with custom configuration. * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param authToken authentication to use, see {@link AuthTokens} * @param config user defined configuration * @return a new driver to the database instance specified by the URL */ - public static Driver driver( String url, AuthToken authToken, Config config ) + public static Driver driver( String uri, AuthToken authToken, Config config ) { - return driver( URI.create( url ), authToken, config ); + return driver( URI.create( uri ), authToken, config ); } /** * Return a driver for a Neo4j instance with custom configuration. * - * @param url the URL to a Neo4j instance + * @param uri the URL to a Neo4j instance * @param authToken authentication to use, see {@link AuthTokens} * @param config user defined configuration * @return a new driver to the database instance specified by the URL */ - public static Driver driver( URI url, AuthToken authToken, Config config ) + public static Driver driver( URI uri, AuthToken authToken, Config config ) { // Fill in defaults + BoltServerAddress address = BoltServerAddress.from( uri ); if (authToken == null) { authToken = AuthTokens.none(); @@ -145,7 +147,7 @@ public static Driver driver( URI url, AuthToken authToken, Config config ) SecurityPlan securityPlan; try { - securityPlan = createSecurityPlan( url, authToken, config ); + securityPlan = createSecurityPlan( address, authToken, config ); } catch ( GeneralSecurityException | IOException ex ) { @@ -158,19 +160,25 @@ public static Driver driver( URI url, AuthToken authToken, Config config ) config.idleTimeBeforeConnectionTest() ); // Finally, construct the driver proper - return new InternalDriver( url, securityPlan, poolSettings, config.logging() ); + switch ( uri.getScheme() ) + { + case "bolt": + return new DirectDriver( address, securityPlan, poolSettings, config.logging() ); + default: + throw new IllegalArgumentException( format( "Scheme '%s' not supported", uri.getScheme() ) ); + } } /* * Establish a complete SecurityPlan based on the details provided for * driver construction. */ - private static SecurityPlan createSecurityPlan( URI url, AuthToken authToken, Config config ) + private static SecurityPlan createSecurityPlan( BoltServerAddress address, AuthToken authToken, Config config ) throws GeneralSecurityException, IOException { Config.EncryptionLevel encryptionLevel = config.encryptionLevel(); boolean requiresEncryption = encryptionLevel.equals( REQUIRED ) || - (encryptionLevel.equals( REQUIRED_NON_LOCAL ) && !isLocalHost( url.getHost() )); + (encryptionLevel.equals( REQUIRED_NON_LOCAL ) && !address.isLocal() ); if ( requiresEncryption ) { @@ -180,7 +188,7 @@ private static SecurityPlan createSecurityPlan( URI url, AuthToken authToken, Co return SecurityPlan.forSignedCertificates( authToken, config.trustStrategy().certFile() ); case TRUST_ON_FIRST_USE: return SecurityPlan.forTrustOnFirstUse( authToken, config.trustStrategy().certFile(), - url.getHost(), url.getPort(), config.logging().getLog( "session" ) ); + address, config.logging().getLog( "session" ) ); default: throw new ClientException( "Unknown TLS authentication strategy: " + config.trustStrategy().strategy().name() ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java index cb91352bd8..21940a2846 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java @@ -27,6 +27,7 @@ import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; @@ -43,9 +44,10 @@ public void testNetworkTimeout() throws Throwable { // Given a server that will never reply ServerSocket server = new ServerSocket( 0 ); + BoltServerAddress address = new BoltServerAddress( "localhost", server.getLocalPort() ); SecurityPlan securityPlan = SecurityPlan.insecure(); - SocketClient client = new SocketClient( "localhost", server.getLocalPort(), securityPlan, new DevNullLogger() ); + SocketClient client = new SocketClient( address, securityPlan, new DevNullLogger() ); // Expect exception.expect( ClientException.class ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java index 2a8fa7c5b6..64fd0a1d90 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java @@ -26,6 +26,7 @@ import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.AuthTokens; @@ -49,22 +50,22 @@ public class InternalConnectionPoolTest public void shouldAcquireAndRelease() throws Throwable { // Given - URI uri = URI.create( "bolt://asd" ); + BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; Connector connector = connector( "bolt" ); Config config = Config.defaultConfig(); SecurityPlan securityPlan = SecurityPlan.insecure(); PoolSettings poolSettings = PoolSettings.defaultSettings(); InternalConnectionPool pool = new InternalConnectionPool( - singletonList( connector ), Clock.SYSTEM, securityPlan, poolSettings, config.logging() ); + connector, Clock.SYSTEM, securityPlan, poolSettings, config.logging() ); - Connection conn = pool.acquire( uri ); + Connection conn = pool.acquire( address ); conn.close(); // When - Connection acquired = pool.acquire( uri ); + Connection acquired = pool.acquire( address ); // Then - verify( connector, times( 1 ) ).connect( uri, securityPlan, config.logging() ); + verify( connector, times( 1 ) ).connect( address, securityPlan, config.logging() ); assertThat( acquired, equalTo(conn) ); } @@ -72,7 +73,7 @@ private Connector connector( String scheme ) { Connector mock = mock( Connector.class ); when( mock.supportedSchemes() ).thenReturn( Collections.singletonList( scheme ) ); - when( mock.connect( any( URI.class ), any( SecurityPlan.class ), any( Logging.class ) ) ).thenReturn( mock( + when( mock.connect( any( BoltServerAddress.class ), any( SecurityPlan.class ), any( Logging.class ) ) ).thenReturn( mock( Connection.class ) ); return mock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java index 3b74649c69..bdf9fef079 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java @@ -31,6 +31,7 @@ import java.util.Scanner; import org.neo4j.driver.internal.security.TrustOnFirstUseTrustManager; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import static org.junit.Assert.assertEquals; @@ -83,9 +84,10 @@ public void teardown() public void shouldLoadExistingCert() throws Throwable { // Given + BoltServerAddress knownServerAddress = new BoltServerAddress( knownServerIp, knownServerPort ); Logger logger = mock(Logger.class); TrustOnFirstUseTrustManager manager = - new TrustOnFirstUseTrustManager( knownServerIp, knownServerPort, knownCertsFile, logger ); + new TrustOnFirstUseTrustManager( knownServerAddress, knownCertsFile, logger ); X509Certificate wrongCertificate = mock( X509Certificate.class ); when( wrongCertificate.getEncoded() ).thenReturn( "fake certificate".getBytes() ); @@ -109,8 +111,9 @@ public void shouldSaveNewCert() throws Throwable { // Given int newPort = 200; + BoltServerAddress address = new BoltServerAddress( knownServerIp, newPort ); Logger logger = mock(Logger.class); - TrustOnFirstUseTrustManager manager = new TrustOnFirstUseTrustManager( knownServerIp, newPort, knownCertsFile, logger ); + TrustOnFirstUseTrustManager manager = new TrustOnFirstUseTrustManager( address, knownCertsFile, logger ); String fingerprint = fingerprint( knownCertificate ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/AddressUtilTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java similarity index 61% rename from driver/src/test/java/org/neo4j/driver/internal/util/AddressUtilTest.java rename to driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java index 2517c58124..117bbaa698 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/AddressUtilTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java @@ -16,25 +16,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.neo4j.driver.internal.util; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.*; -import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; -public class AddressUtilTest +public class BoltServerAddressTest { @Test public void shouldWorkForVariantsOfLocalHost() throws Exception { - assertThat( isLocalHost( "localhost" ), equalTo( true ) ); - assertThat( isLocalHost( "LocalHost" ), equalTo( true ) ); - assertThat( isLocalHost( "LOCALHOST" ), equalTo( true ) ); - assertThat( isLocalHost( "127.0.0.1" ), equalTo( true ) ); - assertThat( isLocalHost( "127.5.6.7" ), equalTo( true ) ); - assertThat( isLocalHost( "x" ), equalTo( false ) ); + assertThat( new BoltServerAddress( "localhost", 7687 ).isLocal(), equalTo( true ) ); + assertThat( new BoltServerAddress( "LocalHost", 7687 ).isLocal(), equalTo( true ) ); + assertThat( new BoltServerAddress( "LOCALHOST", 7687 ).isLocal(), equalTo( true ) ); + assertThat( new BoltServerAddress( "127.0.0.1", 7687 ).isLocal(), equalTo( true ) ); + assertThat( new BoltServerAddress( "127.5.6.7", 7687 ).isLocal(), equalTo( true ) ); + assertThat( new BoltServerAddress( "x", 7687 ).isLocal(), equalTo( false ) ); } } \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/ConnectionPoolIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/ConnectionPoolIT.java index 177c581f95..24ffa799e2 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/ConnectionPoolIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/ConnectionPoolIT.java @@ -48,7 +48,7 @@ public class ConnectionPoolIT public void shouldRecoverFromDownedServer() throws Throwable { // Given a driver - driver = GraphDatabase.driver( neo4j.address() ); + driver = GraphDatabase.driver( neo4j.uri() ); // and given I'm heavily using it to acquire and release sessions sessionGrabber = new SessionGrabber( driver ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java index f6e157b834..5ffbe1ec37 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java @@ -56,7 +56,7 @@ public void basicCredentialsShouldWork() throws Throwable String password = "secret"; enableAuth( password ); - Driver driver = GraphDatabase.driver( neo4j.address(), + Driver driver = GraphDatabase.driver( neo4j.uri(), basic("neo4j", password ) ); // When @@ -79,7 +79,7 @@ public void shouldGetHelpfulErrorOnInvalidCredentials() throws Throwable exception.expectMessage( "The client is unauthorized due to authentication failure." ); // When - GraphDatabase.driver( neo4j.address(), basic("thisisnotthepassword", password ) ).session(); + GraphDatabase.driver( neo4j.uri(), basic("thisisnotthepassword", password ) ).session(); } private void enableAuth( String password ) throws Exception @@ -88,7 +88,7 @@ private void enableAuth( String password ) throws Exception .updateWith( Neo4jSettings.AUTH_ENABLED, "true" ) .updateWith( Neo4jSettings.DATA_DIR, tempDir.getRoot().getAbsolutePath().replace("\\", "/") )); - Driver setPassword = GraphDatabase.driver( neo4j.address(), new InternalAuthToken( + Driver setPassword = GraphDatabase.driver( neo4j.uri(), new InternalAuthToken( parameters( "scheme", "basic", "principal", "neo4j", diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java index cade5ddbcb..ae270bc9f4 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java @@ -25,7 +25,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.driver.internal.util.AddressUtil.isLocalHost; import static org.neo4j.driver.v1.Config.EncryptionLevel.NONE; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL; @@ -39,7 +38,7 @@ public class EncryptionIT public void shouldOperateWithNoEncryption() throws Exception { // Given - Driver driver = GraphDatabase.driver( neo4j.address(), Config.build().withEncryptionLevel( NONE ).toConfig() ); + Driver driver = GraphDatabase.driver( neo4j.uri(), Config.build().withEncryptionLevel( NONE ).toConfig() ); // Then assertThat( driver.isEncrypted(), equalTo( false ) ); @@ -62,10 +61,10 @@ public void shouldOperateWithNoEncryption() throws Exception public void shouldOperateWithRequiredNonLocalEncryption() throws Exception { // Given - Driver driver = GraphDatabase.driver( neo4j.address(), Config.build().withEncryptionLevel( REQUIRED_NON_LOCAL ).toConfig() ); + Driver driver = GraphDatabase.driver( neo4j.uri(), Config.build().withEncryptionLevel( REQUIRED_NON_LOCAL ).toConfig() ); // Then - assertThat( driver.isEncrypted(), equalTo( !isLocalHost( neo4j.host() ) ) ); + assertThat( driver.isEncrypted(), equalTo( neo4j.address().isLocal() ) ); // When Session session = driver.session(); @@ -85,7 +84,7 @@ public void shouldOperateWithRequiredNonLocalEncryption() throws Exception public void shouldOperateWithRequiredEncryption() throws Exception { // Given - Driver driver = GraphDatabase.driver( neo4j.address(), Config.build().withEncryptionLevel( REQUIRED ).toConfig() ); + Driver driver = GraphDatabase.driver( neo4j.uri(), Config.build().withEncryptionLevel( REQUIRED ).toConfig() ); // Then assertThat( driver.isEncrypted(), equalTo( true ) ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/LoadCSVIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/LoadCSVIT.java index 055825c1e0..fcd2683691 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/LoadCSVIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/LoadCSVIT.java @@ -44,7 +44,7 @@ public class LoadCSVIT public void shouldLoadCSV() throws Throwable { // Given - Driver driver = GraphDatabase.driver( neo4j.address() ); + Driver driver = GraphDatabase.driver( neo4j.uri() ); Session session = driver.session(); String csvFileUrl = createLocalIrisData( session ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/LoggingIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/LoggingIT.java index af2b236e55..a9d5fefeeb 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/LoggingIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/LoggingIT.java @@ -50,7 +50,7 @@ public void logShouldRecordDebugAndTraceInfo() throws Exception Logging logging = mock( Logging.class ); Logger logger = mock( Logger.class ); Driver driver = GraphDatabase.driver( - URI.create( Neo4jRunner.DEFAULT_URL ), + Neo4jRunner.DEFAULT_URI, Config.build().withLogging( logging ).toConfig() ); // When diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/ServerKilledIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/ServerKilledIT.java index 42d657aed3..02e60eec3b 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/ServerKilledIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/ServerKilledIT.java @@ -43,7 +43,7 @@ public class ServerKilledIT public void shouldRecoverFromServerRestart() throws Throwable { // Given - try ( Driver driver = GraphDatabase.driver( Neo4jRunner.DEFAULT_URL ) ) + try ( Driver driver = GraphDatabase.driver( Neo4jRunner.DEFAULT_URI ) ) { Session s1 = driver.session(); Session s2 = driver.session(); diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/SessionIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/SessionIT.java index 495b60915d..4ed59dcbf7 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/SessionIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/SessionIT.java @@ -39,7 +39,7 @@ public class SessionIT public void shouldKnowSessionIsClosed() throws Throwable { // Given - Driver driver = GraphDatabase.driver( neo4j.address() ); + Driver driver = GraphDatabase.driver( neo4j.uri() ); Session session = driver.session(); // When @@ -53,7 +53,7 @@ public void shouldKnowSessionIsClosed() throws Throwable public void shouldHandleNullConfig() throws Throwable { // Given - Driver driver = GraphDatabase.driver( neo4j.address(), AuthTokens.none(), null ); + Driver driver = GraphDatabase.driver( neo4j.uri(), AuthTokens.none(), null ); Session session = driver.session(); // When @@ -69,7 +69,7 @@ public void shouldHandleNullAuthToken() throws Throwable { // Given AuthToken token = null; - Driver driver = GraphDatabase.driver( neo4j.address(), token); + Driver driver = GraphDatabase.driver( neo4j.uri(), token); Session session = driver.session(); // When diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java index 927cea0362..bd28eecdf2 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java @@ -24,7 +24,6 @@ import org.junit.Test; import java.io.IOException; -import java.net.URI; import java.security.GeneralSecurityException; import java.util.LinkedList; import java.util.Queue; @@ -35,8 +34,6 @@ import org.neo4j.driver.internal.messaging.InitMessage; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.v1.AuthTokens; -import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.util.TestNeo4j; @@ -61,9 +58,8 @@ public class SocketClientIT @Before public void setup() throws GeneralSecurityException, IOException { - URI url = URI.create( neo4j.address() ); SecurityPlan securityPlan = SecurityPlan.insecure(); - client = new SocketClient( url.getHost(), url.getPort(), securityPlan, new DevNullLogger() ); + client = new SocketClient( neo4j.address(), securityPlan, new DevNullLogger() ); } @After diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java index 0a8c85fab9..3f4cc3ad5d 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java @@ -37,6 +37,7 @@ import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.security.TLSSocketChannel; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.*; import org.neo4j.driver.internal.util.CertificateTool; import org.neo4j.driver.v1.util.CertificateToolTest; @@ -80,14 +81,15 @@ private void performTLSHandshakeUsingKnownCerts( File knownCerts ) throws Throwa { // Given Logger logger = mock( Logger.class ); + BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( "localhost", 7687 ) ); + channel.connect( new InetSocketAddress( address.host(), address.port() ) ); // When - URI url = URI.create( "localhost:7687" ); - SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, url.getHost(), url.getPort(), new DevNullLogger() ); + + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, address, new DevNullLogger() ); TLSSocketChannel sslChannel = - new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger ); + new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); // Then @@ -100,6 +102,7 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable try { // Given + BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; // Create root certificate File rootCert = folder.newFile( "temp_root_cert.cert" ); File rootKey = folder.newFile( "temp_root_key.key" ); @@ -124,13 +127,12 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable Logger logger = mock( Logger.class ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( "localhost", 7687 ) ); + channel.connect( new InetSocketAddress( address.host(), address.port() ) ); // When - URI url = URI.create( "localhost:7687" ); SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), rootCert ); TLSSocketChannel sslChannel = - new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger + new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); @@ -148,21 +150,21 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwable { // Given + BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( "localhost", 7687 ) ); + channel.connect( new InetSocketAddress( address.host(), address.port() ) ); File knownCerts = File.createTempFile( "neo4j_known_hosts", ".tmp" ); knownCerts.deleteOnExit(); //create a Fake Cert for the server in knownCert - createFakeServerCertPairInKnownCerts( "localhost", 7687, knownCerts ); + createFakeServerCertPairInKnownCerts( address.host(), address.port(), knownCerts ); // When & Then - URI url = URI.create( "localhost:7687" ); - SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, url.getHost(), url.getPort(), new DevNullLogger() ); + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, address, new DevNullLogger() ); TLSSocketChannel sslChannel = null; try { - sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, mock( Logger.class ) ); + sslChannel = new TLSSocketChannel( address, securityPlan, channel, mock( Logger.class ) ); sslChannel.close(); } catch ( SSLHandshakeException e ) @@ -205,18 +207,17 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr Neo4jSettings.CERT_DIR, folder.getRoot().getAbsolutePath().replace("\\", "/") ) ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( "localhost", 7687 ) ); + channel.connect( new InetSocketAddress( neo4j.address().host(), neo4j.address().port() ) ); File trustedCertFile = folder.newFile( "neo4j_trusted_cert.tmp" ); X509Certificate aRandomCert = CertificateToolTest.generateSelfSignedCertificate(); CertificateTool.saveX509Cert( aRandomCert, trustedCertFile ); // When & Then - URI url = URI.create( "localhost:7687" ); SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), trustedCertFile ); TLSSocketChannel sslChannel = null; try { - sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, mock( Logger.class ) ); + sslChannel = new TLSSocketChannel( neo4j.address(), securityPlan, channel, mock( Logger.class ) ); sslChannel.close(); } catch ( SSLHandshakeException e ) @@ -237,15 +238,15 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr @Test public void shouldPerformTLSHandshakeWithTheSameTrustedServerCert() throws Throwable { - + BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; Logger logger = mock( Logger.class ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( "localhost", 7687 ) ); + channel.connect( new InetSocketAddress( address.host(), address.port() ) ); // When URI url = URI.create( "localhost:7687" ); SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), Neo4jSettings.DEFAULT_TLS_CERT_FILE ); - TLSSocketChannel sslChannel = new TLSSocketChannel( url.getHost(), url.getPort(), securityPlan, channel, logger ); + TLSSocketChannel sslChannel = new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); // Then @@ -258,9 +259,7 @@ public void shouldEstablishTLSConnection() throws Throwable Config config = Config.build().withEncryptionLevel( Config.EncryptionLevel.REQUIRED ).toConfig(); - Driver driver = GraphDatabase.driver( - URI.create( Neo4jRunner.DEFAULT_URL ), - config ); + Driver driver = GraphDatabase.driver( Neo4jRunner.DEFAULT_URI, config ); StatementResult result = driver.session().run( "RETURN 1" ); assertEquals( 1, result.next().get( 0 ).asInt() ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/stress/SessionPoolingStressIT.java b/driver/src/test/java/org/neo4j/driver/v1/stress/SessionPoolingStressIT.java index 64dd5727a7..4644f8a5dc 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/stress/SessionPoolingStressIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/stress/SessionPoolingStressIT.java @@ -52,7 +52,7 @@ public class SessionPoolingStressIT @Test public void shouldWorkFine() throws InterruptedException { - Driver driver = driver( neo4j.address(), + Driver driver = driver( neo4j.uri(), Config.build() .withEncryptionLevel( Config.EncryptionLevel.NONE ) .withMaxSessions( N_THREADS ).toConfig() ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java index 9dd41ccf5c..30dee1d468 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java +++ b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverAuthSteps.java @@ -92,7 +92,7 @@ public void aDriverIsConfiguredWithAuthEnabledAndTheWrongPasswordIsProvided() th { driver = configureCredentials( "neo4j", "neo4j", "password" ); driver.close(); - driver = GraphDatabase.driver( neo4j.address(), new InternalAuthToken( + driver = GraphDatabase.driver( neo4j.uri(), new InternalAuthToken( parameters( "scheme", "basic", "principal", "neo4j", @@ -125,7 +125,7 @@ private Driver configureCredentials( String name, String oldPassword, String new .updateWith( Neo4jSettings.AUTH_ENABLED, "true" ) .updateWith( Neo4jSettings.DATA_DIR, tempDir.getAbsolutePath().replace("\\", "/") )); - Driver driver = GraphDatabase.driver( neo4j.address(), new InternalAuthToken( + Driver driver = GraphDatabase.driver( neo4j.uri(), new InternalAuthToken( parameters( "scheme", "basic", "principal", name, diff --git a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverSecurityComplianceSteps.java b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverSecurityComplianceSteps.java index 3d4f886042..21a8691892 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/tck/DriverSecurityComplianceSteps.java +++ b/driver/src/test/java/org/neo4j/driver/v1/tck/DriverSecurityComplianceSteps.java @@ -69,7 +69,7 @@ public void firstUseConnect() throws Throwable { knownHostsFile = tempFile( "known_hosts", ".tmp" ); driver = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustOnFirstUse( knownHostsFile ) ).toConfig() ); @@ -97,7 +97,7 @@ public void aRunningNeoJDatabaseThatIHaveConnectedTo() throws Throwable public void iConnectViaATlsEnabledTransportAgain() throws Throwable { driver = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustOnFirstUse( knownHostsFile ) ).toConfig() ); } @@ -158,7 +158,7 @@ public void twoDriversWithDifferentKnownHostsFiles() throws Throwable File tempFile = tempFile( "known_hosts", ".tmp" ); driverKitten = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustOnFirstUse( tempFile ) ).toConfig() ); } @@ -197,7 +197,7 @@ public void aRunningNeo4jDatabaseUsingACertificateSignedByTheSameTrustedCertific // give root certificate to driver driver = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustSignedBy( rootCert ) ).toConfig() ); @@ -221,7 +221,7 @@ public void iConnectViaATlsEnabledTransport() {} public void aRunningNeo4jDatabaseUsingThatExactTrustedCertificate() { driver = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustSignedBy( Neo4jSettings.DEFAULT_TLS_CERT_FILE ) ).toConfig() ); } @@ -235,7 +235,7 @@ public void aRunningNeo4jDatabaseUsingACertNotSignedByTheTrustedCertificates() t // give root certificate to driver driver = GraphDatabase.driver( - Neo4jRunner.DEFAULT_URL, + Neo4jRunner.DEFAULT_URI, Config.build().withEncryptionLevel( EncryptionLevel.REQUIRED ) .withTrustStrategy( trustSignedBy( cert ) ).toConfig() ); } diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java index 0fc4c44ee7..232f50aa0d 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java @@ -26,7 +26,7 @@ import java.nio.channels.SocketChannel; import java.util.Map; -import org.neo4j.driver.v1.Config; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; @@ -44,7 +44,8 @@ public class Neo4jRunner private static final boolean debug = Boolean.getBoolean( "neo4j.runner.debug" ); public static final String NEORUN_START_ARGS = System.getProperty( "neorun.start.args" ); - public static final String DEFAULT_URL = "bolt://localhost:7687"; + public static final URI DEFAULT_URI = URI.create( "bolt://localhost:7687" ); + public static final BoltServerAddress DEFAULT_ADDRESS = BoltServerAddress.from( DEFAULT_URI ); private Driver driver; private Neo4jSettings currentSettings = Neo4jSettings.DEFAULT_SETTINGS; @@ -105,7 +106,7 @@ private void startNeo4j() throws IOException { throw new IOException( "Failed to start neo4j server." ); } - driver = GraphDatabase.driver( DEFAULT_URL /* default encryption REQUIRED_NON_LOCAL */ ); + driver = GraphDatabase.driver( DEFAULT_URI /* default encryption REQUIRED_NON_LOCAL */ ); } public synchronized void stopNeo4j() throws IOException @@ -196,10 +197,9 @@ private ServerStatus serverStatus() { try { - URI uri = URI.create( DEFAULT_URL ); SocketChannel soChannel = SocketChannel.open(); soChannel.setOption( StandardSocketOptions.SO_REUSEADDR, true ); - soChannel.connect( new InetSocketAddress( uri.getHost(), uri.getPort() ) ); + soChannel.connect( new InetSocketAddress( DEFAULT_ADDRESS.host(), DEFAULT_ADDRESS.port() ) ); soChannel.close(); return ServerStatus.ONLINE; } diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java index c324e69aeb..ced9c24b4d 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java @@ -28,6 +28,7 @@ import java.net.URI; import java.net.URL; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Session; @@ -96,14 +97,19 @@ public URL putTmpFile( String prefix, String suffix, String contents ) throws IO return tmpFile.toURI().toURL(); } - public String address() + public URI uri() { - return Neo4jRunner.DEFAULT_URL; + return Neo4jRunner.DEFAULT_URI; + } + + public BoltServerAddress address() + { + return Neo4jRunner.DEFAULT_ADDRESS; } public String host() { - return URI.create( Neo4jRunner.DEFAULT_URL ).getHost(); + return address().host(); } static void clearDatabaseContents( Session session, String reason ) From 1ffe6961e024bc8601b8be300d44fdb8d3c8db4b Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Tue, 12 Jul 2016 15:05:15 +0100 Subject: [PATCH 03/10] Internal refactoring, extra tests and Driver.servers --- .../neo4j/driver/internal/DirectDriver.java | 18 +++- .../connector/socket/SocketClient.java | 4 +- .../connector/socket/SocketConnector.java | 75 -------------- ...ionPool.java => SocketConnectionPool.java} | 53 +++++++--- .../driver/internal/spi/ConnectionPool.java | 8 +- .../neo4j/driver/internal/spi/Connector.java | 54 ----------- .../internal/util/BoltServerAddress.java | 51 ++++++++++ .../main/java/org/neo4j/driver/v1/Driver.java | 11 ++- .../org/neo4j/driver/v1/GraphDatabase.java | 2 +- .../org.neo4j.driver.internal.spi.Connector | 1 - .../pool/InternalConnectionPoolTest.java | 57 +---------- .../internal/util/BoltServerAddressTest.java | 14 ++- .../neo4j/driver/v1/GraphDatabaseTest.java | 97 +++++++++++++++++++ .../driver/v1/integration/EncryptionIT.java | 2 +- .../neo4j/driver/v1/integration/ErrorIT.java | 2 +- .../v1/integration/TLSSocketChannelIT.java | 18 ++-- .../org/neo4j/driver/v1/util/Neo4jRunner.java | 2 +- .../org/neo4j/driver/v1/util/TestNeo4j.java | 5 - 18 files changed, 245 insertions(+), 229 deletions(-) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java rename driver/src/main/java/org/neo4j/driver/internal/pool/{InternalConnectionPool.java => SocketConnectionPool.java} (70%) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java delete mode 100644 driver/src/main/resources/META-INF/services/org.neo4j.driver.internal.spi.Connector create mode 100644 driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java diff --git a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java index c2b51f8034..9e571502fe 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java @@ -18,21 +18,22 @@ */ package org.neo4j.driver.internal; -import org.neo4j.driver.internal.connector.socket.SocketConnector; -import org.neo4j.driver.internal.pool.InternalConnectionPool; +import org.neo4j.driver.internal.pool.SocketConnectionPool; import org.neo4j.driver.internal.pool.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; +import java.util.*; + public class DirectDriver implements Driver { + private final Set servers; private final BoltServerAddress address; private final SecurityPlan securityPlan; private final Logging logging; @@ -41,9 +42,18 @@ public class DirectDriver implements Driver public DirectDriver( BoltServerAddress address, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { this.address = address; + Set servers = new HashSet<>(); + servers.add( this.address ); + this.servers = Collections.unmodifiableSet( servers ); this.securityPlan = securityPlan; this.logging = logging; - this.connections = new InternalConnectionPool( new SocketConnector(), Clock.SYSTEM, securityPlan, poolSettings, logging ); + this.connections = new SocketConnectionPool( securityPlan, poolSettings, logging ); + } + + @Override + public Set servers() + { + return servers; } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java index e998838c4b..1ceef8f275 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java @@ -170,7 +170,7 @@ public boolean isOpen() private SocketProtocol negotiateProtocol() throws IOException { - logger.debug( "~~ [HANDSHAKE] [0x6060B017, 1, 0, 0, 0]." ); + logger.debug( "~~ [HANDSHAKE] [0x6060B017, 1, 0, 0, 0]" ); //Propose protocol versions ByteBuffer buf = ByteBuffer.allocateDirect( 5 * 4 ).order( BIG_ENDIAN ); buf.putInt( MAGIC_PREAMBLE ); @@ -240,7 +240,7 @@ public static ByteChannel create( BoltServerAddress address, SecurityPlan securi SocketChannel soChannel = SocketChannel.open(); soChannel.setOption( StandardSocketOptions.SO_REUSEADDR, true ); soChannel.setOption( StandardSocketOptions.SO_KEEPALIVE, true ); - soChannel.connect( new InetSocketAddress( address.host(), address.port() ) ); + soChannel.connect( address.toSocketAddress() ); ByteChannel channel; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java deleted file mode 100644 index 6407570a62..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal.connector.socket; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -import org.neo4j.driver.internal.Version; -import org.neo4j.driver.internal.connector.ConcurrencyGuardingConnection; -import org.neo4j.driver.internal.security.InternalAuthToken; -import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.Connector; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.v1.*; -import org.neo4j.driver.v1.exceptions.ClientException; - -public class SocketConnector implements Connector -{ - public static final String SCHEME = "bolt"; - - @Override - public boolean supports( String scheme ) - { - return scheme.equals( SCHEME ); - } - - @Override - public Connection connect( BoltServerAddress address, SecurityPlan securityPlan, Logging logging ) throws ClientException - { - Connection conn = new SocketConnection( address, securityPlan, logging ); - - // Because SocketConnection is not thread safe, wrap it in this guard - // to ensure concurrent access leads causes application errors - conn = new ConcurrencyGuardingConnection( conn ); - conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( securityPlan.authToken() ) ); - return conn; - } - - @Override - public Collection supportedSchemes() - { - return Collections.singletonList( SCHEME ); - } - - private Map tokenAsMap( AuthToken token ) - { - if( token instanceof InternalAuthToken ) - { - return ((InternalAuthToken) token).toMap(); - } - else - { - throw new ClientException( "Unknown authentication token, `" + token + "`. Please use one of the supported " + - "tokens from `" + AuthTokens.class.getSimpleName() + "`." ); - } - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java similarity index 70% rename from driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java rename to driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java index 9a52dd17e2..e219085196 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java @@ -18,20 +18,26 @@ */ package org.neo4j.driver.internal.pool; -import java.net.URI; -import java.util.Arrays; +import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import org.neo4j.driver.internal.Version; +import org.neo4j.driver.internal.connector.ConcurrencyGuardingConnection; +import org.neo4j.driver.internal.connector.socket.SocketConnection; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.internal.util.Clock; +import org.neo4j.driver.v1.AuthToken; +import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Logging; +import org.neo4j.driver.v1.Value; +import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; /** @@ -46,31 +52,52 @@ * The driver is thread safe. Each thread could try to get a session from the pool and then return it to the pool * at the same time. */ -public class InternalConnectionPool implements ConnectionPool +public class SocketConnectionPool implements ConnectionPool { /** - * Pools, organized by URL. + * Pools, organized by server address. */ private final ConcurrentHashMap> pools = new ConcurrentHashMap<>(); - private final Connector connector; + private final Clock clock = Clock.SYSTEM; + private final SecurityPlan securityPlan; - private final Clock clock; private final PoolSettings poolSettings; private final Logging logging; /** Shutdown flag */ private final AtomicBoolean stopped = new AtomicBoolean( false ); - public InternalConnectionPool( Connector connector, Clock clock, SecurityPlan securityPlan, - PoolSettings poolSettings, Logging logging ) + public SocketConnectionPool( SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { this.securityPlan = securityPlan; - this.clock = clock; this.poolSettings = poolSettings; this.logging = logging; - this.connector = connector; + } + + private Connection connect( BoltServerAddress address ) throws ClientException + { + Connection conn = new SocketConnection( address, securityPlan, logging ); + + // Because SocketConnection is not thread safe, wrap it in this guard + // to ensure concurrent access leads causes application errors + conn = new ConcurrencyGuardingConnection( conn ); + conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( securityPlan.authToken() ) ); + return conn; + } + + private static Map tokenAsMap( AuthToken token ) + { + if( token instanceof InternalAuthToken ) + { + return ((InternalAuthToken) token).toMap(); + } + else + { + throw new ClientException( "Unknown authentication token, `" + token + "`. Please use one of the supported " + + "tokens from `" + AuthTokens.class.getSimpleName() + "`." ); + } } @Override @@ -84,8 +111,8 @@ public Connection acquire( BoltServerAddress address ) PooledConnection conn = connections.poll(); if ( conn == null ) { - conn = new PooledConnection(connector.connect( address, securityPlan, logging ), new - PooledConnectionReleaseConsumer( connections, stopped, poolSettings ), clock); + conn = new PooledConnection( connect( address ), new + PooledConnectionReleaseConsumer( connections, stopped, poolSettings ), clock ); } conn.updateUsageTimestamp(); return conn; diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java index cf3184e594..8e8427ed14 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java @@ -16,17 +16,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.spi; +package org.neo4j.driver.internal.spi; import org.neo4j.driver.internal.util.BoltServerAddress; public interface ConnectionPool extends AutoCloseable { /** - * Acquire a connection - if a live connection exists in the pool, it will be used, otherwise a new connection - * is created with an applicable {@link Connector}. + * Acquire a connection - if a live connection exists in the pool, it will + * be used, otherwise a new connection will be created. + * * @param address */ Connection acquire( BoltServerAddress address ); + } diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java deleted file mode 100644 index 34ba6dbd85..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal.spi; - -import java.util.Collection; - -import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.v1.Logging; -import org.neo4j.driver.v1.exceptions.ClientException; - -/** - * A Connector conducts the client side of a client-server dialogue, - * along with its server side counterpart, the Listener. - */ -public interface Connector -{ - /** - * Determine whether this connector can support the sessionURL specified. - * - * @param scheme a URL scheme - * @return true if this scheme is supported, false otherwise - */ - boolean supports( String scheme ); - - /** - * Establish a connection to a remote listener and attach to the session identified. - * - * @param address a URL identifying a remote session - * @param securityPlan a security plan for this connection - * @param logging - * @return a Connection object - */ - Connection connect( BoltServerAddress address, SecurityPlan securityPlan, Logging logging ) throws ClientException; - - /** List names of supported schemes, used for error messages and similar signaling to end users. */ - Collection supportedSchemes(); -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java b/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java index 7b0c3b8b9b..57569f899f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java +++ b/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java @@ -44,6 +44,8 @@ public static BoltServerAddress from( URI uri ) private final String host; private final int port; + private SocketAddress socketAddress = null; // created lazily if required + public BoltServerAddress( String host, int port ) { this.host = host; @@ -55,12 +57,61 @@ public BoltServerAddress( String host ) this( host, DEFAULT_PORT ); } + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + { + return true; + } + if ( !(obj instanceof BoltServerAddress) ) + { + return false; + } + BoltServerAddress address = (BoltServerAddress) obj; + return host.equals( address.host ) && port == address.port; + } + + @Override + public int hashCode() + { + return 31 * host.hashCode() + port; + } + @Override public String toString() { return format( "%s:%d", host, port ); } + public SocketAddress toSocketAddress() + { + if (socketAddress == null) + { + socketAddress = new InetSocketAddress( host, port ); + } + return socketAddress; + } + + /** + * Resolve the host name down to an IP address, if not already resolved. + * + * @return this instance if already resolved, otherwise a new address instance + * @throws UnknownHostException + */ + public BoltServerAddress resolve() throws UnknownHostException + { + String hostAddress = InetAddress.getByName( host ).getHostAddress(); + if ( hostAddress.equals( host ) ) + { + return this; + } + else + { + return new BoltServerAddress( hostAddress, port ); + } + } + public String host() { return host; diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index b158a179fa..c5d5b9e57d 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -19,7 +19,9 @@ package org.neo4j.driver.v1; import java.net.URI; +import java.util.Set; +import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.exceptions.Neo4jException; /** @@ -55,10 +57,10 @@ * * // Sessions are pooled, to avoid the overhead of creating new connections - this means * // it is very important to close your session when you are done with it, otherwise you will - * // run out of sessions. + * // run out of resources. * session.close(); * - * // And, to clean up resources, always close the driver when your application is done + * // And, to clean up, always close the driver when your application is done. * driver.close(); * } * @@ -71,6 +73,11 @@ */ public interface Driver extends AutoCloseable { + /** + * Return a collection of the server addresses known by this driver. + */ + Set servers(); + /** * Return a flag to indicate whether or not encryption is used for this driver. */ 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 df7a39f4b8..6624f3ba6c 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -165,7 +165,7 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) case "bolt": return new DirectDriver( address, securityPlan, poolSettings, config.logging() ); default: - throw new IllegalArgumentException( format( "Scheme '%s' not supported", uri.getScheme() ) ); + throw new ClientException( format( "Unsupported URI scheme: %s", uri.getScheme() ) ); } } diff --git a/driver/src/main/resources/META-INF/services/org.neo4j.driver.internal.spi.Connector b/driver/src/main/resources/META-INF/services/org.neo4j.driver.internal.spi.Connector deleted file mode 100644 index a16cb5e170..0000000000 --- a/driver/src/main/resources/META-INF/services/org.neo4j.driver.internal.spi.Connector +++ /dev/null @@ -1 +0,0 @@ -org.neo4j.driver.internal.connector.socket.SocketConnector diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java index 64fd0a1d90..dcd8e32054 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java @@ -18,64 +18,9 @@ */ package org.neo4j.driver.internal.pool; -import org.junit.Test; - -import java.net.URI; -import java.util.Collections; - -import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.Connector; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.AuthTokens; -import org.neo4j.driver.v1.Config; -import org.neo4j.driver.v1.Logging; - -import static java.util.Collections.singletonList; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - - public class InternalConnectionPoolTest { - @Test - public void shouldAcquireAndRelease() throws Throwable - { - // Given - BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; - Connector connector = connector( "bolt" ); - Config config = Config.defaultConfig(); - SecurityPlan securityPlan = SecurityPlan.insecure(); - PoolSettings poolSettings = PoolSettings.defaultSettings(); - InternalConnectionPool pool = new InternalConnectionPool( - connector, Clock.SYSTEM, securityPlan, poolSettings, config.logging() ); - - Connection conn = pool.acquire( address ); - conn.close(); - - // When - Connection acquired = pool.acquire( address ); - - // Then - verify( connector, times( 1 ) ).connect( address, securityPlan, config.logging() ); - assertThat( acquired, equalTo(conn) ); - } + // TODO: write some tests that actually test something - private Connector connector( String scheme ) - { - Connector mock = mock( Connector.class ); - when( mock.supportedSchemes() ).thenReturn( Collections.singletonList( scheme ) ); - when( mock.connect( any( BoltServerAddress.class ), any( SecurityPlan.class ), any( Logging.class ) ) ).thenReturn( mock( - Connection.class - ) ); - return mock; - } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java index 117bbaa698..9ac53ac0ce 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java @@ -27,7 +27,7 @@ public class BoltServerAddressTest { @Test - public void shouldWorkForVariantsOfLocalHost() throws Exception + public void variantsOfLocalHostShouldResolveAsLocal() throws Exception { assertThat( new BoltServerAddress( "localhost", 7687 ).isLocal(), equalTo( true ) ); assertThat( new BoltServerAddress( "LocalHost", 7687 ).isLocal(), equalTo( true ) ); @@ -37,4 +37,16 @@ public void shouldWorkForVariantsOfLocalHost() throws Exception assertThat( new BoltServerAddress( "x", 7687 ).isLocal(), equalTo( false ) ); } + @Test + public void defaultPortShouldBe7687() + { + assertThat( BoltServerAddress.DEFAULT_PORT, equalTo( 7687 ) ); + } + + @Test + public void portShouldUseDefaultIfNotSupplied() + { + assertThat( new BoltServerAddress( "localhost" ).port(), equalTo( BoltServerAddress.DEFAULT_PORT ) ); + } + } \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java new file mode 100644 index 0000000000..24b95333f2 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.v1; + +import org.junit.Rule; +import org.junit.Test; +import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.v1.util.TestNeo4jSession; + +import java.net.URI; +import java.util.Set; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; +import static org.neo4j.driver.v1.Values.parameters; + +public class GraphDatabaseTest +{ + @Rule + public TestNeo4jSession session = new TestNeo4jSession(); + + @Test + public void directDriverShouldRegisterSingleServer() + { + // Given + URI uri = URI.create( "bolt://localhost:7687" ); + BoltServerAddress address = BoltServerAddress.from( uri ); + + // When + Driver driver = GraphDatabase.driver( uri ); + + // Then + Set addresses = driver.servers(); + assertThat( addresses.size(), equalTo( 1 ) ); + assertThat( addresses.contains( address ), equalTo( true ) ); + + } + + @Test + public void missingPortInURIShouldUseDefault() + { + // Given + URI uri = URI.create( "bolt://localhost" ); + + // When + Driver driver = GraphDatabase.driver( uri ); + + // Then + Set addresses = driver.servers(); + assertThat( addresses.size(), equalTo( 1 ) ); + for ( BoltServerAddress address : addresses ) + { + assertThat( address.port(), equalTo( BoltServerAddress.DEFAULT_PORT ) ); + } + + } + + @Test + public void shouldBeAbleRunCypherThroughDirectDriver() + { + // Given + URI uri = URI.create( "bolt://localhost:7687" ); + int x; + + // When + try ( Driver driver = GraphDatabase.driver( uri ) ) + { + try ( Session session = driver.session() ) + { + Record record = session.run( "RETURN {x}", parameters( "x", 1 ) ).single(); + x = record.get( 0 ).asInt(); + } + } + + // Then + assertThat( x, equalTo( 1 ) ); + + } + +} \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java index ae270bc9f4..d8baee2588 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/EncryptionIT.java @@ -64,7 +64,7 @@ public void shouldOperateWithRequiredNonLocalEncryption() throws Exception Driver driver = GraphDatabase.driver( neo4j.uri(), Config.build().withEncryptionLevel( REQUIRED_NON_LOCAL ).toConfig() ); // Then - assertThat( driver.isEncrypted(), equalTo( neo4j.address().isLocal() ) ); + assertThat( driver.isEncrypted(), equalTo( !neo4j.address().isLocal() ) ); // When Session session = driver.session(); diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/ErrorIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/ErrorIT.java index a1d4ae331f..e7a767d1dd 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/ErrorIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/ErrorIT.java @@ -114,7 +114,7 @@ public void shouldExplainConnectionError() throws Throwable { // Expect exception.expect( ClientException.class ); - exception.expectMessage( "Unable to connect to 'localhost' on port 7777, ensure the database is running " + + exception.expectMessage( "Unable to connect to localhost:7777, ensure the database is running " + "and that there is a working network connection to it." ); // When diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java index 3f4cc3ad5d..0669f70c65 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java @@ -83,7 +83,7 @@ private void performTLSHandshakeUsingKnownCerts( File knownCerts ) throws Throwa Logger logger = mock( Logger.class ); BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( address.host(), address.port() ) ); + channel.connect( address.toSocketAddress() ); // When @@ -127,7 +127,7 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable Logger logger = mock( Logger.class ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( address.host(), address.port() ) ); + channel.connect( address.toSocketAddress() ); // When SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), rootCert ); @@ -152,12 +152,12 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa // Given BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( address.host(), address.port() ) ); + channel.connect( address.toSocketAddress() ); File knownCerts = File.createTempFile( "neo4j_known_hosts", ".tmp" ); knownCerts.deleteOnExit(); //create a Fake Cert for the server in knownCert - createFakeServerCertPairInKnownCerts( address.host(), address.port(), knownCerts ); + createFakeServerCertPairInKnownCerts( address, knownCerts ); // When & Then SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, address, new DevNullLogger() ); @@ -183,11 +183,11 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa } } - private void createFakeServerCertPairInKnownCerts( String host, int port, File knownCerts ) + private void createFakeServerCertPairInKnownCerts( BoltServerAddress address, File knownCerts ) throws Throwable { - String ip = InetAddress.getByName( host ).getHostAddress(); // localhost -> 127.0.0.1 - String serverId = ip + ":" + port; + address = address.resolve(); // localhost -> 127.0.0.1 + String serverId = address.toString(); X509Certificate cert = CertificateToolTest.generateSelfSignedCertificate(); String certStr = fingerprint(cert); @@ -207,7 +207,7 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr Neo4jSettings.CERT_DIR, folder.getRoot().getAbsolutePath().replace("\\", "/") ) ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( neo4j.address().host(), neo4j.address().port() ) ); + channel.connect( neo4j.address().toSocketAddress() ); File trustedCertFile = folder.newFile( "neo4j_trusted_cert.tmp" ); X509Certificate aRandomCert = CertificateToolTest.generateSelfSignedCertificate(); CertificateTool.saveX509Cert( aRandomCert, trustedCertFile ); @@ -241,7 +241,7 @@ public void shouldPerformTLSHandshakeWithTheSameTrustedServerCert() throws Throw BoltServerAddress address = BoltServerAddress.LOCAL_DEFAULT; Logger logger = mock( Logger.class ); SocketChannel channel = SocketChannel.open(); - channel.connect( new InetSocketAddress( address.host(), address.port() ) ); + channel.connect( address.toSocketAddress() ); // When URI url = URI.create( "localhost:7687" ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java index 232f50aa0d..560bad65ff 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java @@ -199,7 +199,7 @@ private ServerStatus serverStatus() { SocketChannel soChannel = SocketChannel.open(); soChannel.setOption( StandardSocketOptions.SO_REUSEADDR, true ); - soChannel.connect( new InetSocketAddress( DEFAULT_ADDRESS.host(), DEFAULT_ADDRESS.port() ) ); + soChannel.connect( DEFAULT_ADDRESS.toSocketAddress() ); soChannel.close(); return ServerStatus.ONLINE; } diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java index ced9c24b4d..8e38f6dc07 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java @@ -107,11 +107,6 @@ public BoltServerAddress address() return Neo4jRunner.DEFAULT_ADDRESS; } - public String host() - { - return address().host(); - } - static void clearDatabaseContents( Session session, String reason ) { Neo4jRunner.debug( "Clearing database contents for: %s", reason ); From e90880155bc93f712e2ce91ce2567c39e04e48e5 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Thu, 21 Jul 2016 16:41:28 +0100 Subject: [PATCH 04/10] ClusterDriver implementation --- .../org/neo4j/driver/internal/BaseDriver.java | 64 ++++++++ .../neo4j/driver/internal/ClusterDriver.java | 154 ++++++++++++++++++ .../neo4j/driver/internal/DirectDriver.java | 61 ++----- .../driver/internal/ParameterSupport.java | 34 ---- .../{Version.java => SessionParameters.java} | 37 ++++- .../connector/socket/SocketUtils.java | 67 -------- .../internal/logging/ConsoleLogging.java | 2 +- .../internal/messaging/DiscardAllMessage.java | 2 +- .../internal/messaging/FailureMessage.java | 2 +- .../internal/messaging/IgnoredMessage.java | 2 +- .../internal/messaging/InitMessage.java | 16 +- .../messaging/PackStreamMessageFormatV1.java | 4 +- .../internal/messaging/PullAllMessage.java | 2 +- .../internal/messaging/RecordMessage.java | 2 +- .../internal/messaging/ResetMessage.java | 2 +- .../driver/internal/messaging/RunMessage.java | 2 +- .../internal/messaging/SuccessMessage.java | 2 +- .../{util => net}/BoltServerAddress.java | 14 +- .../socket => net}/BufferingChunkedInput.java | 2 +- .../socket => net}/ChunkedInput.java | 2 +- .../socket => net}/ChunkedOutput.java | 2 +- .../ConcurrencyGuardingConnection.java | 2 +- .../socket => net}/LoggingByteChannel.java | 2 +- .../LoggingResponseHandler.java | 16 +- .../socket => net}/SocketClient.java | 58 +++++-- .../socket => net}/SocketConnection.java | 3 +- .../socket => net}/SocketProtocol.java | 2 +- .../socket => net}/SocketProtocolV1.java | 2 +- .../socket => net}/SocketResponseHandler.java | 4 +- .../{pool => net/pooling}/PoolSettings.java | 2 +- .../pooling}/PooledConnection.java | 8 +- .../PooledConnectionReleaseConsumer.java | 2 +- .../pooling}/SocketConnectionPool.java | 19 ++- .../internal/security/SecurityPlan.java | 25 +-- .../internal/security/TLSSocketChannel.java | 5 +- .../security/TrustOnFirstUseTrustManager.java | 2 +- .../driver/internal/spi/ConnectionPool.java | 2 +- .../driver/internal/util/BytePrinter.java | 2 +- .../main/java/org/neo4j/driver/v1/Config.java | 3 +- .../main/java/org/neo4j/driver/v1/Driver.java | 55 ++----- .../org/neo4j/driver/v1/GraphDatabase.java | 35 ++-- .../java/org/neo4j/driver/v1/Session.java | 2 + .../driver/v1/exceptions/Neo4jException.java | 13 ++ .../connector/socket/SocketClientTest.java | 59 ------- .../FragmentedMessageDeliveryTest.java | 2 +- .../internal/messaging/MessageFormatTest.java | 2 +- .../{util => net}/BoltServerAddressTest.java | 3 +- .../BufferingChunkedInputFuzzTest.java | 2 +- .../BufferingChunkedInputTest.java | 2 +- .../socket => net}/ChunkedInputTest.java | 2 +- .../socket => net}/ChunkedOutputTest.java | 2 +- .../ConcurrencyGuardingConnectionTest.java | 2 +- .../LoggingResponseHandlerTest.java | 20 +-- .../SocketClientTest.java} | 58 +++++-- .../SocketResponseHandlerTest.java | 2 +- .../pooling}/ConnectionInvalidationTest.java | 4 +- .../pooling}/PooledConnectionTest.java | 6 +- .../pooling/SocketConnectionPoolTest.java} | 4 +- .../TrustOnFirstUseTrustManagerTest.java | 3 +- .../neo4j/driver/v1/ClusterDriverTest.java | 70 ++++++++ .../org/neo4j/driver/v1/DirectDriverTest.java | 97 +++++++++++ .../java/org/neo4j/driver/v1/DriverDocIT.java | 58 ------- .../neo4j/driver/v1/GraphDatabaseTest.java | 46 ++---- .../driver/v1/integration/SocketClientIT.java | 4 +- .../v1/integration/TLSSocketChannelIT.java | 20 +-- .../org/neo4j/driver/v1/util/DumpMessage.java | 2 +- .../org/neo4j/driver/v1/util/Neo4jRunner.java | 3 +- .../org/neo4j/driver/v1/util/TestNeo4j.java | 2 +- driver/src/test/resources/discovery.script | 11 ++ 69 files changed, 721 insertions(+), 504 deletions(-) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/ParameterSupport.java rename driver/src/main/java/org/neo4j/driver/internal/{Version.java => SessionParameters.java} (65%) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketUtils.java rename driver/src/main/java/org/neo4j/driver/internal/{util => net}/BoltServerAddress.java (91%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/BufferingChunkedInput.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/ChunkedInput.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/ChunkedOutput.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/{connector => net}/ConcurrencyGuardingConnection.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/LoggingByteChannel.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/LoggingResponseHandler.java (83%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/SocketClient.java (82%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/SocketConnection.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/SocketProtocol.java (94%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/SocketProtocolV1.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/{connector/socket => net}/SocketResponseHandler.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/{pool => net/pooling}/PoolSettings.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/{pool => net/pooling}/PooledConnection.java (95%) rename driver/src/main/java/org/neo4j/driver/internal/{pool => net/pooling}/PooledConnectionReleaseConsumer.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/{pool => net/pooling}/SocketConnectionPool.java (88%) delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java rename driver/src/test/java/org/neo4j/driver/internal/{util => net}/BoltServerAddressTest.java (95%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/BufferingChunkedInputFuzzTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/BufferingChunkedInputTest.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/ChunkedInputTest.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/ChunkedOutputTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{connector => net}/ConcurrencyGuardingConnectionTest.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/LoggingResponseHandlerTest.java (89%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket/SocketUtilsTest.java => net/SocketClientTest.java} (70%) rename driver/src/test/java/org/neo4j/driver/internal/{connector/socket => net}/SocketResponseHandlerTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{pool => net/pooling}/ConnectionInvalidationTest.java (97%) rename driver/src/test/java/org/neo4j/driver/internal/{pool => net/pooling}/PooledConnectionTest.java (96%) rename driver/src/test/java/org/neo4j/driver/internal/{pool/InternalConnectionPoolTest.java => net/pooling/SocketConnectionPoolTest.java} (90%) create mode 100644 driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java create mode 100644 driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java delete mode 100644 driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java create mode 100644 driver/src/test/resources/discovery.script diff --git a/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java new file mode 100644 index 0000000000..1cc3cc9ef1 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.internal; + +import org.neo4j.driver.internal.net.BoltServerAddress; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.Logger; +import org.neo4j.driver.v1.Logging; +import org.neo4j.driver.v1.Session; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +abstract class BaseDriver implements Driver +{ + private final SessionParameters sessionParameters; + private final SecurityPlan securityPlan; + protected final List servers = new LinkedList<>(); + protected final Logger log; + + BaseDriver( BoltServerAddress address, SessionParameters sessionParameters, SecurityPlan securityPlan, Logging logging ) + { + this.servers.add( address ); + this.sessionParameters = sessionParameters; + this.securityPlan = securityPlan; + this.log = logging.getLog( Session.LOG_NAME ); + } + + @Override + public boolean isEncrypted() + { + return securityPlan.requiresEncryption(); + } + + @Override + public List servers() + { + return Collections.unmodifiableList( servers ); + } + + protected BoltServerAddress randomServer() + { + return servers.get( ThreadLocalRandom.current().nextInt( 0, servers.size() ) ); + } + +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java b/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java new file mode 100644 index 0000000000..9cc2cf9eb8 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.internal; + +import org.neo4j.driver.internal.net.BoltServerAddress; +import org.neo4j.driver.internal.net.pooling.PoolSettings; +import org.neo4j.driver.internal.net.pooling.SocketConnectionPool; +import org.neo4j.driver.internal.security.SecurityPlan; +import org.neo4j.driver.internal.spi.ConnectionPool; +import org.neo4j.driver.v1.Logging; +import org.neo4j.driver.v1.Record; +import org.neo4j.driver.v1.Session; +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.driver.v1.exceptions.ClientException; +import org.neo4j.driver.v1.util.Function; + +import java.util.LinkedList; +import java.util.List; + +import static java.lang.String.format; + +public class ClusterDriver extends BaseDriver +{ + private static final String DISCOVER_MEMBERS = "dbms.cluster.discoverMembers"; + private static final String ACQUIRE_ENDPOINTS = "dbms.cluster.acquireEndpoints"; + + private final ConnectionPool connections; + + public ClusterDriver( BoltServerAddress seedAddress, SessionParameters sessionParameters, SecurityPlan securityPlan, + PoolSettings poolSettings, Logging logging ) + { + super( seedAddress, sessionParameters, securityPlan, logging ); + this.connections = new SocketConnectionPool( sessionParameters, securityPlan, poolSettings, logging ); + discover(); + } + + public void discover() + { + final List newServers = new LinkedList<>( ); + try + { + call( DISCOVER_MEMBERS, new Function() + { + @Override + public Integer apply( Record record ) + { + newServers.add( new BoltServerAddress( record.get( "address" ).asString() ) ); + return 0; + } + } ); + this.servers.clear(); + this.servers.addAll( newServers ); + log.debug( "~~ [MEMBERS] -> %s", newServers ); + } + catch ( ClientException ex ) + { + if ( ex.code().equals( "Neo.ClientError.Procedure.ProcedureNotFound" ) ) + { + // no discovery available; keep servers as they are + log.warn( "C: Discovery failed; could not find procedure %s", DISCOVER_MEMBERS ); + } + else + { + throw ex; + } + } + } + +// public void acquire(final String role) +// { +// BoltServerAddress server; +// try +// { +// call( ACQUIRE_ENDPOINTS, new Function() +// { +// @Override +// public Integer apply( Record record ) +// { +// if (record.get( "role" ).asString().equals( role )) +// { +// server = new BoltServerAddress( record.get( "address" ).asString() ); +// } +// return 0; +// } +// } ); +// this.servers.clear(); +// this.servers.addAll( newServers ); +// log.debug( "~~ [MEMBERS] -> %s", newServers ); +// } +// catch ( ClientException ex ) +// { +// if ( ex.code().equals( "Neo.ClientError.Procedure.ProcedureNotFound" ) ) +// { +// // no discovery available; keep servers as they are +// log.warn( "C: Discovery failed; could not find procedure %s", DISCOVER_MEMBERS ); +// } +// else +// { +// throw ex; +// } +// } +// } + +// public void + + void call( String procedureName, Function recorder ) + { + try ( Session session = new InternalSession( connections.acquire( randomServer() ), log ) ) + { + StatementResult records = session.run( format( "CALL %s", procedureName ) ); + while ( records.hasNext() ) + { + recorder.apply( records.next() ); + } + } + } + + @Override + public Session session() + { + throw new UnsupportedOperationException(); + } + + @Override + public void close() + { + try + { + connections.close(); + } + catch ( Exception ex ) + { + log.error( format( "~~ [ERROR] %s", ex.getMessage() ), ex ); + } + } + +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java index 9e571502fe..32b23e67dc 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java @@ -16,76 +16,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.neo4j.driver.internal; -import org.neo4j.driver.internal.pool.SocketConnectionPool; -import org.neo4j.driver.internal.pool.PoolSettings; +import org.neo4j.driver.internal.net.pooling.SocketConnectionPool; +import org.neo4j.driver.internal.net.pooling.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; -import org.neo4j.driver.v1.exceptions.ClientException; -import org.neo4j.driver.v1.exceptions.Neo4jException; -import java.util.*; +import static java.lang.String.format; -public class DirectDriver implements Driver +public class DirectDriver extends BaseDriver { - private final Set servers; - private final BoltServerAddress address; - private final SecurityPlan securityPlan; - private final Logging logging; private final ConnectionPool connections; - public DirectDriver( BoltServerAddress address, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) - { - this.address = address; - Set servers = new HashSet<>(); - servers.add( this.address ); - this.servers = Collections.unmodifiableSet( servers ); - this.securityPlan = securityPlan; - this.logging = logging; - this.connections = new SocketConnectionPool( securityPlan, poolSettings, logging ); - } - - @Override - public Set servers() + public DirectDriver( BoltServerAddress address, SessionParameters sessionParameters, SecurityPlan securityPlan, + PoolSettings poolSettings, Logging logging ) { - return servers; + super( address, sessionParameters, securityPlan, logging ); + this.connections = new SocketConnectionPool( sessionParameters, securityPlan, poolSettings, logging ); } - @Override - public boolean isEncrypted() - { - return securityPlan.requiresEncryption(); - } - - /** - * Establish a session - * @return a session that could be used to run {@link Session#run(String) a statement} or - * {@link Session#beginTransaction() a transaction }. - */ @Override public Session session() { - return new InternalSession( connections.acquire( address ), logging.getLog( "session" ) ); + return new InternalSession( connections.acquire( randomServer() ), log ); } - /** - * Close all the resources assigned to this driver - * @throws Neo4jException any error that might happen when releasing all resources - */ - public void close() throws Neo4jException + @Override + public void close() { try { connections.close(); } - catch ( Exception e ) + catch ( Exception ex ) { - throw new ClientException( "Failed to close driver.", e ); + log.error( format( "~~ [ERROR] %s", ex.getMessage() ), ex ); } } + } diff --git a/driver/src/main/java/org/neo4j/driver/internal/ParameterSupport.java b/driver/src/main/java/org/neo4j/driver/internal/ParameterSupport.java deleted file mode 100644 index 0953eed73d..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/ParameterSupport.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal; - -import java.util.Collections; -import java.util.Map; - -import org.neo4j.driver.v1.Value; - -public final class ParameterSupport -{ - public static final Map NO_PARAMETERS = Collections.emptyMap(); - - private ParameterSupport() - { - throw new UnsupportedOperationException(); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/Version.java b/driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java similarity index 65% rename from driver/src/main/java/org/neo4j/driver/internal/Version.java rename to driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java index 5b0c886b02..cc028fed90 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/Version.java +++ b/driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java @@ -16,22 +16,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.neo4j.driver.internal; +import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.Session; -public class Version +import static java.lang.String.format; + +public class SessionParameters { + public static final String DEFAULT_USER_AGENT = format( "neo4j-java/%s", driverVersion() ); + /** * Extracts the driver version from the driver jar MANIFEST.MF file. */ - public static String driverVersion() + private static String driverVersion() { // "Session" is arbitrary - the only thing that matters is that the class we use here is in the // 'org.neo4j.driver' package, because that is where the jar manifest specifies the version. // This is done as part of the build, adding a MANIFEST.MF file to the generated jarfile. Package pkg = Session.class.getPackage(); - if(pkg != null && pkg.getImplementationVersion() != null) + if ( pkg != null && pkg.getImplementationVersion() != null ) { return pkg.getImplementationVersion(); } @@ -40,4 +46,29 @@ public static String driverVersion() // This should only happen during development, so call the version 'dev'. return "dev"; } + + private final AuthToken authToken; + private final String userAgent; + + public SessionParameters( AuthToken authToken, String userAgent ) + { + this.authToken = authToken; + this.userAgent = userAgent; + } + + public SessionParameters( AuthToken authToken ) + { + this( authToken, DEFAULT_USER_AGENT ); + } + + public AuthToken authToken() + { + return authToken; + } + + public String userAgent() + { + return userAgent; + } + } diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketUtils.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketUtils.java deleted file mode 100644 index 992c9dc8f2..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketUtils.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal.connector.socket; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.ByteChannel; - -import org.neo4j.driver.internal.util.BytePrinter; -import org.neo4j.driver.v1.exceptions.ClientException; - -/** - * Utility class for common operations. - */ -public final class SocketUtils -{ - private SocketUtils() - { - throw new UnsupportedOperationException( "Do not instantiate" ); - } - - public static void blockingRead(ByteChannel channel, ByteBuffer buf) throws IOException - { - while(buf.hasRemaining()) - { - if (channel.read( buf ) < 0) - { - String bufStr = BytePrinter.hex( buf ).trim(); - throw new ClientException( String.format( - "Connection terminated while receiving data. This can happen due to network " + - "instabilities, or due to restarts of the database. Expected %s bytes, received %s.", - buf.limit(), bufStr.isEmpty() ? "none" : bufStr ) ); - } - } - } - - public static void blockingWrite(ByteChannel channel, ByteBuffer buf) throws IOException - { - while(buf.hasRemaining()) - { - if (channel.write( buf ) < 0) - { - String bufStr = BytePrinter.hex( buf ).trim(); - throw new ClientException( String.format( - "Connection terminated while sending data. This can happen due to network " + - "instabilities, or due to restarts of the database. Expected %s bytes, wrote %s.", - buf.limit(), bufStr.isEmpty() ? "none" :bufStr ) ); - } - } - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/logging/ConsoleLogging.java b/driver/src/main/java/org/neo4j/driver/internal/logging/ConsoleLogging.java index 3923578604..95b34af4e1 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/logging/ConsoleLogging.java +++ b/driver/src/main/java/org/neo4j/driver/internal/logging/ConsoleLogging.java @@ -93,7 +93,7 @@ public String format( LogRecord record ) { StringBuilder builder = new StringBuilder( 1000 ); builder.append( dateFormat.format( new Date( record.getMillis() ) ) ); - builder.append( " [" ).append( record.getLoggerName() ).append( "]" ).append( " - " ); + builder.append(" "); // builder.append( "[" ).append( record.getSourceClassName() ).append( "." ); // builder.append( record.getSourceMethodName() ).append( "] - " ); // builder.append( "[" ).append( record.getLevel() ).append( "] - " ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/DiscardAllMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/DiscardAllMessage.java index 7713616932..ed11f4744c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/DiscardAllMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/DiscardAllMessage.java @@ -33,7 +33,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return "[DISCARD_ALL]"; + return "DISCARD_ALL"; } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/FailureMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/FailureMessage.java index 74139e1b43..941300bc19 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/FailureMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/FailureMessage.java @@ -49,7 +49,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return format( "[FAILURE %s \"%s\"]", code, message ); + return format( "FAILURE %s \"%s\"", code, message ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/IgnoredMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/IgnoredMessage.java index 2962f7f7d5..501dba4c92 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/IgnoredMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/IgnoredMessage.java @@ -39,7 +39,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return "[IGNORED]"; + return "IGNORED {}"; } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java index e2c1723c63..296f84971e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java @@ -32,25 +32,25 @@ */ public class InitMessage implements Message { - private final String clientNameAndVersion; + private final String userAgent; private Map authToken; - public InitMessage( String clientNameAndVersion, Map authToken ) + public InitMessage( String userAgent, Map authToken ) { - this.clientNameAndVersion = clientNameAndVersion; + this.userAgent = userAgent; this.authToken = authToken; } @Override public void dispatch( MessageHandler handler ) throws IOException { - handler.handleInitMessage( clientNameAndVersion, authToken ); + handler.handleInitMessage( userAgent, authToken ); } @Override public String toString() { - return format( "[INIT \"%s\"]", clientNameAndVersion ); + return format( "INIT \"%s\" {...}", userAgent, authToken ); } @Override @@ -63,14 +63,14 @@ public boolean equals( Object o ) InitMessage that = (InitMessage) o; - return !(clientNameAndVersion != null ? !clientNameAndVersion.equals( that.clientNameAndVersion ) - : that.clientNameAndVersion != null); + return !(userAgent != null ? !userAgent.equals( that.userAgent ) + : that.userAgent != null); } @Override public int hashCode() { - return clientNameAndVersion != null ? clientNameAndVersion.hashCode() : 0; + return userAgent != null ? userAgent.hashCode() : 0; } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java index 36d6b3113e..c69657de68 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java @@ -31,8 +31,8 @@ import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; import org.neo4j.driver.internal.InternalRelationship; -import org.neo4j.driver.internal.connector.socket.BufferingChunkedInput; -import org.neo4j.driver.internal.connector.socket.ChunkedOutput; +import org.neo4j.driver.internal.net.BufferingChunkedInput; +import org.neo4j.driver.internal.net.ChunkedOutput; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.packstream.PackStream; diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/PullAllMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/PullAllMessage.java index beeb5558be..547ddd8eb5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/PullAllMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/PullAllMessage.java @@ -38,7 +38,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return "[PULL_ALL]"; + return "PULL_ALL"; } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/RecordMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/RecordMessage.java index 127fffddf0..e070745b3a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/RecordMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/RecordMessage.java @@ -41,7 +41,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return "[RECORD " + Arrays.toString( fields ) + ']'; + return "RECORD " + Arrays.toString( fields ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/ResetMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/ResetMessage.java index f53cb38277..d89374483e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/ResetMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/ResetMessage.java @@ -47,7 +47,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return "[RESET]"; + return "RESET"; } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/RunMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/RunMessage.java index a415eeb8df..b1e4176c69 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/RunMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/RunMessage.java @@ -51,7 +51,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return format( "[RUN \"%s\" %s]", statement, parameters ); + return format( "RUN \"%s\" %s", statement, parameters ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/SuccessMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/SuccessMessage.java index 8af5703e77..0386a1034d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/SuccessMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/SuccessMessage.java @@ -49,7 +49,7 @@ public void dispatch( MessageHandler handler ) throws IOException @Override public String toString() { - return format( "[SUCCESS %s]", metadata ); + return format( "SUCCESS %s", metadata ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java b/driver/src/main/java/org/neo4j/driver/internal/net/BoltServerAddress.java similarity index 91% rename from driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java rename to driver/src/main/java/org/neo4j/driver/internal/net/BoltServerAddress.java index 57569f899f..fad7a53171 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/BoltServerAddress.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/BoltServerAddress.java @@ -17,7 +17,7 @@ * limitations under the License. */ -package org.neo4j.driver.internal.util; +package org.neo4j.driver.internal.net; import java.net.*; @@ -54,7 +54,17 @@ public BoltServerAddress( String host, int port ) public BoltServerAddress( String host ) { - this( host, DEFAULT_PORT ); + int colon = host.indexOf( ':' ); + if ( colon >= 0 ) + { + this.port = Integer.parseInt( host.substring( colon + 1 ) ); + this.host = host.substring( 0, colon ); + } + else + { + this.host = host; + this.port = DEFAULT_PORT; + } } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInput.java b/driver/src/main/java/org/neo4j/driver/internal/net/BufferingChunkedInput.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInput.java rename to driver/src/main/java/org/neo4j/driver/internal/net/BufferingChunkedInput.java index ab8b3c5c42..7c1f6e3b57 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInput.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/BufferingChunkedInput.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedInput.java b/driver/src/main/java/org/neo4j/driver/internal/net/ChunkedInput.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedInput.java rename to driver/src/main/java/org/neo4j/driver/internal/net/ChunkedInput.java index a6ee748539..ffc006efcf 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedInput.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/ChunkedInput.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedOutput.java b/driver/src/main/java/org/neo4j/driver/internal/net/ChunkedOutput.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedOutput.java rename to driver/src/main/java/org/neo4j/driver/internal/net/ChunkedOutput.java index 93bd808a11..bec45447bb 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/ChunkedOutput.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/ChunkedOutput.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnection.java b/driver/src/main/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnection.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnection.java index 5b16549c26..bb78409ff5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector; +package org.neo4j.driver.internal.net; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingByteChannel.java b/driver/src/main/java/org/neo4j/driver/internal/net/LoggingByteChannel.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingByteChannel.java rename to driver/src/main/java/org/neo4j/driver/internal/net/LoggingByteChannel.java index 12333bd400..9804906b1c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingByteChannel.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/LoggingByteChannel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/net/LoggingResponseHandler.java similarity index 83% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java rename to driver/src/main/java/org/neo4j/driver/internal/net/LoggingResponseHandler.java index 15da9d98c5..e74f4a085d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/LoggingResponseHandler.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.util.Arrays; import java.util.Map; @@ -40,17 +40,17 @@ public LoggingResponseHandler( Logger logger ) } @Override - public void handleInitMessage( String clientNameAndVersion, Map authToken ) + public void handleInitMessage( String userAgent, Map authToken ) { - super.handleInitMessage( clientNameAndVersion, authToken ); - logger.debug( "S: [INIT \"%s\"]", clientNameAndVersion ); + super.handleInitMessage( userAgent, authToken ); + logger.debug( "S: INIT \"%s\" {...}", userAgent ); } @Override public void handleRunMessage( String statement, Map parameters ) { super.handleRunMessage( statement, parameters ); - logger.debug( "S: [RUN \"%s\" %s]", statement, parameters ); + logger.debug( "S: RUN \"%s\" %s", statement, parameters ); } @Override @@ -78,21 +78,21 @@ public void handleResetMessage() public void handleSuccessMessage( Map meta ) { super.handleSuccessMessage( meta ); - logger.debug( "S: [SUCCESS %s]", meta ); + logger.debug( "S: SUCCESS %s", meta ); } @Override public void handleRecordMessage( Value[] fields ) { super.handleRecordMessage( fields ); - logger.debug( "S: [RECORD %s]", Arrays.asList( fields ) ); + logger.debug( "S: RECORD %s", Arrays.asList( fields ) ); } @Override public void handleFailureMessage( String code, String message ) { super.handleFailureMessage( code, message ); - logger.debug("S: [FAILURE %s \"%s\"]", code, message ); + logger.debug("S: FAILURE %s \"%s\"", code, message ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java b/driver/src/main/java/org/neo4j/driver/internal/net/SocketClient.java similarity index 82% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java rename to driver/src/main/java/org/neo4j/driver/internal/net/SocketClient.java index 1ceef8f275..e1ac4aeb90 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketClient.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/SocketClient.java @@ -16,11 +16,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.net.ConnectException; -import java.net.InetSocketAddress; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; @@ -32,14 +31,12 @@ import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.security.TLSSocketChannel; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.util.BytePrinter; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.v1.exceptions.ClientException; import static java.lang.String.format; import static java.nio.ByteOrder.BIG_ENDIAN; -import static org.neo4j.driver.internal.connector.socket.SocketUtils.blockingRead; -import static org.neo4j.driver.internal.connector.socket.SocketUtils.blockingWrite; public class SocketClient { @@ -67,12 +64,47 @@ public SocketClient( BoltServerAddress address, SecurityPlan securityPlan, Logge this.channel = null; } + void setChannel( ByteChannel channel ) + { + this.channel = channel; + } + + void blockingRead( ByteBuffer buf ) throws IOException + { + while(buf.hasRemaining()) + { + if (channel.read( buf ) < 0) + { + String bufStr = BytePrinter.hex( buf ).trim(); + throw new ClientException( format( + "Connection terminated while receiving data. This can happen due to network " + + "instabilities, or due to restarts of the database. Expected %s bytes, received %s.", + buf.limit(), bufStr.isEmpty() ? "none" : bufStr ) ); + } + } + } + + void blockingWrite( ByteBuffer buf ) throws IOException + { + while(buf.hasRemaining()) + { + if (channel.write( buf ) < 0) + { + String bufStr = BytePrinter.hex( buf ).trim(); + throw new ClientException( format( + "Connection terminated while sending data. This can happen due to network " + + "instabilities, or due to restarts of the database. Expected %s bytes, wrote %s.", + buf.limit(), bufStr.isEmpty() ? "none" :bufStr ) ); + } + } + } + public void start() { try { logger.debug( "~~ [CONNECT] %s", address ); - channel = ChannelFactory.create( address, securityPlan, logger ); + setChannel( ChannelFactory.create( address, securityPlan, logger ) ); protocol = negotiateProtocol(); reader = protocol.reader(); writer = protocol.writer(); @@ -145,9 +177,9 @@ public void stop() { if ( channel != null ) { - logger.debug( "~~ [CLOSE]" ); channel.close(); - channel = null; + setChannel( null ); + logger.debug( "~~ [DISCONNECT]" ); } } catch ( IOException e ) @@ -170,25 +202,25 @@ public boolean isOpen() private SocketProtocol negotiateProtocol() throws IOException { - logger.debug( "~~ [HANDSHAKE] [0x6060B017, 1, 0, 0, 0]" ); //Propose protocol versions ByteBuffer buf = ByteBuffer.allocateDirect( 5 * 4 ).order( BIG_ENDIAN ); + logger.debug( "C: [HANDSHAKE] 0x6060B017" ); buf.putInt( MAGIC_PREAMBLE ); + logger.debug( "C: [HANDSHAKE] [1, 0, 0, 0]" ); for ( int version : SUPPORTED_VERSIONS ) { buf.putInt( version ); } buf.flip(); - //Do a blocking write - blockingWrite(channel, buf); + blockingWrite( buf ); // Read (blocking) back the servers choice buf.clear(); buf.limit( 4 ); try { - blockingRead( channel, buf ); + blockingRead( buf ); } catch ( ClientException e ) { @@ -209,7 +241,7 @@ private SocketProtocol negotiateProtocol() throws IOException switch ( proposal ) { case VERSION1: - logger.debug( "~~ [HANDSHAKE] 1" ); + logger.debug( "S: [HANDSHAKE] -> 1" ); return new SocketProtocolV1( channel ); case NO_VERSION: throw new ClientException( "The server does not support any of the protocol versions supported by " + diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java b/driver/src/main/java/org/neo4j/driver/internal/net/SocketConnection.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/net/SocketConnection.java index fc9b1342b3..a03736d261 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/SocketConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.net.SocketTimeoutException; @@ -31,7 +31,6 @@ import org.neo4j.driver.internal.messaging.RunMessage; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.spi.StreamCollector; import org.neo4j.driver.v1.Logging; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocol.java similarity index 94% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocol.java rename to driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocol.java index 45ee361062..dee354a07a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocol.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.neo4j.driver.internal.messaging.MessageFormat.Reader; import org.neo4j.driver.internal.messaging.MessageFormat.Writer; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocolV1.java b/driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocolV1.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocolV1.java rename to driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocolV1.java index 29046927a5..e1184eedf5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketProtocolV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/SocketProtocolV1.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.io.IOException; import java.nio.channels.ByteChannel; diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/net/SocketResponseHandler.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java rename to driver/src/main/java/org/neo4j/driver/internal/net/SocketResponseHandler.java index b00b6065a4..34e92ee28e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/SocketResponseHandler.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import java.util.LinkedList; import java.util.Map; @@ -221,7 +221,7 @@ public void appendResultCollector( StreamCollector collector ) public boolean protocolViolationErrorOccurred() { - return error != null && error.neo4jErrorCode().startsWith( "Neo.ClientError.Request" ); + return error != null && error.code().startsWith( "Neo.ClientError.Request" ); } public boolean serverFailureOccurred() diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PoolSettings.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java rename to driver/src/main/java/org/neo4j/driver/internal/net/pooling/PoolSettings.java index 1a4dde42eb..c1246ae9c7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/PoolSettings.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PoolSettings.java @@ -17,7 +17,7 @@ * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; public class PoolSettings { diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnection.java similarity index 95% rename from driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnection.java index bcfcd5745a..1338b7aa3a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; import java.util.Map; @@ -231,15 +231,15 @@ public void onError( Runnable runnable ) private boolean isProtocolViolationError(RuntimeException e ) { return e instanceof Neo4jException - && ((Neo4jException) e).neo4jErrorCode().startsWith( "Neo.ClientError.Request" ); + && ((Neo4jException) e).code().startsWith( "Neo.ClientError.Request" ); } private boolean isClientOrTransientError( RuntimeException e ) { // Eg: DatabaseErrors and unknown (no status code or not neo4j exception) cause session to be discarded return e instanceof Neo4jException - && (((Neo4jException) e).neo4jErrorCode().contains( "ClientError" ) - || ((Neo4jException) e).neo4jErrorCode().contains( "TransientError" )); + && (((Neo4jException) e).code().contains( "ClientError" ) + || ((Neo4jException) e).code().contains( "TransientError" )); } public long idleTime() diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnectionReleaseConsumer.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java rename to driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnectionReleaseConsumer.java index a3c002b084..974e8c8d8d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnectionReleaseConsumer.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/PooledConnectionReleaseConsumer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; import java.util.HashMap; import java.util.Map; diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java similarity index 88% rename from driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java rename to driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java index e219085196..16878d0f62 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/SocketConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; import java.util.Map; import java.util.concurrent.BlockingQueue; @@ -24,14 +24,14 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.driver.internal.Version; -import org.neo4j.driver.internal.connector.ConcurrencyGuardingConnection; -import org.neo4j.driver.internal.connector.socket.SocketConnection; +import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.net.BoltServerAddress; +import org.neo4j.driver.internal.net.ConcurrencyGuardingConnection; +import org.neo4j.driver.internal.net.SocketConnection; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.internal.util.BoltServerAddress; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.AuthTokens; @@ -62,6 +62,7 @@ public class SocketConnectionPool implements ConnectionPool private final Clock clock = Clock.SYSTEM; + private final SessionParameters sessionParameters; private final SecurityPlan securityPlan; private final PoolSettings poolSettings; private final Logging logging; @@ -69,8 +70,10 @@ public class SocketConnectionPool implements ConnectionPool /** Shutdown flag */ private final AtomicBoolean stopped = new AtomicBoolean( false ); - public SocketConnectionPool( SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) + public SocketConnectionPool( SessionParameters sessionParameters, SecurityPlan securityPlan, + PoolSettings poolSettings, Logging logging ) { + this.sessionParameters = sessionParameters; this.securityPlan = securityPlan; this.poolSettings = poolSettings; this.logging = logging; @@ -83,7 +86,7 @@ private Connection connect( BoltServerAddress address ) throws ClientException // Because SocketConnection is not thread safe, wrap it in this guard // to ensure concurrent access leads causes application errors conn = new ConcurrencyGuardingConnection( conn ); - conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( securityPlan.authToken() ) ); + conn.init( sessionParameters.userAgent(), tokenAsMap( sessionParameters.authToken() ) ); return conn; } @@ -134,7 +137,7 @@ private BlockingQueue pool( BoltServerAddress address ) } @Override - public void close() throws Neo4jException + public void close() { if( !stopped.compareAndSet( false, true ) ) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java index 0c14cf5ba4..a36b985236 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java @@ -19,7 +19,7 @@ package org.neo4j.driver.internal.security; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.*; import javax.net.ssl.TrustManager; @@ -32,13 +32,11 @@ import static org.neo4j.driver.internal.util.CertificateTool.loadX509Cert; /** - * A SecurityPlan consists of authentication information, - * an encryption on/off flag and an array of TrustManager - * instances that can be used to initialize an SSL context. + * A SecurityPlan consists of encryption and trust details. */ public class SecurityPlan { - public static SecurityPlan forSignedCertificates( AuthToken authToken, File certFile ) + public static SecurityPlan forSignedCertificates( File certFile ) throws GeneralSecurityException, IOException { // A certificate file is specified so we will load the certificates in the file @@ -52,36 +50,29 @@ public static SecurityPlan forSignedCertificates( AuthToken authToken, File cert // Create TrustManager from TrustedKeyStore TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( "SunX509" ); trustManagerFactory.init( trustedKeyStore ); - return new SecurityPlan( authToken, true, trustManagerFactory.getTrustManagers() ); + return new SecurityPlan( true, trustManagerFactory.getTrustManagers() ); } - public static SecurityPlan forTrustOnFirstUse( AuthToken authToken, File knownHosts, BoltServerAddress address, Logger logger ) + public static SecurityPlan forTrustOnFirstUse( File knownHosts, BoltServerAddress address, Logger logger ) throws IOException { - return new SecurityPlan( authToken, true, new TrustOnFirstUseTrustManager( address, knownHosts, logger ) ); + return new SecurityPlan( true, new TrustOnFirstUseTrustManager( address, knownHosts, logger ) ); } public static SecurityPlan insecure() { - return new SecurityPlan( AuthTokens.none(), false ); + return new SecurityPlan( false ); } - private final AuthToken authToken; private final boolean requiresEncryption; private final TrustManager[] trustManagers; - public SecurityPlan( AuthToken authToken, boolean requiresEncryption, TrustManager... trustManagers ) + public SecurityPlan( boolean requiresEncryption, TrustManager... trustManagers ) { - this.authToken = authToken; this.requiresEncryption = requiresEncryption; this.trustManagers = trustManagers; } - public AuthToken authToken() - { - return authToken; - } - public boolean requiresEncryption() { return requiresEncryption; diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java index e196986201..f9f540d4c2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java @@ -29,7 +29,7 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.util.BytePrinter; import org.neo4j.driver.v1.exceptions.ClientException; @@ -111,6 +111,7 @@ public TLSSocketChannel( ByteChannel channel, Logger logger, SSLEngine sslEngine */ private void runHandshake() throws IOException { + logger.debug( "~~ [OPENING SECURE CHANNEL]" ); sslEngine.beginHandshake(); HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus(); while ( handshakeStatus != FINISHED && handshakeStatus != NOT_HANDSHAKING ) @@ -442,7 +443,7 @@ public void close() throws IOException } // Close transport channel.close(); - logger.debug( "TLS connection closed" ); + logger.debug( "~~ [CLOSED SECURE CHANNEL]" ); } catch(IOException e) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java index 24ce7f20be..45f836fd53 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java +++ b/driver/src/main/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManager.java @@ -30,7 +30,7 @@ import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.internal.util.BytePrinter; diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java index 8e8427ed14..b6ca104614 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/ConnectionPool.java @@ -19,7 +19,7 @@ package org.neo4j.driver.internal.spi; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; public interface ConnectionPool extends AutoCloseable { diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/BytePrinter.java b/driver/src/main/java/org/neo4j/driver/internal/util/BytePrinter.java index 740b7d2b95..9eedf69ee6 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/BytePrinter.java +++ b/driver/src/main/java/org/neo4j/driver/internal/util/BytePrinter.java @@ -251,7 +251,7 @@ public static String hexInOneLine( ByteBuffer bytes, int offset, int length ) } else if ( (i - offset + 1) % 8 == 0 ) { - out.print( " " ); + out.print( " " ); } else { diff --git a/driver/src/main/java/org/neo4j/driver/v1/Config.java b/driver/src/main/java/org/neo4j/driver/v1/Config.java index da2c8fceec..3ae6e5b704 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Config.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Config.java @@ -21,8 +21,9 @@ import java.io.File; import java.util.logging.Level; +import org.neo4j.driver.internal.logging.ConsoleLogging; import org.neo4j.driver.internal.logging.JULogging; -import org.neo4j.driver.internal.pool.PoolSettings; +import org.neo4j.driver.internal.net.pooling.PoolSettings; import org.neo4j.driver.v1.util.Immutable; import static java.lang.System.getProperty; diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index c5d5b9e57d..1100028943 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -19,72 +19,38 @@ package org.neo4j.driver.v1; import java.net.URI; -import java.util.Set; +import java.util.List; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.v1.exceptions.Neo4jException; +import org.neo4j.driver.internal.net.BoltServerAddress; /** * A Neo4j database driver, through which you can create {@link Session sessions} to run statements against the database. *

- * An example: - *

- * {@code
- * // Create a driver with default configuration
- * Driver driver = GraphDatabase.driver( "bolt://localhost:7687" );
- *
- * // Establish a session
- * Session session = driver.session();
- *
- * // Running a simple statement can be done like this
- * session.run( "CREATE (n {name:'Bob'})" );
- *
- * // Or, run multiple statements together in an atomic transaction:
- * try( Transaction tx = session.beginTransaction() )
- * {
- *     tx.run( "CREATE (n {name:'Alice'})" );
- *     tx.run( "CREATE (n {name:'Tina'})" );
- *     tx.success();
- * }
- *
- * // Retrieve results
- * StatementResult result = session.run( "MATCH (n) RETURN n.name" );
- * List names = new LinkedList<>();
- * while( result.hasNext() )
- * {
- *     names.add( result.next().get("n.name").asString() );
- * }
- *
- * // Sessions are pooled, to avoid the overhead of creating new connections - this means
- * // it is very important to close your session when you are done with it, otherwise you will
- * // run out of resources.
- * session.close();
- *
- * // And, to clean up, always close the driver when your application is done.
- * driver.close();
- * }
- * 
- *

- * * A driver maintains a connection pool for each Neo4j instance. For resource efficiency reasons you are encouraged * to use the same driver instance across your application. You can control the connection pooling behavior when you * create the driver using the {@link Config} you pass into {@link GraphDatabase#driver(URI, Config)}. + * * @since 1.0 */ public interface Driver extends AutoCloseable { /** * Return a collection of the server addresses known by this driver. + * + * @return list of server addresses */ - Set servers(); + List servers(); /** * Return a flag to indicate whether or not encryption is used for this driver. + * + * @return true if the driver requires encryption, false otherwise */ boolean isEncrypted(); /** * Establish a session + * * @return a session that could be used to run {@link Session#run(String) a statement} or * {@link Session#beginTransaction() a transaction }. */ @@ -92,7 +58,6 @@ public interface Driver extends AutoCloseable /** * Close all the resources assigned to this driver - * @throws Neo4jException any error that might happen when releasing all resources */ - void close() throws Neo4jException; + void close(); } 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 6624f3ba6c..c76448dfde 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -22,10 +22,12 @@ import java.net.URI; import java.security.GeneralSecurityException; +import org.neo4j.driver.internal.ClusterDriver; import org.neo4j.driver.internal.DirectDriver; -import org.neo4j.driver.internal.pool.PoolSettings; +import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.net.pooling.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.exceptions.ClientException; import static java.lang.String.format; @@ -132,12 +134,15 @@ public static Driver driver( String uri, AuthToken authToken, Config config ) */ public static Driver driver( URI uri, AuthToken authToken, Config config ) { - // Fill in defaults + // Break down the URI into its constituent parts + String scheme = uri.getScheme(); BoltServerAddress address = BoltServerAddress.from( uri ); - if (authToken == null) - { - authToken = AuthTokens.none(); - } + + // Collate session parameters + SessionParameters sessionParameters = + new SessionParameters( authToken == null ? AuthTokens.none() : authToken ); + + // Make sure we have some configuration to play with if (config == null) { config = Config.defaultConfig(); @@ -159,13 +164,15 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) config.maxIdleConnectionPoolSize(), config.idleTimeBeforeConnectionTest() ); - // Finally, construct the driver proper - switch ( uri.getScheme() ) + // And finally, construct the driver proper + switch ( scheme ) { case "bolt": - return new DirectDriver( address, securityPlan, poolSettings, config.logging() ); + return new DirectDriver( address, sessionParameters, securityPlan, poolSettings, config.logging() ); + case "bolt+discovery": + return new ClusterDriver( address, sessionParameters, securityPlan, poolSettings, config.logging() ); default: - throw new ClientException( format( "Unsupported URI scheme: %s", uri.getScheme() ) ); + throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) ); } } @@ -185,9 +192,9 @@ private static SecurityPlan createSecurityPlan( BoltServerAddress address, AuthT switch ( config.trustStrategy().strategy() ) { case TRUST_SIGNED_CERTIFICATES: - return SecurityPlan.forSignedCertificates( authToken, config.trustStrategy().certFile() ); + return SecurityPlan.forSignedCertificates( config.trustStrategy().certFile() ); case TRUST_ON_FIRST_USE: - return SecurityPlan.forTrustOnFirstUse( authToken, config.trustStrategy().certFile(), + return SecurityPlan.forTrustOnFirstUse( config.trustStrategy().certFile(), address, config.logging().getLog( "session" ) ); default: throw new ClientException( "Unknown TLS authentication strategy: " + config.trustStrategy().strategy().name() ); @@ -195,7 +202,7 @@ private static SecurityPlan createSecurityPlan( BoltServerAddress address, AuthT } else { - return new SecurityPlan( authToken, false ); + return new SecurityPlan( false ); } } diff --git a/driver/src/main/java/org/neo4j/driver/v1/Session.java b/driver/src/main/java/org/neo4j/driver/v1/Session.java index 32ed7fed9d..e5be539dd9 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Session.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Session.java @@ -41,6 +41,8 @@ */ public interface Session extends Resource, StatementRunner { + String LOG_NAME = "session"; + /** * Begin a new transaction in this session. A session can have at most one transaction running at a time, if you * want to run multiple concurrent transactions, you should use multiple concurrent sessions. diff --git a/driver/src/main/java/org/neo4j/driver/v1/exceptions/Neo4jException.java b/driver/src/main/java/org/neo4j/driver/v1/exceptions/Neo4jException.java index 21facb978f..d334803e19 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/exceptions/Neo4jException.java +++ b/driver/src/main/java/org/neo4j/driver/v1/exceptions/Neo4jException.java @@ -56,8 +56,21 @@ public Neo4jException( String code, String message, Throwable cause ) * * @return the Neo4j Status Code for this exception, or 'N/A' if none is available */ + @Deprecated public String neo4jErrorCode() { return code; } + + /** + * Access the status code for this exception. The Neo4j manual can + * provide further details on the available codes and their meanings. + * + * @return textual code, such as "Neo.ClientError.Procedure.ProcedureNotFound" + */ + public String code() + { + return code; + } + } diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java deleted file mode 100644 index 21940a2846..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketClientTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.internal.connector.socket; - -import java.net.ServerSocket; - -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.neo4j.driver.internal.logging.DevNullLogger; -import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.util.BoltServerAddress; -import org.neo4j.driver.v1.Config; -import org.neo4j.driver.v1.exceptions.ClientException; - -public class SocketClientTest -{ - @Rule - public ExpectedException exception = ExpectedException.none(); - - // TODO: This is not possible with blocking NIO channels, unless we use inputStreams, but then we can't use - // off-heap buffers. We need to swap to use selectors, which would allow us to time out. - @Test - @Ignore - public void testNetworkTimeout() throws Throwable - { - // Given a server that will never reply - ServerSocket server = new ServerSocket( 0 ); - BoltServerAddress address = new BoltServerAddress( "localhost", server.getLocalPort() ); - - SecurityPlan securityPlan = SecurityPlan.insecure(); - SocketClient client = new SocketClient( address, securityPlan, new DevNullLogger() ); - - // Expect - exception.expect( ClientException.class ); - exception.expectMessage( "database took longer than network timeout (100ms) to reply." ); - - // When - client.start(); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/FragmentedMessageDeliveryTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/FragmentedMessageDeliveryTest.java index a4f3d02782..94648f4080 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/FragmentedMessageDeliveryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/FragmentedMessageDeliveryTest.java @@ -29,7 +29,7 @@ import java.util.ArrayList; import java.util.Collections; -import org.neo4j.driver.internal.connector.socket.ChunkedOutput; +import org.neo4j.driver.internal.net.ChunkedOutput; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.util.DumpMessage; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java index 00a04d55b0..0e4cf82b2f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java @@ -33,7 +33,7 @@ import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; import org.neo4j.driver.internal.InternalRelationship; -import org.neo4j.driver.internal.connector.socket.ChunkedOutput; +import org.neo4j.driver.internal.net.ChunkedOutput; import org.neo4j.driver.internal.packstream.PackStream; import org.neo4j.driver.internal.util.BytePrinter; import org.neo4j.driver.v1.Value; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java index 9ac53ac0ce..57f22b7797 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/BoltServerAddressTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java @@ -17,9 +17,10 @@ * limitations under the License. */ -package org.neo4j.driver.internal.util; +package org.neo4j.driver.internal.net; import org.junit.Test; +import org.neo4j.driver.internal.net.BoltServerAddress; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.*; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputFuzzTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputFuzzTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputFuzzTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputFuzzTest.java index dc73e8e544..c8a1a5a4a9 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputFuzzTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputFuzzTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.junit.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputTest.java index 704d80e891..65027eb97e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/BufferingChunkedInputTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/BufferingChunkedInputTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.junit.Rule; import org.junit.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedInputTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/ChunkedInputTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedInputTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/ChunkedInputTest.java index 8d00443393..e01ef14eb0 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedInputTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/ChunkedInputTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.junit.Rule; import org.junit.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedOutputTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/ChunkedOutputTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedOutputTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/ChunkedOutputTest.java index f8aa6cea8a..87d37ebfc7 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/ChunkedOutputTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/ChunkedOutputTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.hamcrest.MatcherAssert; import org.junit.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnectionTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnectionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnectionTest.java index b1f5068852..d05653d827 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/ConcurrencyGuardingConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnectionTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector; +package org.neo4j.driver.internal.net; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/LoggingResponseHandlerTest.java similarity index 89% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/LoggingResponseHandlerTest.java index e63039c297..24659dad55 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/LoggingResponseHandlerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.junit.Test; @@ -61,7 +61,7 @@ public void shouldLogInitMessage() throws Throwable handler.handleInitMessage( "client", parameters().asMap( ofValue())); // Then - assertEquals( "S: [INIT \"client\"]", log ); + assertEquals( "S: INIT \"client\" {...}", log ); assertEquals( format( new InitMessage( "client", parameters().asMap( ofValue()) ) ), log ); } @@ -72,7 +72,7 @@ public void shouldLogRunMessage() throws Throwable handler.handleRunMessage( "stat", parameters( "value", new String[]{"cat", "cat", "cat"} ).asMap( ofValue()) ); // Then - assertEquals( "S: [RUN \"stat\" {value=[\"cat\", \"cat\", \"cat\"]}]", log ); + assertEquals( "S: RUN \"stat\" {value=[\"cat\", \"cat\", \"cat\"]}", log ); assertEquals( format( new RunMessage( "stat", parameters( "value", new String[]{"cat", "cat", "cat"} ).asMap( ofValue()) ) ), log ); @@ -85,7 +85,7 @@ public void shouldLogPullAllMessage() throws Throwable handler.handlePullAllMessage(); // Then - assertEquals( "S: [PULL_ALL]", log ); + assertEquals( "S: PULL_ALL", log ); assertEquals( format( new PullAllMessage() ), log ); } @@ -96,7 +96,7 @@ public void shouldLogDiscardAllMessage() throws Throwable // When handler.handleDiscardAllMessage(); // Then - assertEquals( "S: [DISCARD_ALL]", log ); + assertEquals( "S: DISCARD_ALL", log ); assertEquals( format( new DiscardAllMessage() ), log ); } @@ -107,7 +107,7 @@ public void shouldLogAckFailureMessage() throws Throwable handler.handleResetMessage(); // Then - assertEquals( "S: [RESET]", log ); + assertEquals( "S: RESET", log ); assertEquals( format( new ResetMessage() ), log ); } @@ -119,7 +119,7 @@ public void shouldLogSuccessMessage() throws Throwable handler.handleSuccessMessage( new HashMap() ); // Then - assertEquals( "S: [SUCCESS {}]", log ); + assertEquals( "S: SUCCESS {}", log ); assertEquals( format( new SuccessMessage( new HashMap() ) ), log ); } @@ -131,7 +131,7 @@ public void shouldLogRecordMessage() throws Throwable handler.handleRecordMessage( new Value[]{} ); // Then - assertEquals( "S: [RECORD []]", log ); + assertEquals( "S: RECORD []", log ); assertEquals( format( new RecordMessage( new Value[]{} ) ), log ); } @@ -143,7 +143,7 @@ public void shouldLogFailureMessage() throws Throwable handler.handleFailureMessage( "code.error", "message" ); // Then - assertEquals( "S: [FAILURE code.error \"message\"]", log ); + assertEquals( "S: FAILURE code.error \"message\"", log ); assertEquals( format( new FailureMessage( "code.error", "message" ) ), log ); } @@ -155,7 +155,7 @@ public void shouldLogIgnoredMessage() throws Throwable handler.handleIgnoredMessage(); // Then - assertEquals( "S: [IGNORED]", log ); + assertEquals( "S: IGNORED {}", log ); assertEquals( format( new IgnoredMessage() ), log ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketUtilsTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/SocketClientTest.java similarity index 70% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketUtilsTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/SocketClientTest.java index 94b3baa231..6628308e77 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketUtilsTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/SocketClientTest.java @@ -16,18 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +package org.neo4j.driver.internal.net; import java.io.IOException; +import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.util.ArrayList; import java.util.List; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.neo4j.driver.internal.logging.DevNullLogger; +import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.v1.exceptions.ClientException; import static org.hamcrest.CoreMatchers.equalTo; @@ -35,20 +39,48 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class SocketUtilsTest +public class SocketClientTest { @Rule public ExpectedException exception = ExpectedException.none(); + // TODO: This is not possible with blocking NIO channels, unless we use inputStreams, but then we can't use + // off-heap buffers. We need to swap to use selectors, which would allow us to time out. + @Test + @Ignore + public void testNetworkTimeout() throws Throwable + { + // Given a server that will never reply + ServerSocket server = new ServerSocket( 0 ); + BoltServerAddress address = new BoltServerAddress( "localhost", server.getLocalPort() ); + + SecurityPlan securityPlan = SecurityPlan.insecure(); + SocketClient client = new SocketClient( address, securityPlan, new DevNullLogger() ); + + // Expect + exception.expect( ClientException.class ); + exception.expectMessage( "database took longer than network timeout (100ms) to reply." ); + + // When + client.start(); + } + + private SocketClient dummyClient() + { + return new SocketClient( BoltServerAddress.LOCAL_DEFAULT, SecurityPlan.insecure(), new DevNullLogger() ); + } + @Test public void shouldReadAllBytes() throws IOException { // Given ByteBuffer buffer = ByteBuffer.allocate( 4 ); ByteAtATimeChannel channel = new ByteAtATimeChannel( new byte[]{0, 1, 2, 3} ); + SocketClient client = dummyClient(); // When - SocketUtils.blockingRead(channel, buffer ); + client.setChannel( channel ); + client.blockingRead( buffer ); buffer.flip(); // Then @@ -65,13 +97,15 @@ public void shouldFailIfConnectionFailsWhileReading() throws IOException ByteBuffer buffer = ByteBuffer.allocate( 4 ); ByteChannel channel = mock( ByteChannel.class ); when(channel.read( buffer )).thenReturn( -1 ); + SocketClient client = dummyClient(); //Expect exception.expect( ClientException.class ); exception.expectMessage( "Expected 4 bytes, received none" ); // When - SocketUtils.blockingRead(channel, buffer ); + client.setChannel( channel ); + client.blockingRead( buffer ); } @Test @@ -80,9 +114,11 @@ public void shouldWriteAllBytes() throws IOException // Given ByteBuffer buffer = ByteBuffer.wrap( new byte[]{0, 1, 2, 3}); ByteAtATimeChannel channel = new ByteAtATimeChannel( new byte[0] ); + SocketClient client = dummyClient(); // When - SocketUtils.blockingWrite(channel, buffer ); + client.setChannel( channel ); + client.blockingWrite( buffer ); // Then assertThat(channel.writtenBytes.get(0), equalTo((byte) 0)); @@ -99,13 +135,15 @@ public void shouldFailIfConnectionFailsWhileWriting() throws IOException buffer.position( 1 ); ByteChannel channel = mock( ByteChannel.class ); when(channel.write( buffer )).thenReturn( -1 ); + SocketClient client = dummyClient(); //Expect exception.expect( ClientException.class ); exception.expectMessage( "Expected 4 bytes, wrote 00" ); // When - SocketUtils.blockingWrite(channel, buffer ); + client.setChannel( channel ); + client.blockingWrite( buffer ); } private static class ByteAtATimeChannel implements ByteChannel diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/SocketResponseHandlerTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandlerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/SocketResponseHandlerTest.java index 7bd71cc066..3aa5dbd1ee 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/SocketResponseHandlerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.connector.socket; +package org.neo4j.driver.internal.net; import org.junit.Before; import org.junit.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/ConnectionInvalidationTest.java similarity index 97% rename from driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/pooling/ConnectionInvalidationTest.java index aaafbf8f05..ad6fbeaf91 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/ConnectionInvalidationTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/ConnectionInvalidationTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; import org.junit.Test; import org.mockito.Mockito; @@ -26,6 +26,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import org.neo4j.driver.internal.net.pooling.PoolSettings; +import org.neo4j.driver.internal.net.pooling.PooledConnection; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.StreamCollector; import org.neo4j.driver.internal.util.Clock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/PooledConnectionTest.java similarity index 96% rename from driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/pooling/PooledConnectionTest.java index abce31e5aa..b860095701 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/PooledConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/PooledConnectionTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; import org.junit.Test; @@ -24,9 +24,11 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import org.neo4j.driver.internal.net.pooling.PoolSettings; +import org.neo4j.driver.internal.net.pooling.PooledConnection; +import org.neo4j.driver.internal.net.pooling.PooledConnectionReleaseConsumer; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.v1.Config; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPoolTest.java similarity index 90% rename from driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java rename to driver/src/test/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPoolTest.java index dcd8e32054..440e65284d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPoolTest.java @@ -16,9 +16,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.pool; +package org.neo4j.driver.internal.net.pooling; -public class InternalConnectionPoolTest +public class SocketConnectionPoolTest { // TODO: write some tests that actually test something diff --git a/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java index bdf9fef079..8b35482810 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/security/TrustOnFirstUseTrustManagerTest.java @@ -30,8 +30,7 @@ import java.security.cert.X509Certificate; import java.util.Scanner; -import org.neo4j.driver.internal.security.TrustOnFirstUseTrustManager; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Logger; import static org.junit.Assert.assertEquals; diff --git a/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java b/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java new file mode 100644 index 0000000000..9a2ed1e101 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.v1; + +import org.junit.Test; +import org.neo4j.driver.internal.logging.ConsoleLogging; +import org.neo4j.driver.internal.net.BoltServerAddress; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class ClusterDriverTest +{ + private static final Config config = Config.build().withLogging( new ConsoleLogging( Level.INFO ) ).toConfig(); + + private Process startStubServer( int port, String script ) throws IOException + { + List command = new ArrayList<>(); + command.addAll( singletonList( "boltstub" ) ); + command.addAll( asList( Integer.toString( port ), script ) ); + ProcessBuilder processBuilder = new ProcessBuilder().inheritIO(); + return processBuilder.command( command ).start(); + } + + @Test + public void shouldDiscoverServers() throws IOException + { + // Given + startStubServer( 9001, "../driver/src/test/resources/discovery.script" ); + URI uri = URI.create( "bolt+discovery://127.0.0.1:9001" ); + + // When + try ( Driver driver = GraphDatabase.driver( uri, config ) ) + { + // Then + List addresses = driver.servers(); + assertThat( addresses.size(), equalTo( 3 ) ); + assertThat( addresses.get( 0 ), equalTo( new BoltServerAddress( "127.0.0.1", 9001 ) ) ); + assertThat( addresses.get( 1 ), equalTo( new BoltServerAddress( "127.0.0.1", 9002 ) ) ); + assertThat( addresses.get( 2 ), equalTo( new BoltServerAddress( "127.0.0.1", 9003 ) ) ); + } + + } + +} \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java b/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java new file mode 100644 index 0000000000..1eda50a286 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package org.neo4j.driver.v1; + +import org.junit.Rule; +import org.junit.Test; +import org.neo4j.driver.internal.net.BoltServerAddress; +import org.neo4j.driver.v1.util.TestNeo4jSession; + +import java.net.URI; +import java.util.Collection; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.neo4j.driver.v1.Values.parameters; + +public class DirectDriverTest +{ + @Rule + public TestNeo4jSession session = new TestNeo4jSession(); + + @Test + public void shouldUseDefaultPortIfMissing() + { + // Given + URI uri = URI.create( "bolt://localhost" ); + + // When + Driver driver = GraphDatabase.driver( uri ); + + // Then + Collection addresses = driver.servers(); + assertThat( addresses.size(), equalTo( 1 ) ); + for ( BoltServerAddress address : addresses ) + { + assertThat( address.port(), equalTo( BoltServerAddress.DEFAULT_PORT ) ); + } + + } + + @Test + public void shouldRegisterSingleServer() + { + // Given + URI uri = URI.create( "bolt://localhost:7687" ); + BoltServerAddress address = BoltServerAddress.from( uri ); + + // When + Driver driver = GraphDatabase.driver( uri ); + + // Then + Collection addresses = driver.servers(); + assertThat( addresses.size(), equalTo( 1 ) ); + assertThat( addresses.contains( address ), equalTo( true ) ); + + } + + @Test + public void shouldBeAbleRunCypher() + { + // Given + URI uri = URI.create( "bolt://localhost:7687" ); + int x; + + // When + try ( Driver driver = GraphDatabase.driver( uri ) ) + { + try ( Session session = driver.session() ) + { + Record record = session.run( "RETURN {x}", parameters( "x", 1 ) ).single(); + x = record.get( 0 ).asInt(); + } + } + + // Then + assertThat( x, equalTo( 1 ) ); + + } + +} \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java b/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java deleted file mode 100644 index 9cc6b88a61..0000000000 --- a/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ -package org.neo4j.driver.v1; - -import javadoctest.DocSnippet; -import javadoctest.DocTestRunner; -import org.junit.Rule; -import org.junit.runner.RunWith; - -import java.util.LinkedList; -import java.util.List; - -import org.neo4j.driver.v1.util.TestNeo4jSession; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.junit.Assert.assertEquals; - -@RunWith( DocTestRunner.class ) -public class DriverDocIT -{ - @Rule - public TestNeo4jSession session = new TestNeo4jSession(); - - /** @see Driver */ - @SuppressWarnings("unchecked") - public void exampleUsage( DocSnippet snippet ) - { - // given - snippet.addImport( List.class ); - snippet.addImport( LinkedList.class ); - session.run( "MATCH (n) DETACH DELETE (n)" ); - - // when - snippet.run(); - - // then it should've created a bunch of data - StatementResult result = session.run( "MATCH (n) RETURN count(n)" ); - assertEquals( 3, result.single().get( 0 ).asInt() ); - assertThat( (List)snippet.get( "names" ), containsInAnyOrder( "Bob", "Alice", "Tina" ) ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java index 24b95333f2..1620b2bfcc 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java +++ b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java @@ -21,13 +21,17 @@ import org.junit.Rule; import org.junit.Test; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.ClusterDriver; +import org.neo4j.driver.internal.DirectDriver; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.util.TestNeo4jSession; import java.net.URI; +import java.util.Collection; import java.util.Set; import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.*; import static org.neo4j.driver.v1.Values.parameters; @@ -37,60 +41,30 @@ public class GraphDatabaseTest public TestNeo4jSession session = new TestNeo4jSession(); @Test - public void directDriverShouldRegisterSingleServer() + public void boltSchemeShouldInstantiateDirectDriver() { // Given URI uri = URI.create( "bolt://localhost:7687" ); - BoltServerAddress address = BoltServerAddress.from( uri ); // When Driver driver = GraphDatabase.driver( uri ); // Then - Set addresses = driver.servers(); - assertThat( addresses.size(), equalTo( 1 ) ); - assertThat( addresses.contains( address ), equalTo( true ) ); + assertThat( driver, instanceOf( DirectDriver.class ) ); } @Test - public void missingPortInURIShouldUseDefault() + public void boltPlusDiscoverySchemeShouldInstantiateClusterDriver() { // Given - URI uri = URI.create( "bolt://localhost" ); + URI uri = URI.create( "bolt+discovery://localhost:7687" ); // When Driver driver = GraphDatabase.driver( uri ); // Then - Set addresses = driver.servers(); - assertThat( addresses.size(), equalTo( 1 ) ); - for ( BoltServerAddress address : addresses ) - { - assertThat( address.port(), equalTo( BoltServerAddress.DEFAULT_PORT ) ); - } - - } - - @Test - public void shouldBeAbleRunCypherThroughDirectDriver() - { - // Given - URI uri = URI.create( "bolt://localhost:7687" ); - int x; - - // When - try ( Driver driver = GraphDatabase.driver( uri ) ) - { - try ( Session session = driver.session() ) - { - Record record = session.run( "RETURN {x}", parameters( "x", 1 ) ).single(); - x = record.get( 0 ).asInt(); - } - } - - // Then - assertThat( x, equalTo( 1 ) ); + assertThat( driver, instanceOf( ClusterDriver.class ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java index bd28eecdf2..cfa05487a8 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java @@ -28,8 +28,8 @@ import java.util.LinkedList; import java.util.Queue; -import org.neo4j.driver.internal.connector.socket.SocketClient; -import org.neo4j.driver.internal.connector.socket.SocketResponseHandler; +import org.neo4j.driver.internal.net.SocketClient; +import org.neo4j.driver.internal.net.SocketResponseHandler; import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.internal.messaging.InitMessage; import org.neo4j.driver.internal.messaging.Message; diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java index 0669f70c65..75b44d9f03 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/TLSSocketChannelIT.java @@ -27,8 +27,6 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.URI; import java.nio.channels.SocketChannel; import java.security.cert.X509Certificate; @@ -37,7 +35,7 @@ import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.security.TLSSocketChannel; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.*; import org.neo4j.driver.internal.util.CertificateTool; import org.neo4j.driver.v1.util.CertificateToolTest; @@ -87,13 +85,13 @@ private void performTLSHandshakeUsingKnownCerts( File knownCerts ) throws Throwa // When - SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, address, new DevNullLogger() ); + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( knownCerts, address, new DevNullLogger() ); TLSSocketChannel sslChannel = new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); // Then - verify( logger, atLeastOnce() ).debug( "TLS connection closed" ); + verify( logger, atLeastOnce() ).debug( "~~ [CLOSED SECURE CHANNEL]" ); } @Test @@ -130,14 +128,14 @@ public void shouldPerformTLSHandshakeWithTrustedCert() throws Throwable channel.connect( address.toSocketAddress() ); // When - SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), rootCert ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( rootCert ); TLSSocketChannel sslChannel = new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); // Then - verify( logger, atLeastOnce() ).debug( "TLS connection closed" ); + verify( logger, atLeastOnce() ).debug( "~~ [OPENING SECURE CHANNEL]" ); } finally { @@ -160,7 +158,7 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa createFakeServerCertPairInKnownCerts( address, knownCerts ); // When & Then - SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( AuthTokens.none(), knownCerts, address, new DevNullLogger() ); + SecurityPlan securityPlan = SecurityPlan.forTrustOnFirstUse( knownCerts, address, new DevNullLogger() ); TLSSocketChannel sslChannel = null; try { @@ -213,7 +211,7 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr CertificateTool.saveX509Cert( aRandomCert, trustedCertFile ); // When & Then - SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), trustedCertFile ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( trustedCertFile ); TLSSocketChannel sslChannel = null; try { @@ -245,12 +243,12 @@ public void shouldPerformTLSHandshakeWithTheSameTrustedServerCert() throws Throw // When URI url = URI.create( "localhost:7687" ); - SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( AuthTokens.none(), Neo4jSettings.DEFAULT_TLS_CERT_FILE ); + SecurityPlan securityPlan = SecurityPlan.forSignedCertificates( Neo4jSettings.DEFAULT_TLS_CERT_FILE ); TLSSocketChannel sslChannel = new TLSSocketChannel( address, securityPlan, channel, logger ); sslChannel.close(); // Then - verify( logger, atLeastOnce() ).debug( "TLS connection closed" ); + verify( logger, atLeastOnce() ).debug( "~~ [OPENING SECURE CHANNEL]" ); } @Test diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java b/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java index ab26779efa..9e7f68f54b 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java @@ -27,7 +27,7 @@ import java.util.List; import java.util.Map; -import org.neo4j.driver.internal.connector.socket.ChunkedInput; +import org.neo4j.driver.internal.net.ChunkedInput; import org.neo4j.driver.internal.messaging.ResetMessage; import org.neo4j.driver.internal.messaging.DiscardAllMessage; import org.neo4j.driver.internal.messaging.FailureMessage; diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java index 560bad65ff..ca2daa90ca 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java @@ -20,13 +20,12 @@ import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; import java.net.StandardSocketOptions; import java.net.URI; import java.nio.channels.SocketChannel; import java.util.Map; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java index 8e38f6dc07..1f32e2ce18 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java @@ -28,7 +28,7 @@ import java.net.URI; import java.net.URL; -import org.neo4j.driver.internal.util.BoltServerAddress; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Session; diff --git a/driver/src/test/resources/discovery.script b/driver/src/test/resources/discovery.script new file mode 100644 index 0000000000..eb82579397 --- /dev/null +++ b/driver/src/test/resources/discovery.script @@ -0,0 +1,11 @@ +!: AUTO INIT +!: AUTO RESET +!: AUTO RUN "RETURN 1 // JavaDriver poll to test connection" {} + +C: RUN "CALL dbms.cluster.discoverMembers" {} + PULL_ALL +S: SUCCESS {"fields": ["address"]} + RECORD ["127.0.0.1:9001"] + RECORD ["127.0.0.1:9002"] + RECORD ["127.0.0.1:9003"] + SUCCESS {} From 88e757e54daa1d7ec5fe21b72f1fd67378b87cf5 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 10:34:45 +0100 Subject: [PATCH 05/10] Rolled back cluster feature --- .../neo4j/driver/internal/ClusterDriver.java | 154 ------------------ .../org/neo4j/driver/v1/GraphDatabase.java | 17 +- .../neo4j/driver/v1/ClusterDriverTest.java | 70 -------- .../neo4j/driver/v1/GraphDatabaseTest.java | 20 --- driver/src/test/resources/discovery.script | 11 -- 5 files changed, 7 insertions(+), 265 deletions(-) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java delete mode 100644 driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java delete mode 100644 driver/src/test/resources/discovery.script diff --git a/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java b/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java deleted file mode 100644 index 9cc2cf9eb8..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/ClusterDriver.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -package org.neo4j.driver.internal; - -import org.neo4j.driver.internal.net.BoltServerAddress; -import org.neo4j.driver.internal.net.pooling.PoolSettings; -import org.neo4j.driver.internal.net.pooling.SocketConnectionPool; -import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.v1.Logging; -import org.neo4j.driver.v1.Record; -import org.neo4j.driver.v1.Session; -import org.neo4j.driver.v1.StatementResult; -import org.neo4j.driver.v1.exceptions.ClientException; -import org.neo4j.driver.v1.util.Function; - -import java.util.LinkedList; -import java.util.List; - -import static java.lang.String.format; - -public class ClusterDriver extends BaseDriver -{ - private static final String DISCOVER_MEMBERS = "dbms.cluster.discoverMembers"; - private static final String ACQUIRE_ENDPOINTS = "dbms.cluster.acquireEndpoints"; - - private final ConnectionPool connections; - - public ClusterDriver( BoltServerAddress seedAddress, SessionParameters sessionParameters, SecurityPlan securityPlan, - PoolSettings poolSettings, Logging logging ) - { - super( seedAddress, sessionParameters, securityPlan, logging ); - this.connections = new SocketConnectionPool( sessionParameters, securityPlan, poolSettings, logging ); - discover(); - } - - public void discover() - { - final List newServers = new LinkedList<>( ); - try - { - call( DISCOVER_MEMBERS, new Function() - { - @Override - public Integer apply( Record record ) - { - newServers.add( new BoltServerAddress( record.get( "address" ).asString() ) ); - return 0; - } - } ); - this.servers.clear(); - this.servers.addAll( newServers ); - log.debug( "~~ [MEMBERS] -> %s", newServers ); - } - catch ( ClientException ex ) - { - if ( ex.code().equals( "Neo.ClientError.Procedure.ProcedureNotFound" ) ) - { - // no discovery available; keep servers as they are - log.warn( "C: Discovery failed; could not find procedure %s", DISCOVER_MEMBERS ); - } - else - { - throw ex; - } - } - } - -// public void acquire(final String role) -// { -// BoltServerAddress server; -// try -// { -// call( ACQUIRE_ENDPOINTS, new Function() -// { -// @Override -// public Integer apply( Record record ) -// { -// if (record.get( "role" ).asString().equals( role )) -// { -// server = new BoltServerAddress( record.get( "address" ).asString() ); -// } -// return 0; -// } -// } ); -// this.servers.clear(); -// this.servers.addAll( newServers ); -// log.debug( "~~ [MEMBERS] -> %s", newServers ); -// } -// catch ( ClientException ex ) -// { -// if ( ex.code().equals( "Neo.ClientError.Procedure.ProcedureNotFound" ) ) -// { -// // no discovery available; keep servers as they are -// log.warn( "C: Discovery failed; could not find procedure %s", DISCOVER_MEMBERS ); -// } -// else -// { -// throw ex; -// } -// } -// } - -// public void - - void call( String procedureName, Function recorder ) - { - try ( Session session = new InternalSession( connections.acquire( randomServer() ), log ) ) - { - StatementResult records = session.run( format( "CALL %s", procedureName ) ); - while ( records.hasNext() ) - { - recorder.apply( records.next() ); - } - } - } - - @Override - public Session session() - { - throw new UnsupportedOperationException(); - } - - @Override - public void close() - { - try - { - connections.close(); - } - catch ( Exception ex ) - { - log.error( format( "~~ [ERROR] %s", ex.getMessage() ), ex ); - } - } - -} 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 c76448dfde..c131addbd4 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -18,18 +18,17 @@ */ package org.neo4j.driver.v1; -import java.io.IOException; -import java.net.URI; -import java.security.GeneralSecurityException; - -import org.neo4j.driver.internal.ClusterDriver; import org.neo4j.driver.internal.DirectDriver; import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.net.pooling.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.exceptions.ClientException; +import java.io.IOException; +import java.net.URI; +import java.security.GeneralSecurityException; + import static java.lang.String.format; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED; import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL; @@ -152,7 +151,7 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) SecurityPlan securityPlan; try { - securityPlan = createSecurityPlan( address, authToken, config ); + securityPlan = createSecurityPlan( address, config ); } catch ( GeneralSecurityException | IOException ex ) { @@ -169,8 +168,6 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) { case "bolt": return new DirectDriver( address, sessionParameters, securityPlan, poolSettings, config.logging() ); - case "bolt+discovery": - return new ClusterDriver( address, sessionParameters, securityPlan, poolSettings, config.logging() ); default: throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) ); } @@ -180,7 +177,7 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) * Establish a complete SecurityPlan based on the details provided for * driver construction. */ - private static SecurityPlan createSecurityPlan( BoltServerAddress address, AuthToken authToken, Config config ) + private static SecurityPlan createSecurityPlan( BoltServerAddress address, Config config ) throws GeneralSecurityException, IOException { Config.EncryptionLevel encryptionLevel = config.encryptionLevel(); diff --git a/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java b/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java deleted file mode 100644 index 9a2ed1e101..0000000000 --- a/driver/src/test/java/org/neo4j/driver/v1/ClusterDriverTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -package org.neo4j.driver.v1; - -import org.junit.Test; -import org.neo4j.driver.internal.logging.ConsoleLogging; -import org.neo4j.driver.internal.net.BoltServerAddress; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -public class ClusterDriverTest -{ - private static final Config config = Config.build().withLogging( new ConsoleLogging( Level.INFO ) ).toConfig(); - - private Process startStubServer( int port, String script ) throws IOException - { - List command = new ArrayList<>(); - command.addAll( singletonList( "boltstub" ) ); - command.addAll( asList( Integer.toString( port ), script ) ); - ProcessBuilder processBuilder = new ProcessBuilder().inheritIO(); - return processBuilder.command( command ).start(); - } - - @Test - public void shouldDiscoverServers() throws IOException - { - // Given - startStubServer( 9001, "../driver/src/test/resources/discovery.script" ); - URI uri = URI.create( "bolt+discovery://127.0.0.1:9001" ); - - // When - try ( Driver driver = GraphDatabase.driver( uri, config ) ) - { - // Then - List addresses = driver.servers(); - assertThat( addresses.size(), equalTo( 3 ) ); - assertThat( addresses.get( 0 ), equalTo( new BoltServerAddress( "127.0.0.1", 9001 ) ) ); - assertThat( addresses.get( 1 ), equalTo( new BoltServerAddress( "127.0.0.1", 9002 ) ) ); - assertThat( addresses.get( 2 ), equalTo( new BoltServerAddress( "127.0.0.1", 9003 ) ) ); - } - - } - -} \ No newline at end of file diff --git a/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java index 1620b2bfcc..647386e4e4 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java +++ b/driver/src/test/java/org/neo4j/driver/v1/GraphDatabaseTest.java @@ -21,19 +21,13 @@ import org.junit.Rule; import org.junit.Test; -import org.neo4j.driver.internal.ClusterDriver; import org.neo4j.driver.internal.DirectDriver; -import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.util.TestNeo4jSession; import java.net.URI; -import java.util.Collection; -import java.util.Set; -import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.*; -import static org.neo4j.driver.v1.Values.parameters; public class GraphDatabaseTest { @@ -54,18 +48,4 @@ public void boltSchemeShouldInstantiateDirectDriver() } - @Test - public void boltPlusDiscoverySchemeShouldInstantiateClusterDriver() - { - // Given - URI uri = URI.create( "bolt+discovery://localhost:7687" ); - - // When - Driver driver = GraphDatabase.driver( uri ); - - // Then - assertThat( driver, instanceOf( ClusterDriver.class ) ); - - } - } \ No newline at end of file diff --git a/driver/src/test/resources/discovery.script b/driver/src/test/resources/discovery.script deleted file mode 100644 index eb82579397..0000000000 --- a/driver/src/test/resources/discovery.script +++ /dev/null @@ -1,11 +0,0 @@ -!: AUTO INIT -!: AUTO RESET -!: AUTO RUN "RETURN 1 // JavaDriver poll to test connection" {} - -C: RUN "CALL dbms.cluster.discoverMembers" {} - PULL_ALL -S: SUCCESS {"fields": ["address"]} - RECORD ["127.0.0.1:9001"] - RECORD ["127.0.0.1:9002"] - RECORD ["127.0.0.1:9003"] - SUCCESS {} From 1d2bbb7f789990268c80d5e1947c94e43ae4c59b Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 12:44:41 +0100 Subject: [PATCH 06/10] Reworked example test due to new behaviour --- .../org/neo4j/docs/driver/ExamplesIT.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java b/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java index e725456773..358edf9b5c 100644 --- a/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java +++ b/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java @@ -21,19 +21,18 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.driver.v1.Session; +import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.util.StdIOCapture; import org.neo4j.driver.v1.util.TestNeo4j; -import static java.util.Arrays.asList; +import java.io.FileNotFoundException; +import static java.util.Arrays.asList; import static junit.framework.TestCase.assertEquals; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -288,11 +287,22 @@ public void trustOnFirstUse() throws Throwable @Test public void trustSignedCertificates() throws Throwable { - Driver driver = Examples.trustSignedCertificates(); - - // Then - assertNotNull( driver ); - driver.close(); + try + { + Driver driver = Examples.trustSignedCertificates(); + } + catch ( ClientException ex ) + { + // This will ultimately fail as it can't find "/path/to/ca-certificate.pem" + // We'll check for that error specifically and OK it, but die for everything + // else. Previously, this was not evaluated on driver construction so never + // occurred. + // TODO: find a way to mock this properly + assertThat( ex.getMessage(), equalTo( "Unable to establish SSL parameters" ) ); + Throwable cause = ex.getCause(); + assertThat( cause, instanceOf( FileNotFoundException.class ) ); + assertThat( cause.getMessage(), equalTo( "/path/to/ca-certificate.pem (No such file or directory)" ) ); + } } @Test From 64139b763725b96539b9b7bfb868a56872751ca6 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 13:43:30 +0100 Subject: [PATCH 07/10] SessionParameters->ConnectionSettings --- .../java/org/neo4j/driver/internal/BaseDriver.java | 6 +++--- ...SessionParameters.java => ConnectionSettings.java} | 10 +++++++--- .../java/org/neo4j/driver/internal/DirectDriver.java | 6 +++--- .../internal/net/pooling/SocketConnectionPool.java | 11 +++++------ .../main/java/org/neo4j/driver/v1/GraphDatabase.java | 8 ++++---- 5 files changed, 22 insertions(+), 19 deletions(-) rename driver/src/main/java/org/neo4j/driver/internal/{SessionParameters.java => ConnectionSettings.java} (87%) diff --git a/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java index 1cc3cc9ef1..7ece41f15e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java @@ -31,15 +31,15 @@ abstract class BaseDriver implements Driver { - private final SessionParameters sessionParameters; + private final ConnectionSettings connectionSettings; private final SecurityPlan securityPlan; protected final List servers = new LinkedList<>(); protected final Logger log; - BaseDriver( BoltServerAddress address, SessionParameters sessionParameters, SecurityPlan securityPlan, Logging logging ) + BaseDriver( BoltServerAddress address, ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging ) { this.servers.add( address ); - this.sessionParameters = sessionParameters; + this.connectionSettings = connectionSettings; this.securityPlan = securityPlan; this.log = logging.getLog( Session.LOG_NAME ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java b/driver/src/main/java/org/neo4j/driver/internal/ConnectionSettings.java similarity index 87% rename from driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java rename to driver/src/main/java/org/neo4j/driver/internal/ConnectionSettings.java index cc028fed90..f110359173 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/SessionParameters.java +++ b/driver/src/main/java/org/neo4j/driver/internal/ConnectionSettings.java @@ -24,7 +24,11 @@ import static java.lang.String.format; -public class SessionParameters +/** + * The connection settings are used whenever a new connection is + * established to a server, specifically as part of the INIT request. + */ +public class ConnectionSettings { public static final String DEFAULT_USER_AGENT = format( "neo4j-java/%s", driverVersion() ); @@ -50,13 +54,13 @@ private static String driverVersion() private final AuthToken authToken; private final String userAgent; - public SessionParameters( AuthToken authToken, String userAgent ) + public ConnectionSettings( AuthToken authToken, String userAgent ) { this.authToken = authToken; this.userAgent = userAgent; } - public SessionParameters( AuthToken authToken ) + public ConnectionSettings( AuthToken authToken ) { this( authToken, DEFAULT_USER_AGENT ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java index 32b23e67dc..79e4dc16d2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java @@ -33,11 +33,11 @@ public class DirectDriver extends BaseDriver { private final ConnectionPool connections; - public DirectDriver( BoltServerAddress address, SessionParameters sessionParameters, SecurityPlan securityPlan, + public DirectDriver( BoltServerAddress address, ConnectionSettings connectionSettings, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { - super( address, sessionParameters, securityPlan, logging ); - this.connections = new SocketConnectionPool( sessionParameters, securityPlan, poolSettings, logging ); + super( address, connectionSettings, securityPlan, logging ); + this.connections = new SocketConnectionPool( connectionSettings, securityPlan, poolSettings, logging ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java index 16878d0f62..9c62ebe728 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/net/pooling/SocketConnectionPool.java @@ -24,7 +24,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.net.ConcurrencyGuardingConnection; import org.neo4j.driver.internal.net.SocketConnection; @@ -38,7 +38,6 @@ import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.exceptions.ClientException; -import org.neo4j.driver.v1.exceptions.Neo4jException; /** * The pool is designed to buffer certain amount of free sessions into session pool. When closing a session, we first @@ -62,7 +61,7 @@ public class SocketConnectionPool implements ConnectionPool private final Clock clock = Clock.SYSTEM; - private final SessionParameters sessionParameters; + private final ConnectionSettings connectionSettings; private final SecurityPlan securityPlan; private final PoolSettings poolSettings; private final Logging logging; @@ -70,10 +69,10 @@ public class SocketConnectionPool implements ConnectionPool /** Shutdown flag */ private final AtomicBoolean stopped = new AtomicBoolean( false ); - public SocketConnectionPool( SessionParameters sessionParameters, SecurityPlan securityPlan, + public SocketConnectionPool( ConnectionSettings connectionSettings, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { - this.sessionParameters = sessionParameters; + this.connectionSettings = connectionSettings; this.securityPlan = securityPlan; this.poolSettings = poolSettings; this.logging = logging; @@ -86,7 +85,7 @@ private Connection connect( BoltServerAddress address ) throws ClientException // Because SocketConnection is not thread safe, wrap it in this guard // to ensure concurrent access leads causes application errors conn = new ConcurrencyGuardingConnection( conn ); - conn.init( sessionParameters.userAgent(), tokenAsMap( sessionParameters.authToken() ) ); + conn.init( connectionSettings.userAgent(), tokenAsMap( connectionSettings.authToken() ) ); return conn; } 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 c131addbd4..0b7d5c69cd 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -19,7 +19,7 @@ package org.neo4j.driver.v1; import org.neo4j.driver.internal.DirectDriver; -import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.net.pooling.PoolSettings; import org.neo4j.driver.internal.security.SecurityPlan; @@ -138,8 +138,8 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) BoltServerAddress address = BoltServerAddress.from( uri ); // Collate session parameters - SessionParameters sessionParameters = - new SessionParameters( authToken == null ? AuthTokens.none() : authToken ); + ConnectionSettings connectionSettings = + new ConnectionSettings( authToken == null ? AuthTokens.none() : authToken ); // Make sure we have some configuration to play with if (config == null) @@ -167,7 +167,7 @@ public static Driver driver( URI uri, AuthToken authToken, Config config ) switch ( scheme ) { case "bolt": - return new DirectDriver( address, sessionParameters, securityPlan, poolSettings, config.logging() ); + return new DirectDriver( address, connectionSettings, securityPlan, poolSettings, config.logging() ); default: throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) ); } From 2e19061df2c6151341a44c2d6f7f850543039f14 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 13:47:28 +0100 Subject: [PATCH 08/10] DriverDocIT back --- .../main/java/org/neo4j/driver/v1/Driver.java | 39 +++++++++++++ .../java/org/neo4j/driver/v1/DriverDocIT.java | 58 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index 1100028943..a3061c073c 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -26,6 +26,45 @@ /** * A Neo4j database driver, through which you can create {@link Session sessions} to run statements against the database. *

+ * An example: + *

+ * {@code
+ * // Create a driver with default configuration
+ * Driver driver = GraphDatabase.driver( "bolt://localhost:7687" );
+ *
+ * // Establish a session
+ * Session session = driver.session();
+ *
+ * // Running a simple statement can be done like this
+ * session.run( "CREATE (n {name:'Bob'})" );
+ *
+ * // Or, run multiple statements together in an atomic transaction:
+ * try( Transaction tx = session.beginTransaction() )
+ * {
+ *     tx.run( "CREATE (n {name:'Alice'})" );
+ *     tx.run( "CREATE (n {name:'Tina'})" );
+ *     tx.success();
+ * }
+ *
+ * // Retrieve results
+ * StatementResult result = session.run( "MATCH (n) RETURN n.name" );
+ * List names = new LinkedList<>();
+ * while( result.hasNext() )
+ * {
+ *     names.add( result.next().get("n.name").asString() );
+ * }
+ *
+ * // Sessions are pooled, to avoid the overhead of creating new connections - this means
+ * // it is very important to close your session when you are done with it, otherwise you will
+ * // run out of sessions.
+ * session.close();
+ *
+ * // And, to clean up resources, always close the driver when your application is done
+ * driver.close();
+ * }
+ * 
+ *

+ * * A driver maintains a connection pool for each Neo4j instance. For resource efficiency reasons you are encouraged * to use the same driver instance across your application. You can control the connection pooling behavior when you * create the driver using the {@link Config} you pass into {@link GraphDatabase#driver(URI, Config)}. diff --git a/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java b/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java new file mode 100644 index 0000000000..9cc6b88a61 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/v1/DriverDocIT.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ +package org.neo4j.driver.v1; + +import javadoctest.DocSnippet; +import javadoctest.DocTestRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import java.util.LinkedList; +import java.util.List; + +import org.neo4j.driver.v1.util.TestNeo4jSession; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; + +@RunWith( DocTestRunner.class ) +public class DriverDocIT +{ + @Rule + public TestNeo4jSession session = new TestNeo4jSession(); + + /** @see Driver */ + @SuppressWarnings("unchecked") + public void exampleUsage( DocSnippet snippet ) + { + // given + snippet.addImport( List.class ); + snippet.addImport( LinkedList.class ); + session.run( "MATCH (n) DETACH DELETE (n)" ); + + // when + snippet.run(); + + // then it should've created a bunch of data + StatementResult result = session.run( "MATCH (n) RETURN count(n)" ); + assertEquals( 3, result.single().get( 0 ).asInt() ); + assertThat( (List)snippet.get( "names" ), containsInAnyOrder( "Bob", "Alice", "Tina" ) ); + } +} From 539ee70ecb20ece12c11496c2cc76bd727bb6541 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 14:18:18 +0100 Subject: [PATCH 09/10] Removed cluster artifacts from Driver classes --- .../org/neo4j/driver/internal/BaseDriver.java | 21 +------------------ .../neo4j/driver/internal/DirectDriver.java | 10 +++++---- .../main/java/org/neo4j/driver/v1/Driver.java | 10 --------- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java index 7ece41f15e..b2d82c8b3f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java @@ -19,27 +19,19 @@ package org.neo4j.driver.internal; -import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Logger; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; - abstract class BaseDriver implements Driver { - private final ConnectionSettings connectionSettings; private final SecurityPlan securityPlan; - protected final List servers = new LinkedList<>(); protected final Logger log; - BaseDriver( BoltServerAddress address, ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging ) + BaseDriver( SecurityPlan securityPlan, Logging logging ) { - this.servers.add( address ); - this.connectionSettings = connectionSettings; this.securityPlan = securityPlan; this.log = logging.getLog( Session.LOG_NAME ); } @@ -50,15 +42,4 @@ public boolean isEncrypted() return securityPlan.requiresEncryption(); } - @Override - public List servers() - { - return Collections.unmodifiableList( servers ); - } - - protected BoltServerAddress randomServer() - { - return servers.get( ThreadLocalRandom.current().nextInt( 0, servers.size() ) ); - } - } diff --git a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java index 79e4dc16d2..94c56c1f13 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java @@ -19,11 +19,11 @@ package org.neo4j.driver.internal; -import org.neo4j.driver.internal.net.pooling.SocketConnectionPool; +import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.net.pooling.PoolSettings; +import org.neo4j.driver.internal.net.pooling.SocketConnectionPool; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; -import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.v1.Logging; import org.neo4j.driver.v1.Session; @@ -31,19 +31,21 @@ public class DirectDriver extends BaseDriver { + private final BoltServerAddress address; private final ConnectionPool connections; public DirectDriver( BoltServerAddress address, ConnectionSettings connectionSettings, SecurityPlan securityPlan, PoolSettings poolSettings, Logging logging ) { - super( address, connectionSettings, securityPlan, logging ); + super( securityPlan, logging ); + this.address = address; this.connections = new SocketConnectionPool( connectionSettings, securityPlan, poolSettings, logging ); } @Override public Session session() { - return new InternalSession( connections.acquire( randomServer() ), log ); + return new InternalSession( connections.acquire( address ), log ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index a3061c073c..058aca817d 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -19,9 +19,6 @@ package org.neo4j.driver.v1; import java.net.URI; -import java.util.List; - -import org.neo4j.driver.internal.net.BoltServerAddress; /** * A Neo4j database driver, through which you can create {@link Session sessions} to run statements against the database. @@ -73,13 +70,6 @@ */ public interface Driver extends AutoCloseable { - /** - * Return a collection of the server addresses known by this driver. - * - * @return list of server addresses - */ - List servers(); - /** * Return a flag to indicate whether or not encryption is used for this driver. * From b5f8cc0ab0578c42c2ca390059c6654c9f53633b Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Mon, 25 Jul 2016 14:24:19 +0100 Subject: [PATCH 10/10] Removed future tests --- .../org/neo4j/driver/v1/DirectDriverTest.java | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java diff --git a/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java b/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java deleted file mode 100644 index 1eda50a286..0000000000 --- a/driver/src/test/java/org/neo4j/driver/v1/DirectDriverTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2002-2016 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -package org.neo4j.driver.v1; - -import org.junit.Rule; -import org.junit.Test; -import org.neo4j.driver.internal.net.BoltServerAddress; -import org.neo4j.driver.v1.util.TestNeo4jSession; - -import java.net.URI; -import java.util.Collection; - -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; -import static org.neo4j.driver.v1.Values.parameters; - -public class DirectDriverTest -{ - @Rule - public TestNeo4jSession session = new TestNeo4jSession(); - - @Test - public void shouldUseDefaultPortIfMissing() - { - // Given - URI uri = URI.create( "bolt://localhost" ); - - // When - Driver driver = GraphDatabase.driver( uri ); - - // Then - Collection addresses = driver.servers(); - assertThat( addresses.size(), equalTo( 1 ) ); - for ( BoltServerAddress address : addresses ) - { - assertThat( address.port(), equalTo( BoltServerAddress.DEFAULT_PORT ) ); - } - - } - - @Test - public void shouldRegisterSingleServer() - { - // Given - URI uri = URI.create( "bolt://localhost:7687" ); - BoltServerAddress address = BoltServerAddress.from( uri ); - - // When - Driver driver = GraphDatabase.driver( uri ); - - // Then - Collection addresses = driver.servers(); - assertThat( addresses.size(), equalTo( 1 ) ); - assertThat( addresses.contains( address ), equalTo( true ) ); - - } - - @Test - public void shouldBeAbleRunCypher() - { - // Given - URI uri = URI.create( "bolt://localhost:7687" ); - int x; - - // When - try ( Driver driver = GraphDatabase.driver( uri ) ) - { - try ( Session session = driver.session() ) - { - Record record = session.run( "RETURN {x}", parameters( "x", 1 ) ).single(); - x = record.get( 0 ).asInt(); - } - } - - // Then - assertThat( x, equalTo( 1 ) ); - - } - -} \ No newline at end of file