diff --git a/driver/clirr-ignored-differences.xml b/driver/clirr-ignored-differences.xml
index b9b9547a70..c0944f7808 100644
--- a/driver/clirr-ignored-differences.xml
+++ b/driver/clirr-ignored-differences.xml
@@ -1,3 +1,22 @@
-
+
+
+
+ org/neo4j/driver/summary/ServerInfo
+ 7007
+ java.lang.String version()
+
+
+
+ org/neo4j/driver/summary/ServerInfo
+ 7012
+ java.lang.String protocolVersion()
+
+
+
+ org/neo4j/driver/summary/ServerInfo
+ 7012
+ java.lang.String agent()
+
+
diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java
index dda50e6bda..6bbbd3fd33 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java
@@ -54,6 +54,7 @@ public class NetworkConnection implements Connection
{
private final Channel channel;
private final InboundMessageDispatcher messageDispatcher;
+ private final String serverAgent;
private final BoltServerAddress serverAddress;
private final ServerVersion serverVersion;
private final BoltProtocol protocol;
@@ -69,6 +70,7 @@ public NetworkConnection( Channel channel, ExtendedChannelPool channelPool, Cloc
{
this.channel = channel;
this.messageDispatcher = ChannelAttributes.messageDispatcher( channel );
+ this.serverAgent = ChannelAttributes.serverAgent( channel );
this.serverAddress = ChannelAttributes.serverAddress( channel );
this.serverVersion = ChannelAttributes.serverVersion( channel );
this.protocol = BoltProtocol.forChannel( channel );
@@ -185,6 +187,12 @@ public void terminateAndRelease( String reason )
}
}
+ @Override
+ public String serverAgent()
+ {
+ return serverAgent;
+ }
+
@Override
public BoltServerAddress serverAddress()
{
diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java
index 8bc781c437..35fddcfc94 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java
@@ -33,6 +33,7 @@ public final class ChannelAttributes
private static final AttributeKey CONNECTION_ID = newInstance( "connectionId" );
private static final AttributeKey POOL_ID = newInstance( "poolId" );
private static final AttributeKey PROTOCOL_VERSION = newInstance( "protocolVersion" );
+ private static final AttributeKey SERVER_AGENT = newInstance( "serverAgent" );
private static final AttributeKey ADDRESS = newInstance( "serverAddress" );
private static final AttributeKey SERVER_VERSION = newInstance( "serverVersion" );
private static final AttributeKey CREATION_TIMESTAMP = newInstance( "creationTimestamp" );
@@ -74,6 +75,16 @@ public static void setProtocolVersion( Channel channel, BoltProtocolVersion vers
setOnce( channel, PROTOCOL_VERSION, version );
}
+ public static void setServerAgent( Channel channel, String serverAgent )
+ {
+ setOnce( channel, SERVER_AGENT, serverAgent );
+ }
+
+ public static String serverAgent( Channel channel )
+ {
+ return get( channel, SERVER_AGENT );
+ }
+
public static BoltServerAddress serverAddress( Channel channel )
{
return get( channel, ADDRESS );
diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java
index e871a9ac12..1d74213de7 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java
@@ -20,6 +20,7 @@
import java.util.concurrent.CompletionStage;
+import org.neo4j.driver.AccessMode;
import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.DatabaseName;
import org.neo4j.driver.internal.DirectConnectionProvider;
@@ -28,7 +29,6 @@
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.ResponseHandler;
import org.neo4j.driver.internal.util.ServerVersion;
-import org.neo4j.driver.AccessMode;
/**
* This is a connection used by {@link DirectConnectionProvider} to connect to a remote database.
@@ -111,6 +111,12 @@ public void terminateAndRelease( String reason )
delegate.terminateAndRelease( reason );
}
+ @Override
+ public String serverAgent()
+ {
+ return delegate.serverAgent();
+ }
+
@Override
public BoltServerAddress serverAddress()
{
diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java
index cd2e99c7f3..3799224611 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java
@@ -20,6 +20,7 @@
import java.util.concurrent.CompletionStage;
+import org.neo4j.driver.AccessMode;
import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.DatabaseName;
import org.neo4j.driver.internal.RoutingErrorHandler;
@@ -29,7 +30,6 @@
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.ResponseHandler;
import org.neo4j.driver.internal.util.ServerVersion;
-import org.neo4j.driver.AccessMode;
/**
* A connection used by the routing driver.
@@ -109,6 +109,12 @@ public void terminateAndRelease( String reason )
delegate.terminateAndRelease( reason );
}
+ @Override
+ public String serverAgent()
+ {
+ return delegate.serverAgent();
+ }
+
@Override
public BoltServerAddress serverAddress()
{
diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java
index e106cf9fcf..95c97849f7 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java
@@ -23,14 +23,16 @@
import java.util.Map;
+import org.neo4j.driver.Value;
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
import org.neo4j.driver.internal.spi.ResponseHandler;
-import org.neo4j.driver.Value;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId;
+import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAgent;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion;
import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion;
+import static org.neo4j.driver.internal.util.MetadataExtractor.extractServer;
import static org.neo4j.driver.internal.util.ServerVersion.fromBoltProtocolVersion;
public class HelloResponseHandler implements ResponseHandler
@@ -53,6 +55,9 @@ public void onSuccess( Map metadata )
{
try
{
+ Value serverValue = extractServer( metadata );
+ setServerAgent( channel, serverValue.asString() );
+
// From Server V4 extracting server from metadata in the success message is unreliable
// so we fix the Server version against the Bolt Protocol version for Server V4 and above.
if ( BoltProtocolV3.VERSION.equals( protocolVersion ) )
diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java
index 26a3c31292..13c2946937 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java
@@ -51,6 +51,8 @@ public interface Connection
void terminateAndRelease( String reason );
+ String serverAgent();
+
BoltServerAddress serverAddress();
ServerVersion serverVersion();
diff --git a/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java b/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java
index 5f6bad23e5..71ab8983b0 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java
@@ -21,18 +21,29 @@
import java.util.Objects;
import org.neo4j.driver.internal.BoltServerAddress;
+import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.util.ServerVersion;
import org.neo4j.driver.summary.ServerInfo;
public class InternalServerInfo implements ServerInfo
{
+ private final String agent;
private final String address;
private final String version;
+ private final String protocolVersion;
- public InternalServerInfo( BoltServerAddress address, ServerVersion version )
+ public InternalServerInfo( String agent, BoltServerAddress address, ServerVersion version, BoltProtocolVersion protocolVersion )
{
+ this.agent = agent;
this.address = address.toString();
this.version = version.toString();
+ this.protocolVersion = protocolVersion.toString();
+ }
+
+ @Override
+ public String agent()
+ {
+ return agent;
}
@Override
@@ -47,6 +58,12 @@ public String version()
return version;
}
+ @Override
+ public String protocolVersion()
+ {
+ return protocolVersion;
+ }
+
@Override
public boolean equals( Object o )
{
diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java b/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java
index 91921f322b..c4968ffc4a 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java
@@ -100,7 +100,8 @@ public long extractResultAvailableAfter( Map metadata )
public ResultSummary extractSummary(Query query, Connection connection, long resultAvailableAfter, Map metadata )
{
- ServerInfo serverInfo = new InternalServerInfo( connection.serverAddress(), connection.serverVersion() );
+ ServerInfo serverInfo =
+ new InternalServerInfo( connection.serverAgent(), connection.serverAddress(), connection.serverVersion(), connection.protocol().version() );
DatabaseInfo dbInfo = extractDatabaseInfo( metadata );
return new InternalResultSummary(query, serverInfo, dbInfo, extractQueryType( metadata ), extractCounters( metadata ), extractPlan( metadata ),
extractProfiledPlan( metadata ), extractNotifications( metadata ), resultAvailableAfter,
@@ -132,26 +133,29 @@ public static Bookmark extractBookmarks( Map metadata )
public static ServerVersion extractNeo4jServerVersion( Map metadata )
{
- Value versionValue = metadata.get( "server" );
- if ( versionValue == null || versionValue.isNull() )
+ Value serverValue = extractServer( metadata );
+ ServerVersion server = ServerVersion.version( serverValue.asString() );
+ if ( ServerVersion.NEO4J_PRODUCT.equalsIgnoreCase( server.product() ) )
{
- throw new UntrustedServerException( "Server provides no product identifier" );
+ return server;
}
else
{
- ServerVersion server = ServerVersion.version( versionValue.asString() );
- if ( ServerVersion.NEO4J_PRODUCT.equalsIgnoreCase( server.product() ) )
- {
- return server;
- }
- else
- {
- throw new UntrustedServerException( "Server does not identify as a genuine Neo4j instance: '" + server.product() + "'" );
- }
+ throw new UntrustedServerException( "Server does not identify as a genuine Neo4j instance: '" + server.product() + "'" );
+ }
+ }
+
+ public static Value extractServer( Map metadata )
+ {
+ Value versionValue = metadata.get( "server" );
+ if ( versionValue == null || versionValue.isNull() )
+ {
+ throw new UntrustedServerException( "Server provides no product identifier" );
}
+ return versionValue;
}
- private static QueryType extractQueryType(Map metadata )
+ private static QueryType extractQueryType( Map metadata )
{
Value typeValue = metadata.get( "type" );
if ( typeValue != null )
diff --git a/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java b/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java
index fa7ada44a3..5178ebc985 100644
--- a/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java
+++ b/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java
@@ -31,9 +31,27 @@ public interface ServerInfo
String address();
/**
- * Returns a string telling which version of the server the query was executed.
- * Supported since neo4j 3.1.
+ * Returns a string telling which version of the server the query was executed. Supported since neo4j 3.1.
+ *
* @return The server version.
+ * @deprecated in 4.3, please use {@link ServerInfo#agent()}, {@link ServerInfo#protocolVersion()}, or call the dbms.components procedure instead.
+ * Method might be removed in the next major release.
*/
+ @Deprecated
String version();
+
+ /**
+ * Returns Bolt protocol version with which the remote server communicates. This is returned as a string in format X.Y where X is the major version and Y is
+ * the minor version.
+ *
+ * @return The Bolt protocol version.
+ */
+ String protocolVersion();
+
+ /**
+ * Returns server agent string by which the remote server identifies itself.
+ *
+ * @return The agent string.
+ */
+ String agent();
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java
index 75e1793cd5..9fcfec64cf 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java
@@ -39,6 +39,7 @@
import org.neo4j.driver.internal.handlers.PullResponseCompletionListener;
import org.neo4j.driver.internal.handlers.RunResponseHandler;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.value.NullValue;
import org.neo4j.driver.util.Pair;
@@ -360,6 +361,8 @@ private Result createResult(int numberOfRecords )
Connection connection = mock( Connection.class );
when( connection.serverAddress() ).thenReturn( LOCAL_DEFAULT );
when( connection.serverVersion() ).thenReturn( anyServerVersion() );
+ when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
PullAllResponseHandler pullAllHandler =
new LegacyPullAllResponseHandler( query, runHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR,
mock( PullResponseCompletionListener.class ) );
diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java
index b7b101f88b..548ac4d4a5 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java
@@ -37,6 +37,7 @@
import org.neo4j.driver.internal.handlers.PullAllResponseHandler;
import org.neo4j.driver.internal.handlers.RunResponseHandler;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.summary.InternalResultSummary;
import org.neo4j.driver.internal.summary.InternalServerInfo;
import org.neo4j.driver.internal.summary.InternalSummaryCounters;
@@ -86,10 +87,13 @@ void shouldReturnSummary()
{
PullAllResponseHandler pullAllHandler = mock( PullAllResponseHandler.class );
- ResultSummary summary = new InternalResultSummary( new Query( "RETURN 42" ),
- new InternalServerInfo( BoltServerAddress.LOCAL_DEFAULT, anyServerVersion() ), DEFAULT_DATABASE_INFO, QueryType.SCHEMA_WRITE,
+ ResultSummary summary = new InternalResultSummary(
+ new Query( "RETURN 42" ),
+ new InternalServerInfo( "Neo4j/4.2.5", BoltServerAddress.LOCAL_DEFAULT, anyServerVersion(), BoltProtocolV43.VERSION ),
+ DEFAULT_DATABASE_INFO, QueryType.SCHEMA_WRITE,
new InternalSummaryCounters( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 ),
- null, null, emptyList(), 42, 42 );
+ null, null, emptyList(), 42, 42
+ );
when( pullAllHandler.consumeAsync() ).thenReturn( completedFuture( summary ) );
AsyncResultCursorImpl cursor = newCursor( pullAllHandler );
diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java
index 8fc4b63d25..d118fb6dcf 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java
@@ -348,6 +348,31 @@ void shouldNotWriteAndFlushMultipleMessagesWhenTerminated()
assertConnectionTerminatedError( failureCaptor.getValue() );
}
+ @Test
+ void shouldReturnServerAgentWhenCreated()
+ {
+ EmbeddedChannel channel = newChannel();
+ String agent = "Neo4j/4.2.5";
+ ChannelAttributes.setServerAgent( channel, agent );
+
+ NetworkConnection connection = newConnection( channel );
+
+ assertEquals( agent, connection.serverAgent() );
+ }
+
+ @Test
+ void shouldReturnServerAgentWhenReleased()
+ {
+ EmbeddedChannel channel = newChannel();
+ String agent = "Neo4j/4.2.5";
+ ChannelAttributes.setServerAgent( channel, agent );
+
+ NetworkConnection connection = newConnection( channel );
+ connection.release();
+
+ assertEquals( agent, connection.serverAgent() );
+ }
+
@Test
void shouldReturnServerAddressWhenReleased()
{
diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java
index f20e85e8b7..d63ad4d8cc 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java
@@ -36,6 +36,7 @@
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.protocolVersion;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress;
+import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAgent;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setCreationTimestamp;
@@ -43,6 +44,7 @@
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAddress;
+import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAgent;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setTerminationReason;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.terminationReason;
@@ -82,6 +84,23 @@ void shouldFailToSetProtocolVersionTwice()
assertThrows( IllegalStateException.class, () -> setProtocolVersion( channel, new BoltProtocolVersion( 43, 0 ) ) );
}
+ @Test
+ void shouldSetAndGetServerAgent()
+ {
+ String agent = "Neo4j/4.2.5";
+ setServerAgent( channel, agent );
+ assertEquals( agent, serverAgent( channel ) );
+ }
+
+ @Test
+ void shouldFailToSetServerAgentTwice()
+ {
+ String agent = "Neo4j/4.2.5";
+ setServerAgent( channel, agent );
+
+ assertThrows( IllegalStateException.class, () -> setServerAgent( channel, agent ) );
+ }
+
@Test
void shouldSetAndGetAddress()
{
diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java
new file mode 100644
index 0000000000..3e843dad75
--- /dev/null
+++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.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.async.connection;
+
+import org.junit.jupiter.api.Test;
+
+import org.neo4j.driver.internal.spi.Connection;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.then;
+import static org.mockito.Mockito.mock;
+import static org.neo4j.driver.AccessMode.READ;
+import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase;
+
+public class DirectConnectionTest
+{
+ @Test
+ void shouldReturnServerAgent()
+ {
+ // given
+ Connection connection = mock( Connection.class );
+ DirectConnection directConnection = new DirectConnection( connection, defaultDatabase(), READ );
+ String agent = "Neo4j/4.2.5";
+ given( connection.serverAgent() ).willReturn( agent );
+
+ // when
+ String actualAgent = directConnection.serverAgent();
+
+ // then
+ assertEquals( agent, actualAgent );
+ then( connection ).should().serverAgent();
+ }
+}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java
index 2d0727ada8..aac0b28b32 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java
@@ -28,6 +28,9 @@
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.junit.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -62,6 +65,24 @@ void shouldWrapHandlersWhenWritingAndFlushingMultipleMessages()
testHandlersWrappingWithMultipleMessages( true );
}
+ @Test
+ void shouldReturnServerAgent()
+ {
+ // given
+ Connection connection = mock( Connection.class );
+ RoutingErrorHandler errorHandler = mock( RoutingErrorHandler.class );
+ RoutingConnection routingConnection = new RoutingConnection( connection, defaultDatabase(), READ, errorHandler );
+ String agent = "Neo4j/4.2.5";
+ given( connection.serverAgent() ).willReturn( agent );
+
+ // when
+ String actualAgent = routingConnection.serverAgent();
+
+ // then
+ assertEquals( agent, actualAgent );
+ then( connection ).should().serverAgent();
+ }
+
private static void testHandlersWrappingWithSingleMessage( boolean flush )
{
Connection connection = mock( Connection.class );
diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java
index bbc749449c..2fd3600b6e 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java
@@ -48,6 +48,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.neo4j.driver.Values.value;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.connectionId;
+import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAgent;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion;
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher;
import static org.neo4j.driver.internal.async.outbound.OutboundMessageHandler.NAME;
@@ -86,6 +87,20 @@ void shouldSetServerVersionOnChannel()
assertEquals( anyServerVersion(), serverVersion( channel ) );
}
+ @Test
+ void shouldSetServerAgentOnChannel()
+ {
+ ChannelPromise channelPromise = channel.newPromise();
+ HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
+
+ String agent = "Neo4j/4.2.5";
+ Map metadata = metadata( agent, "bolt-1" );
+ handler.onSuccess( metadata );
+
+ assertTrue( channelPromise.isSuccess() );
+ assertEquals( agent, serverAgent( channel ) );
+ }
+
@Test
void shouldThrowWhenServerVersionNotReturned()
{
diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java
index a23f543eec..78763751d0 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java
@@ -22,9 +22,7 @@
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
@@ -36,10 +34,10 @@
import org.neo4j.driver.exceptions.SessionExpiredException;
import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.InternalRecord;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
-import org.neo4j.driver.internal.value.BooleanValue;
-import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.summary.QueryType;
+import org.neo4j.driver.summary.ResultSummary;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@@ -714,6 +712,8 @@ protected Connection connectionMock()
Connection connection = mock( Connection.class );
when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT );
when( connection.serverVersion() ).thenReturn( anyServerVersion() );
+ when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
return connection;
}
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java
index f97117418e..7f3bf6e8bc 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java
@@ -28,6 +28,7 @@
import org.neo4j.driver.internal.InternalBookmark;
import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.ResponseHandler;
@@ -94,6 +95,8 @@ private static Connection newConnectionMock()
Connection connection = mock( Connection.class );
when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT );
when( connection.serverVersion() ).thenReturn( anyServerVersion() );
+ when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
return connection;
}
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java
index 6575c0d723..8ef5b5a6c6 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java
@@ -32,6 +32,7 @@
import org.neo4j.driver.internal.async.UnmanagedTransaction;
import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler;
import org.neo4j.driver.internal.handlers.pulln.PullResponseHandler;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
import static org.mockito.Mockito.mock;
@@ -63,13 +64,19 @@ private static void testErrorHandling( Throwable error )
Connection connection = mock( Connection.class );
when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT );
when( connection.serverVersion() ).thenReturn( anyServerVersion() );
+ when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
UnmanagedTransaction tx = mock( UnmanagedTransaction.class );
TransactionPullResponseCompletionListener listener = new TransactionPullResponseCompletionListener( tx );
RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR );
PullResponseHandler handler = new BasicPullResponseHandler( new Query( "RETURN 1" ), runHandler,
- connection, METADATA_EXTRACTOR, listener );
- handler.installRecordConsumer( ( record, throwable ) -> {} );
- handler.installSummaryConsumer( ( resultSummary, throwable ) -> {} );
+ connection, METADATA_EXTRACTOR, listener );
+ handler.installRecordConsumer( ( record, throwable ) ->
+ {
+ } );
+ handler.installSummaryConsumer( ( resultSummary, throwable ) ->
+ {
+ } );
handler.onFailure( error );
diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java
index 09349ad2b1..eb8f68e699 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java
@@ -26,14 +26,15 @@
import java.util.function.BiConsumer;
import java.util.stream.Stream;
+import org.neo4j.driver.Record;
+import org.neo4j.driver.Value;
import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.messaging.request.DiscardMessage;
import org.neo4j.driver.internal.messaging.request.PullMessage;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.util.ServerVersion;
import org.neo4j.driver.internal.value.BooleanValue;
-import org.neo4j.driver.Record;
-import org.neo4j.driver.Value;
import org.neo4j.driver.summary.ResultSummary;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -241,6 +242,8 @@ static Connection mockConnection()
Connection conn = mock( Connection.class );
when( conn.serverAddress() ).thenReturn( mock( BoltServerAddress.class ) );
when( conn.serverVersion() ).thenReturn( mock( ServerVersion.class ) );
+ when( conn.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( conn.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
return conn;
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java
index a7441da5e8..57bb9b3c5b 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java
@@ -25,6 +25,8 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import org.neo4j.driver.AuthToken;
+import org.neo4j.driver.Config;
import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.DriverFactory;
import org.neo4j.driver.internal.cluster.RoutingContext;
@@ -35,8 +37,6 @@
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.neo4j.driver.internal.spi.ResponseHandler;
-import org.neo4j.driver.AuthToken;
-import org.neo4j.driver.Config;
public class FailingConnectionDriverFactory extends DriverFactory
{
@@ -194,6 +194,12 @@ public void terminateAndRelease( String reason )
delegate.terminateAndRelease( reason );
}
+ @Override
+ public String serverAgent()
+ {
+ return delegate.serverAgent();
+ }
+
@Override
public BoltServerAddress serverAddress()
{
diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java
index e228b36cb0..afbc7c5dd2 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java
@@ -25,14 +25,15 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.neo4j.driver.Bookmark;
import org.neo4j.driver.Query;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.neo4j.driver.exceptions.UntrustedServerException;
import org.neo4j.driver.exceptions.value.Uncoercible;
import org.neo4j.driver.internal.BoltServerAddress;
-import org.neo4j.driver.Bookmark;
import org.neo4j.driver.internal.InternalBookmark;
+import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.summary.InternalInputPosition;
import org.neo4j.driver.summary.DatabaseInfo;
@@ -60,6 +61,7 @@
import static org.neo4j.driver.internal.summary.InternalSummaryCounters.EMPTY_STATS;
import static org.neo4j.driver.internal.util.MetadataExtractor.extractDatabaseInfo;
import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion;
+import static org.neo4j.driver.internal.util.MetadataExtractor.extractServer;
import static org.neo4j.driver.internal.util.ServerVersion.v4_0_0;
import static org.neo4j.driver.summary.QueryType.READ_ONLY;
import static org.neo4j.driver.summary.QueryType.READ_WRITE;
@@ -78,7 +80,7 @@ class MetadataExtractorTest
void shouldExtractQueryKeys()
{
List keys = asList( "hello", " ", "world", "!" );
- Map keyIndex = new HashMap<>();
+ Map keyIndex = new HashMap<>();
keyIndex.put( "hello", 0 );
keyIndex.put( " ", 1 );
keyIndex.put( "world", 2 );
@@ -116,11 +118,11 @@ void shouldExtractNoResultAvailableAfterWhenNoneInMetadata()
void shouldBuildResultSummaryWithQuery()
{
Query query = new Query( "UNWIND range(10, 100) AS x CREATE (:Node {name: $name, x: x})",
- singletonMap( "name", "Apa" ) );
+ singletonMap( "name", "Apa" ) );
- ResultSummary summary = extractor.extractSummary(query, connectionMock(), 42, emptyMap() );
+ ResultSummary summary = extractor.extractSummary( query, connectionMock(), 42, emptyMap() );
- assertEquals(query, summary.query() );
+ assertEquals( query, summary.query() );
}
@Test
@@ -413,6 +415,16 @@ void shouldExtractServerVersion()
assertEquals( ServerVersion.v3_5_0, version );
}
+ @Test
+ void shouldExtractServer()
+ {
+ String agent = "Neo4j/3.5.0";
+ Map metadata = singletonMap( "server", value( agent ) );
+
+ Value serverValue = extractServer( metadata );
+
+ assertEquals( agent, serverValue.asString() );
+ }
@Test
void shouldExtractDatabase()
@@ -466,7 +478,7 @@ void shouldFailToExtractServerVersionFromNonNeo4jProduct()
assertThrows( UntrustedServerException.class, () -> extractNeo4jServerVersion( singletonMap( "server", value( "NotNeo4j/1.2.3" ) ) ) );
}
- private ResultSummary createWithQueryType(Value typeValue )
+ private ResultSummary createWithQueryType( Value typeValue )
{
Map metadata = singletonMap( "type", typeValue );
return extractor.extractSummary( query(), connectionMock(), 42, metadata );
@@ -487,6 +499,8 @@ private static Connection connectionMock( BoltServerAddress address, ServerVersi
Connection connection = mock( Connection.class );
when( connection.serverAddress() ).thenReturn( address );
when( connection.serverVersion() ).thenReturn( version );
+ when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE );
+ when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" );
return connection;
}
}
diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java
index 3a4df65624..6722ebd5ab 100644
--- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java
+++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java
@@ -23,7 +23,7 @@
import lombok.Setter;
import neo4j.org.testkit.backend.TestkitState;
import neo4j.org.testkit.backend.messages.responses.NullRecord;
-import neo4j.org.testkit.backend.messages.responses.ResultSummary;
+import neo4j.org.testkit.backend.messages.responses.Summary;
import neo4j.org.testkit.backend.messages.responses.TestkitResponse;
import org.neo4j.driver.Result;
@@ -42,8 +42,17 @@ public TestkitResponse process( TestkitState testkitState )
try
{
Result result = testkitState.getResults().get( data.getResultId() );
- result.consume();
- return ResultSummary.builder().build();
+ org.neo4j.driver.summary.ResultSummary summary = result.consume();
+ Summary.ServerInfo serverInfo = Summary.ServerInfo.builder()
+ .protocolVersion( summary.server().protocolVersion() )
+ .agent( summary.server().agent() )
+ .build();
+ Summary.SummaryBody data = Summary.SummaryBody.builder()
+ .serverInfo( serverInfo )
+ .build();
+ return Summary.builder()
+ .data( data )
+ .build();
}
catch ( NoSuchRecordException ignored )
{
diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java
similarity index 69%
rename from testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java
rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java
index 0df7a42e1a..db32033abb 100644
--- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java
+++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java
@@ -25,11 +25,31 @@
@Setter
@Getter
@Builder
-public class ResultSummary implements TestkitResponse
+public class Summary implements TestkitResponse
{
+ private SummaryBody data;
+
@Override
public String testkitName()
{
- return "ResultSummary";
+ return "Summary";
+ }
+
+ @Setter
+ @Getter
+ @Builder
+ public static class SummaryBody
+ {
+ private ServerInfo serverInfo;
+ }
+
+ @Setter
+ @Getter
+ @Builder
+ public static class ServerInfo
+ {
+ private String protocolVersion;
+
+ private String agent;
}
}