diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java index a42dbe761f35f..4fed2e51cb26d 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java @@ -41,8 +41,8 @@ import org.neo4j.bolt.transport.NettyServer.ProtocolInitializer; import org.neo4j.bolt.transport.SocketTransport; import org.neo4j.bolt.transport.TransportThrottleGroup; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactoryImpl; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.ListenSocketAddress; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java index 0d715071b2ad4..601a580ff9773 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java @@ -40,7 +40,7 @@ public interface BoltMessageLogger void serverError( String eventName, Status status ); - void logInit( String userAgent ); + void logUserAgent( String userAgent ); void logRun(); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java index 36759650ea3ba..fe35fe7e0caf5 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java @@ -102,7 +102,7 @@ public void serverError( String eventName, Status status ) } @Override - public void logInit( String userAgent ) + public void logUserAgent( String userAgent ) { clientEvent( "INIT", () -> userAgent); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java index 43512fc088a20..9e55a2199981e 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java @@ -73,7 +73,7 @@ public void serverError( String eventName, Status status ) } @Override - public void logInit( String userAgent ) + public void logUserAgent( String userAgent ) { } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java index 263195999208d..4127b808f9e5d 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java @@ -47,4 +47,6 @@ public interface BoltStateMachine extends ManagedBoltStateMachine, AutoCloseable @Override void close(); + + String id(); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java similarity index 93% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactory.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java index ab149071b1e94..becb84efb9a85 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactory.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java @@ -17,10 +17,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.runtime.BoltStateMachine; /** * Factory class for Bolt runtime environments. diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java similarity index 74% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImpl.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java index c2ae72ac535bc..d094159b5f459 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImpl.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java @@ -17,16 +17,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; import java.time.Clock; import java.time.Duration; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.runtime.BoltStateMachine; -import org.neo4j.bolt.runtime.BoltStateMachineSPI; -import org.neo4j.bolt.runtime.TransactionStateMachineSPI; import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1SPI; +import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltProtocolV3; +import org.neo4j.bolt.v3.BoltStateMachineV3; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.kernel.AvailabilityGuard; import org.neo4j.kernel.api.bolt.BoltConnectionTracker; @@ -62,7 +65,18 @@ public BoltStateMachineFactoryImpl( GraphDatabaseAPI db, UsageData usageData, Av @Override public BoltStateMachine newStateMachine( long protocolVersion, BoltChannel boltChannel ) { - return newStateMachineV1( boltChannel ); + if ( protocolVersion == BoltProtocolV1.VERSION || protocolVersion == BoltProtocolV2.VERSION ) + { + return newStateMachineV1( boltChannel ); + } + else if ( protocolVersion == BoltProtocolV3.VERSION ) + { + return newStateMachineV3( boltChannel ); + } + else + { + return null; + } } private BoltStateMachine newStateMachineV1( BoltChannel boltChannel ) @@ -72,6 +86,13 @@ private BoltStateMachine newStateMachineV1( BoltChannel boltChannel ) return new BoltStateMachineV1( boltSPI, boltChannel, clock ); } + private BoltStateMachine newStateMachineV3( BoltChannel boltChannel ) + { + TransactionStateMachineSPI transactionSPI = createTxSpi( clock ); + BoltStateMachineSPI boltSPI = new BoltStateMachineV1SPI( boltChannel, usageData, logging, authentication, connectionTracker, transactionSPI ); + return new BoltStateMachineV3( boltSPI, boltChannel, clock ); + } + private TransactionStateMachineSPI createTxSpi( Clock clock ) { long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java index a629e6a2a1c76..7a1ac70047f57 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java @@ -27,6 +27,8 @@ import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.monitoring.Monitors; +import static java.util.Objects.requireNonNull; + public class DefaultBoltConnectionFactory implements BoltConnectionFactory { private final BoltSchedulerProvider schedulerProvider; @@ -52,6 +54,9 @@ public DefaultBoltConnectionFactory( BoltSchedulerProvider schedulerProvider, Tr @Override public BoltConnection newConnection( BoltChannel channel, BoltStateMachine stateMachine ) { + requireNonNull( channel ); + requireNonNull( stateMachine ); + BoltScheduler scheduler = schedulerProvider.get( channel ); BoltConnectionQueueMonitor connectionQueueMonitor = queueMonitor == null ? scheduler : new BoltConnectionQueueMonitorAggregate( scheduler, queueMonitor ); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java index ed3dfee4bbe62..056b710dd37db 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java @@ -34,4 +34,6 @@ public interface StateMachineContext MutableConnectionState connectionState(); Clock clock(); + + String connectionId(); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java index f71ccf7367fed..6e37d34317092 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java @@ -19,12 +19,15 @@ */ package org.neo4j.bolt.transport; +import java.io.IOException; + import org.neo4j.bolt.BoltChannel; import org.neo4j.bolt.BoltProtocol; import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; import org.neo4j.bolt.v1.BoltProtocolV1; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltProtocolV3; import org.neo4j.kernel.impl.logging.LogService; public class DefaultBoltProtocolFactory implements BoltProtocolFactory @@ -52,6 +55,10 @@ else if ( protocolVersion == BoltProtocolV2.VERSION ) { return new BoltProtocolV2( channel, connectionFactory, stateMachineFactory, logService ); } + else if ( protocolVersion == BoltProtocolV3.VERSION ) + { + return new BoltProtocolV3( channel, connectionFactory, stateMachineFactory, logService ); + } else { return null; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java index e29b2865000d5..cdcda7fbae277 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java @@ -28,6 +28,7 @@ import org.neo4j.bolt.runtime.BoltConnection; import org.neo4j.bolt.runtime.BoltConnectionFactory; import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; import org.neo4j.bolt.transport.pipeline.ChunkDecoder; import org.neo4j.bolt.transport.pipeline.HouseKeeper; import org.neo4j.bolt.transport.pipeline.MessageAccumulator; @@ -35,7 +36,6 @@ import org.neo4j.bolt.v1.messaging.BoltRequestMessageReaderV1; import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriterV1; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; import org.neo4j.kernel.impl.logging.LogService; /** @@ -61,7 +61,7 @@ public BoltProtocolV1( BoltChannel channel, BoltConnectionFactory connectionFact this.connection = connectionFactory.newConnection( channel, stateMachine ); this.neo4jPack = createPack(); - this.messageReader = createBoltMessageReaderV1( channel, neo4jPack, connection, logging ); + this.messageReader = createMessageReader( channel, neo4jPack, connection, logging ); } /** @@ -89,7 +89,7 @@ public long version() return VERSION; } - public static BoltRequestMessageReader createBoltMessageReaderV1( BoltChannel channel, Neo4jPack neo4jPack, BoltConnection connection, LogService logging ) + protected BoltRequestMessageReader createMessageReader( BoltChannel channel, Neo4jPack neo4jPack, BoltConnection connection, LogService logging ) { BoltResponseMessageWriterV1 responseWriter = new BoltResponseMessageWriterV1( neo4jPack, connection.output(), logging, channel.log() ); return new BoltRequestMessageReaderV1( connection, responseWriter, channel.log(), logging ); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java index b5dd2a6c9cb7b..6bc1476d92bfb 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java @@ -77,4 +77,10 @@ public Clock clock() { return clock; } + + @Override + public String connectionId() + { + return machine.id(); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java index 41ef50265d35e..29fc6ca157e47 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java @@ -40,7 +40,7 @@ import static org.neo4j.bolt.v1.messaging.response.IgnoredMessage.IGNORED_MESSAGE; -class MessageProcessingHandler implements BoltResponseHandler +public class MessageProcessingHandler implements BoltResponseHandler { // Errors that are expected when the client disconnects mid-operation private static final Set CLIENT_MID_OP_DISCONNECT_ERRORS = @@ -54,7 +54,7 @@ class MessageProcessingHandler implements BoltResponseHandler private Neo4jError error; private boolean ignored; - MessageProcessingHandler( BoltResponseMessageWriter messageWriter, BoltConnection connection, Log logger ) + public MessageProcessingHandler( BoltResponseMessageWriter messageWriter, BoltConnection connection, Log logger ) { this.messageWriter = messageWriter; this.connection = connection; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java index 5c4a7bed48336..fbb0ac3e8d826 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java @@ -27,9 +27,9 @@ import org.neo4j.logging.Log; import org.neo4j.values.AnyValue; -class ResultHandler extends MessageProcessingHandler +public class ResultHandler extends MessageProcessingHandler { - ResultHandler( BoltResponseMessageWriter handler, BoltConnection connection, Log log ) + public ResultHandler( BoltResponseMessageWriter handler, BoltConnection connection, Log log ) { super( handler, connection, log ); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java index 9d56a9ee78d7e..ff63247ed4b86 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java @@ -59,7 +59,7 @@ public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException { String userAgent = unpacker.unpackString(); Map authToken = readAuthToken( unpacker ); - messageLogger.logInit( userAgent ); + messageLogger.logUserAgent( userAgent ); return new InitMessage( userAgent, authToken ); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java index 74ccd212767c7..64c5895af3a5a 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java @@ -37,9 +37,9 @@ * {@link AnyValueWriter Writer} that allows to convert {@link AnyValue} to any primitive Java type. It explicitly * prohibits conversion of nodes, relationships, spatial and temporal types. They are not expected in auth token map. */ -class PrimitiveOnlyValueWriter extends BaseToObjectValueWriter +public class PrimitiveOnlyValueWriter extends BaseToObjectValueWriter { - Object valueAsObject( AnyValue value ) + public Object valueAsObject( AnyValue value ) { value.writeTo( this ); return value(); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java index 4f2a26afc67af..e15ee43e2b697 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java @@ -233,6 +233,12 @@ public void close() } } + @Override + public String id() + { + return id; + } + @Override public String owner() { @@ -351,7 +357,7 @@ private void resetStatementProcessor() } } - private static States buildStates() + protected States buildStates() { ConnectedState connected = new ConnectedState(); ReadyState ready = new ReadyState(); @@ -379,12 +385,12 @@ private static States buildStates() return new States( connected, failed ); } - private static class States + public static class States { final BoltStateMachineState initial; final BoltStateMachineState failed; - States( BoltStateMachineState initial, BoltStateMachineState failed ) + public States( BoltStateMachineState initial, BoltStateMachineState failed ) { this.initial = initial; this.failed = failed; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java index 927f56a95a04c..fef706b350190 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java @@ -35,7 +35,7 @@ import org.neo4j.udc.UsageData; import org.neo4j.udc.UsageDataKeys; -class BoltStateMachineV1SPI implements BoltStateMachineSPI +public class BoltStateMachineV1SPI implements BoltStateMachineSPI { private final BoltConnectionDescriptor connectionDescriptor; private final UsageData usageData; @@ -45,7 +45,7 @@ class BoltStateMachineV1SPI implements BoltStateMachineSPI private final String version; private final TransactionStateMachineSPI transactionSpi; - BoltStateMachineV1SPI( BoltConnectionDescriptor connectionDescriptor, UsageData usageData, LogService logging, Authentication authentication, + public BoltStateMachineV1SPI( BoltConnectionDescriptor connectionDescriptor, UsageData usageData, LogService logging, Authentication authentication, BoltConnectionTracker connectionTracker, TransactionStateMachineSPI transactionStateMachineSPI ) { this.connectionDescriptor = connectionDescriptor; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java index a0aa57eecc03d..c14d7882f5bdc 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java @@ -52,7 +52,7 @@ import static java.lang.String.format; import static org.neo4j.internal.kernel.api.Transaction.Type.implicit; -class TransactionStateMachineSPI implements org.neo4j.bolt.runtime.TransactionStateMachineSPI +public class TransactionStateMachineSPI implements org.neo4j.bolt.runtime.TransactionStateMachineSPI { private static final PropertyContainerLocker locker = new PropertyContainerLocker(); @@ -64,7 +64,7 @@ class TransactionStateMachineSPI implements org.neo4j.bolt.runtime.TransactionSt private final Duration txAwaitDuration; private final Clock clock; - TransactionStateMachineSPI( GraphDatabaseAPI db, AvailabilityGuard availabilityGuard, Duration txAwaitDuration, Clock clock ) + public TransactionStateMachineSPI( GraphDatabaseAPI db, AvailabilityGuard availabilityGuard, Duration txAwaitDuration, Clock clock ) { this.db = db; this.txBridge = db.getDependencyResolver().resolveDependency( ThreadToStatementContextBridge.class ); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java b/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java index 47056d2b6e78b..3862723670f48 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java @@ -22,8 +22,8 @@ import org.neo4j.bolt.BoltChannel; import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; import org.neo4j.bolt.v1.BoltProtocolV1; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; import org.neo4j.bolt.v2.messaging.Neo4jPackV2; import org.neo4j.kernel.impl.logging.LogService; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java new file mode 100644 index 0000000000000..489ad3ba20d4d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriterV1; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.bolt.v3.messaging.BoltRequestMessageReaderV3; +import org.neo4j.kernel.impl.logging.LogService; + +/** + * Bolt protocol V3. It hosts all the components that are specific to BoltV3 + */ +public class BoltProtocolV3 extends BoltProtocolV1 +{ + public static final long VERSION = 3; + + public BoltProtocolV3( BoltChannel channel, BoltConnectionFactory connectionFactory, BoltStateMachineFactory stateMachineFactory, LogService logging ) + { + super( channel, connectionFactory, stateMachineFactory, logging ); + } + + protected Neo4jPack createPack() + { + return new Neo4jPackV2(); + } + + @Override + public long version() + { + return VERSION; + } + + protected BoltRequestMessageReader createMessageReader( BoltChannel channel, Neo4jPack neo4jPack, BoltConnection connection, LogService logging ) + { + BoltResponseMessageWriterV1 responseWriter = new BoltResponseMessageWriterV1( neo4jPack, connection.output(), logging, channel.log() ); + return new BoltRequestMessageReaderV3( connection, responseWriter, channel.log(), logging ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java new file mode 100644 index 0000000000000..eefb7a79c3fb3 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import java.time.Clock; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v1.runtime.ConnectedState; +import org.neo4j.bolt.v1.runtime.FailedState; +import org.neo4j.bolt.v1.runtime.InterruptedState; +import org.neo4j.bolt.v1.runtime.ReadyState; +import org.neo4j.bolt.v1.runtime.StreamingState; +import org.neo4j.bolt.v3.runtime.ExtraMetaDataConnectedState; + +public class BoltStateMachineV3 extends BoltStateMachineV1 +{ + public BoltStateMachineV3( BoltStateMachineSPI boltSPI, BoltChannel boltChannel, Clock clock ) + { + super( boltSPI, boltChannel, clock ); + } + + @Override + protected States buildStates() + { + ConnectedState connected = new ExtraMetaDataConnectedState(); + ReadyState ready = new ReadyState(); + StreamingState streaming = new StreamingState(); + FailedState failed = new FailedState(); + InterruptedState interrupted = new InterruptedState(); + + connected.setReadyState( ready ); + connected.setFailedState( failed ); + + ready.setStreamingState( streaming ); + ready.setInterruptedState( interrupted ); + ready.setFailedState( failed ); + + streaming.setReadyState( ready ); + streaming.setInterruptedState( interrupted ); + streaming.setFailedState( failed ); + + failed.setReadyState( ready ); + failed.setInterruptedState( interrupted ); + + interrupted.setReadyState( ready ); + interrupted.setFailedState( failed ); + + return new States( connected, failed ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java new file mode 100644 index 0000000000000..7456bdbd7463d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.util.Arrays; +import java.util.List; + +import org.neo4j.bolt.logging.BoltMessageLogger; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.MessageProcessingHandler; +import org.neo4j.bolt.v1.messaging.ResultHandler; +import org.neo4j.bolt.v1.messaging.decoder.AckFailureMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.DiscardAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.PullAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.ResetMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.RunMessageDecoder; +import org.neo4j.kernel.impl.logging.LogService; +import org.neo4j.logging.Log; + +public class BoltRequestMessageReaderV3 extends BoltRequestMessageReader +{ + public BoltRequestMessageReaderV3( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, + BoltMessageLogger messageLogger, LogService logService ) + { + super( connection, + newSimpleResponseHandler( connection, responseMessageWriter, logService ), + buildDecoders( connection, responseMessageWriter, messageLogger, logService ), + messageLogger ); + } + + private static List buildDecoders( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, + BoltMessageLogger messageLogger, LogService logService ) + { + BoltResponseHandler helloHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + BoltResponseHandler runHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + BoltResponseHandler resultHandler = new ResultHandler( responseMessageWriter, connection, internalLog( logService ) ); + BoltResponseHandler defaultHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + + return Arrays.asList( + new HelloMessageDecoder( helloHandler, messageLogger ), + new AckFailureMessageDecoder( defaultHandler, messageLogger ), + new ResetMessageDecoder( connection, defaultHandler, messageLogger ), + new RunMessageDecoder( runHandler, messageLogger ), + new DiscardAllMessageDecoder( resultHandler, messageLogger ), + new PullAllMessageDecoder( resultHandler, messageLogger ) + ); + } + + private static BoltResponseHandler newSimpleResponseHandler( BoltConnection connection, + BoltResponseMessageWriter responseMessageWriter, LogService logService ) + { + return new MessageProcessingHandler( responseMessageWriter, connection, internalLog( logService ) ); + } + + private static Log internalLog( LogService logService ) + { + return logService.getInternalLog( BoltRequestMessageReaderV3.class ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessage.java new file mode 100644 index 0000000000000..135aeb1944b9b --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessage.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.util.Map; +import java.util.Objects; + +import org.neo4j.bolt.v1.messaging.request.InitMessage; + +public class HelloMessage extends InitMessage +{ + public static final byte SIGNATURE = InitMessage.SIGNATURE; + private static final String USER_AGENT = "user_agent"; + private final Map meta; + + public HelloMessage( Map meta ) + { + super( (String) meta.get( USER_AGENT ), meta ); + this.meta = meta; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + HelloMessage that = (HelloMessage) o; + return Objects.equals( meta, that.meta ); + } + + public Map meta() + { + return meta; + } + + @Override + public int hashCode() + { + return Objects.hash( meta ); + } + + @Override + public String toString() + { + return "HELLO " + meta; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoder.java new file mode 100644 index 0000000000000..8b51e5a100ca6 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoder.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.bolt.logging.BoltMessageLogger; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.decoder.PrimitiveOnlyValueWriter; +import org.neo4j.values.virtual.MapValue; + +public class HelloMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + private final BoltMessageLogger messageLogger; + + public HelloMessageDecoder( BoltResponseHandler responseHandler, BoltMessageLogger messageLogger ) + { + this.responseHandler = responseHandler; + this.messageLogger = messageLogger; + } + + @Override + public int signature() + { + return HelloMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + MapValue helloMeta = unpacker.unpackMap(); + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + Map meta = new HashMap<>( helloMeta.size() ); + helloMeta.foreach( ( key, value ) -> meta.put( key, writer.valueAsObject( value ) ) ); + HelloMessage helloMessage = new HelloMessage( meta ); + + messageLogger.logUserAgent( helloMessage.userAgent() ); + return helloMessage; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedState.java new file mode 100644 index 0000000000000..2f04ee84af3e4 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedState.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.runtime.ConnectedState; +import org.neo4j.values.storable.Values; + +/** + * Following the socket connection and a small handshake exchange to + * establish protocol version, the machine begins in the CONNECTED + * state. The only valid transition from here is through a + * correctly authorised HELLO into the READY state. Any other action + * results in disconnection. + */ +public class ExtraMetaDataConnectedState extends ConnectedState +{ + private static final String ROUTING_TABLE_VALUE = "dbms.cluster.routing.getRoutingTable"; + private static final String ROUTING_TABLE_KEY = "routing_table"; + private static final String CONNECTION_ID_KEY = "connection_id"; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + BoltStateMachineState processResult = super.process( message, context ); + context.connectionState().onMetadata( ROUTING_TABLE_KEY, Values.stringValue( ROUTING_TABLE_VALUE ) ); + context.connectionState().onMetadata( CONNECTION_ID_KEY, Values.stringValue( context.connectionId() ) ); + return processResult; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java index cc3a7c6539da0..cf01f79c7194e 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java @@ -124,7 +124,7 @@ public void logAckFailure() public void logInit() { // when - boltMessageLogger.logInit( "userAgent" ); + boltMessageLogger.logUserAgent( "userAgent" ); // then verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C INIT userAgent" ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java index 6ee9b2a95f1b8..752d891477bf5 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java @@ -31,7 +31,7 @@ import org.neo4j.bolt.runtime.BoltConnectionFactory; import org.neo4j.bolt.runtime.BoltStateMachine; import org.neo4j.bolt.v1.BoltProtocolV1; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; import org.neo4j.bolt.v2.BoltProtocolV2; import org.neo4j.kernel.impl.logging.NullLogService; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java index d28dc9f11648b..a86e66f5f9ddd 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java @@ -37,7 +37,7 @@ import org.neo4j.bolt.transport.pipeline.HouseKeeper; import org.neo4j.bolt.transport.pipeline.MessageAccumulator; import org.neo4j.bolt.transport.pipeline.MessageDecoder; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; import org.neo4j.kernel.impl.logging.NullLogService; import static org.hamcrest.Matchers.instanceOf; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java index 90cdb7697e9bf..a6b322ed7ef8e 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java @@ -34,7 +34,7 @@ public class BoltRequestMessageWriter { - private final Neo4jPack.Packer packer; + protected final Neo4jPack.Packer packer; public BoltRequestMessageWriter( Neo4jPack.Packer packer ) { diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImplTest.java index bda26354eda45..341a0ca722f1e 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImplTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineFactoryImplTest.java @@ -19,6 +19,7 @@ */ package org.neo4j.bolt.v1.runtime; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -26,9 +27,11 @@ import org.neo4j.bolt.BoltChannel; import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; import org.neo4j.bolt.security.auth.Authentication; import org.neo4j.bolt.v1.BoltProtocolV1; import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltStateMachineV3; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.kernel.AvailabilityGuard; import org.neo4j.kernel.GraphDatabaseQueryService; @@ -40,24 +43,50 @@ import org.neo4j.test.OnDemandJobScheduler; import org.neo4j.udc.UsageData; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class BoltStateMachineFactoryImplTest +class BoltStateMachineFactoryImplTest { private static final Clock CLOCK = Clock.systemUTC(); private static final BoltChannel CHANNEL = mock( BoltChannel.class ); @ParameterizedTest( name = "V{0}" ) @ValueSource( longs = {BoltProtocolV1.VERSION, BoltProtocolV2.VERSION} ) - public void shouldCreateBoltStateMachines( long protocolVersion ) + void shouldCreateBoltStateMachines( long protocolVersion ) { BoltStateMachineFactoryImpl factory = newBoltFactory(); BoltStateMachine boltStateMachine = factory.newStateMachine( protocolVersion, CHANNEL ); assertNotNull( boltStateMachine ); + assertThat( boltStateMachine, instanceOf( BoltStateMachineV1.class ) ); + } + + @Test + void shouldCreateBoltStateMachinesV3() + { + BoltStateMachineFactoryImpl factory = newBoltFactory(); + + BoltStateMachine boltStateMachine = factory.newStateMachine( 3L, CHANNEL ); + + assertNotNull( boltStateMachine ); + assertThat( boltStateMachine, instanceOf( BoltStateMachineV3.class ) ); + } + + @ParameterizedTest( name = "V{0}" ) + @ValueSource( longs = {999, -1} ) + void shouldReturnNullIfVersionIsUnknown( long protocolVersion ) + { + BoltStateMachineFactoryImpl factory = newBoltFactory(); + + BoltStateMachine boltStateMachine = factory.newStateMachine( protocolVersion, CHANNEL ); + + assertNull( boltStateMachine ); } private static BoltStateMachineFactoryImpl newBoltFactory() diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java new file mode 100644 index 0000000000000..27ce9da95375a --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.bolt.v3.messaging.BoltRequestMessageReaderV3; +import org.neo4j.kernel.impl.logging.NullLogService; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; + +class BoltProtocolV3Test +{ + @Test + void shouldCreatePackForBoltV3() + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.createPack(), instanceOf( Neo4jPackV2.class ) ); + } + + @Test + void shouldVersionReturnBoltV3() + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.version(), equalTo( 3L ) ); + } + + @Test + void shouldCreateMessageReaderForBoltV3() + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.createMessageReader( mock( BoltChannel.class ), mock( Neo4jPack.class ), mock( BoltConnection.class ), + NullLogService.getInstance() ), instanceOf( BoltRequestMessageReaderV3.class ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java new file mode 100644 index 0000000000000..bf7cbf6dcb8cb --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.io.IOException; + +import org.neo4j.bolt.logging.NullBoltMessageLogger; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; +import org.neo4j.bolt.v1.messaging.RecordingByteChannel; +import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.kernel.impl.logging.NullLogService; + +import static org.mockito.Mockito.mock; + +/** + * A helper factory to generate boltV3 component in tests + */ +public class BoltProtocolV3ComponentFactory +{ + public static Neo4jPack neo4jPack() + { + return new Neo4jPackV2(); + } + + public static BoltRequestMessageWriter requestMessageWriter( Neo4jPack.Packer packer ) + { + return new BoltRequestMessageWriterV3( packer ); + } + + public static BoltRequestMessageReader requestMessageReader( BoltStateMachine stateMachine ) + { + return new BoltRequestMessageReaderV3( new SynchronousBoltConnection( stateMachine ), + mock( BoltResponseMessageWriter.class ), NullBoltMessageLogger.getInstance(), NullLogService.getInstance() ); + } + + public static byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException + { + RecordingByteChannel rawData = new RecordingByteChannel(); + Neo4jPack.Packer packer = neo4jPack.newPacker( new BufferedChannelOutput( rawData ) ); + BoltRequestMessageWriter writer = requestMessageWriter( packer ); + + for ( RequestMessage message : messages ) + { + writer.write( message ); + } + writer.flush(); + + return rawData.getBytes(); + } + +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java new file mode 100644 index 0000000000000..aebb2a94315cf --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.packstream.PackedInputArray; + +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.encode; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.neo4jPack; +import static org.neo4j.helpers.collection.MapUtil.map; + +class BoltRequestMessageReaderV3Test +{ + @Test + void shouldDecodeHelloMessage() throws Exception + { + testMessageDecoding( new HelloMessage( map( "user_agent", "My driver", "one", 1L, "two", 2L ) ) ); + } + + @Test + void shouldNotDecodeInitMessage() throws Exception + { + BoltIOException exception = + assertThrows( BoltIOException.class, () -> testMessageDecoding( new InitMessage( "My driver", map( "one", 1L, "two", 2L ) ) ) ); + assertThat( exception.getMessage(), startsWith( "Unable to read message type." ) ); + } + + private static void testMessageDecoding( RequestMessage message ) throws Exception + { + Neo4jPack neo4jPack = neo4jPack(); + + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + BoltRequestMessageReader reader = BoltProtocolV3ComponentFactory.requestMessageReader( stateMachine ); + + PackedInputArray innput = new PackedInputArray( encode( neo4jPack, message ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + reader.read( unpacker ); + + verify( stateMachine ).process( eq( message ), any() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java new file mode 100644 index 0000000000000..c2ff2e3607e62 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; +import org.neo4j.kernel.impl.util.ValueUtils; + +/** + * This writer simulates the client. + */ +public class BoltRequestMessageWriterV3 extends BoltRequestMessageWriter +{ + public BoltRequestMessageWriterV3( Neo4jPack.Packer packer ) + { + super( packer ); + } + + @Override + public BoltRequestMessageWriter write( RequestMessage message ) throws IOException + { + if ( message instanceof HelloMessage ) + { + writeHello( (HelloMessage) message ); + } + return super.write( message ); + } + + private void writeHello( HelloMessage message ) + { + try + { + packer.packStructHeader( 0, HelloMessage.SIGNATURE ); + packer.pack( ValueUtils.asMapValue( message.meta() ) ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoderTest.java new file mode 100644 index 0000000000000..c9ef4ec41d0c5 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/HelloMessageDecoderTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.logging.BoltMessageLogger; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.packstream.PackedInputArray; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.encode; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.neo4jPack; +import static org.neo4j.helpers.collection.MapUtil.map; + +class HelloMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final BoltMessageLogger messageLogger = mock( BoltMessageLogger.class ); + private final RequestMessageDecoder decoder = new HelloMessageDecoder( responseHandler, messageLogger ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( HelloMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + Neo4jPack neo4jPack = neo4jPack(); + HelloMessage originalMessage = new HelloMessage( map( "user_agent", "My Driver", "user", "neo4j", "password", "secret" ) ); + + PackedInputArray innput = new PackedInputArray( encode( neo4jPack, originalMessage ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + assertEquals( originalMessage, deserializedMessage ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedStateTest.java new file mode 100644 index 0000000000000..2ed3982a6d1b9 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ExtraMetaDataConnectedStateTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.bolt.v1.runtime.ConnectedState; +import org.neo4j.bolt.v3.messaging.HelloMessage; +import org.neo4j.values.storable.StringValue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.api.security.AuthToken.CREDENTIALS; +import static org.neo4j.kernel.api.security.AuthToken.PRINCIPAL; +import static org.neo4j.values.storable.Values.stringValue; + +class ExtraMetaDataConnectedStateTest +{ + @Test + void shouldAddServerVersionMetadataOnHelloMessage() throws Exception + { + // Given + // hello message + Map meta = map( "user_agent", "3.0", PRINCIPAL, "neo4j", CREDENTIALS, "password" ); + HelloMessage helloMessage = new HelloMessage( meta ); + + // setup state machine + ConnectedState state = new ExtraMetaDataConnectedState(); + BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + BoltStateMachineState failedState = mock( BoltStateMachineState.class ); + + StateMachineContext context = mock( StateMachineContext.class ); + BoltStateMachineSPI boltSpi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); + MutableConnectionState connectionState = new MutableConnectionState(); + + state.setReadyState( readyState ); + state.setFailedState( failedState ); + + when( context.boltSpi() ).thenReturn( boltSpi ); + when( context.connectionState() ).thenReturn( connectionState ); + + when( boltSpi.version() ).thenReturn( "42.42.42" ); + MutableConnectionState connectionStateMock = mock( MutableConnectionState.class ); + when( context.connectionState() ).thenReturn( connectionStateMock ); + when( context.connectionId() ).thenReturn( "connection-uuid" ); + + AuthenticationResult authResult = mock( AuthenticationResult.class ); + when( authResult.credentialsExpired() ).thenReturn( true ); + when( boltSpi.authenticate( meta ) ).thenReturn( authResult ); + + // When + BoltStateMachineState newState = state.process( helloMessage, context ); + + // Then + assertEquals( readyState, newState ); + verify( connectionStateMock ).onMetadata( "server", stringValue( "42.42.42" ) ); + verify( connectionStateMock ).onMetadata( "routing_table", stringValue( "dbms.cluster.routing.getRoutingTable" ) ); + verify( connectionStateMock ).onMetadata( eq( "connection_id" ), any( StringValue.class ) ); + } +} diff --git a/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java index 1df546d57826e..46e4abd86332f 100644 --- a/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java @@ -36,7 +36,7 @@ import org.neo4j.bolt.runtime.BoltStateMachine; import org.neo4j.bolt.security.auth.Authentication; import org.neo4j.bolt.security.auth.BasicAuthentication; -import org.neo4j.bolt.v1.runtime.BoltStateMachineFactoryImpl; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; import org.neo4j.bolt.v2.BoltProtocolV2; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.config.Setting;