From 3393c005644dc92822ab1cec8b4a2562392ca618 Mon Sep 17 00:00:00 2001 From: Dmitriy Tverdiakov <11927660+injectives@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:17:23 +0000 Subject: [PATCH] Update internal Bolt exception structure The main goal of this update is to refactor internal Bolt exception handling and to separate it from the user facing exceptions. --- .../driver/exceptions/Neo4jException.java | 1 + .../SecurityRetryableException.java | 2 +- .../neo4j/driver/internal/DriverFactory.java | 32 ++- .../neo4j/driver/internal/InternalResult.java | 6 +- .../driver/internal/InternalSession.java | 4 +- .../driver/internal/SessionFactoryImpl.java | 10 +- .../AdaptingDriverBoltConnection.java | 219 ++++++++++++++++ .../AdaptingDriverBoltConnectionProvider.java | 107 ++++++++ .../AdaptingDriverResponseHandler.java | 117 +++++++++ .../adaptedbolt/BasicResponseHandler.java | 176 +++++++++++++ .../adaptedbolt/DriverBoltConnection.java | 103 ++++++++ .../DriverBoltConnectionProvider.java | 63 +++++ .../adaptedbolt/DriverResponseHandler.java | 91 +++++++ .../internal/adaptedbolt/ErrorMapper.java | 239 ++++++++++++++++++ .../adaptedbolt/RoutedErrorMapper.java | 71 ++++++ .../BoltConnectionWithAuthTokenManager.java | 8 +- .../BoltConnectionWithCloseTracking.java | 4 +- .../async/DelegatingBoltConnection.java | 40 +-- .../async/DelegatingResponseHandler.java | 8 +- .../async/ErrorMappingResponseHandler.java | 4 +- .../async/LeakLoggingNetworkSession.java | 4 +- .../driver/internal/async/NetworkSession.java | 22 +- .../async/TerminationAwareBoltConnection.java | 16 +- .../TerminationAwareResponseHandler.java | 4 +- .../internal/async/UnmanagedTransaction.java | 50 ++-- .../bolt/api/BasicResponseHandler.java | 4 +- .../api/exception/BoltClientException.java | 32 +++ .../BoltConnectionAcquisitionException.java | 32 +++ .../BoltConnectionReadTimeoutException.java | 32 +++ .../api/exception/BoltDiscoveryException.java | 28 ++ .../bolt/api/exception/BoltException.java | 32 +++ .../api/exception/BoltFailureException.java | 44 ++++ .../api/exception/BoltGqlErrorException.java | 73 ++++++ .../api/exception/BoltProtocolException.java | 32 +++ .../BoltServiceUnavailableException.java | 32 +++ .../api/exception/BoltTransientException.java | 32 +++ .../BoltUnsupportedFeatureException.java | 28 ++ .../BoltUntrustedServerException.java | 32 +++ .../MinVersionAcquisitionException.java | 3 +- .../bolt/basicimpl/BoltConnectionImpl.java | 35 +-- .../MessageIgnoredException.java | 6 +- .../connection/ChannelConnectedListener.java | 11 +- .../async/connection/HandshakeHandler.java | 56 ++-- .../async/inbound/ChannelErrorHandler.java | 15 +- .../async/inbound/ConnectTimeoutHandler.java | 6 +- .../inbound/ConnectionReadTimeoutHandler.java | 6 +- .../inbound/InboundMessageDispatcher.java | 40 ++- .../handlers/LogoffResponseHandler.java | 4 +- .../handlers/LogonResponseHandler.java | 4 +- .../basicimpl/messaging/BoltProtocol.java | 18 +- .../messaging/common/CommonValueUnpacker.java | 31 +-- .../messaging/request/MultiDatabaseUtil.java | 14 +- .../messaging/v3/BoltProtocolV3.java | 12 +- .../messaging/v52/BoltProtocolV52.java | 4 +- .../messaging/v57/MessageReaderV57.java | 4 +- .../basicimpl/util/MetadataExtractor.java | 6 +- .../bolt/pooledimpl/PooledBoltConnection.java | 8 +- .../PooledBoltConnectionProvider.java | 13 +- .../AuthTokenManagerExecutionException.java | 28 ++ .../bolt/routedimpl/RoutedBoltConnection.java | 53 ++-- .../RoutedBoltConnectionProvider.java | 62 +++-- .../routedimpl/cluster/RediscoveryImpl.java | 82 ++++-- .../bolt/routedimpl/util/FutureUtil.java | 7 +- .../internal/cursor/ResultCursorImpl.java | 10 +- .../internal/cursor/RxResultCursorImpl.java | 13 +- .../retry/ExponentialBackoffRetryLogic.java | 2 +- .../internal/telemetry/ApiTelemetryWork.java | 4 +- .../neo4j/driver/internal/util/ErrorUtil.java | 168 ------------ .../internal/util/MetadataExtractor.java | 4 +- .../java/org/neo4j/driver/PackageTests.java | 9 +- .../java/org/neo4j/driver/ParametersTest.java | 4 +- .../driver/integration/ParametersIT.java | 2 +- .../driver/internal/DriverFactoryTest.java | 6 +- .../driver/internal/InternalResultTest.java | 4 +- .../internal/InternalTransactionTest.java | 18 +- .../internal/SessionFactoryImplTest.java | 4 +- .../internal/adaptedbolt/ErrorMapperTest.java | 165 ++++++++++++ .../async/InternalAsyncSessionTest.java | 26 +- .../async/InternalAsyncTransactionTest.java | 22 +- .../async/LeakLoggingNetworkSessionTest.java | 10 +- .../internal/async/NetworkSessionTest.java | 34 +-- .../async/UnmanagedTransactionTest.java | 6 +- .../ChannelConnectedListenerTest.java | 8 +- .../connection/ChannelErrorHandlerTest.java | 14 +- .../connection/HandshakeHandlerTest.java | 27 +- .../inbound/ConnectTimeoutHandlerTest.java | 6 +- .../ConnectionReadTimeoutHandlerTest.java | 4 +- .../inbound/InboundMessageDispatcherTest.java | 6 +- .../inbound/InboundMessageHandlerTest.java | 4 +- .../handlers/HelloResponseHandlerTest.java | 8 +- .../basicimpl/messaging/BoltProtocolTest.java | 10 +- .../messaging/MessageFormatTest.java | 4 +- .../messaging/v3/BoltProtocolV3Test.java | 8 +- .../basicimpl/util/MetadataExtractorTest.java | 9 +- .../routedimpl/cluster/RediscoveryTest.java | 77 +++--- .../internal/cursor/ResultCursorImplTest.java | 25 +- .../cursor/RxResultCursorImplTest.java | 4 +- .../reactive/InternalRxResultTest.java | 20 +- .../telemetry/ApiTelemetryWorkTest.java | 10 +- .../driver/internal/util/ErrorUtilTest.java | 147 ----------- .../internal/util/MetadataExtractorTest.java | 8 +- .../org/neo4j/driver/testutil/TestUtil.java | 84 +++--- 102 files changed, 2442 insertions(+), 914 deletions(-) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnection.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnectionProvider.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverResponseHandler.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/BasicResponseHandler.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnection.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnectionProvider.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverResponseHandler.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapper.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/RoutedErrorMapper.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltClientException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionAcquisitionException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionReadTimeoutException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltDiscoveryException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltFailureException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltGqlErrorException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltProtocolException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltServiceUnavailableException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltTransientException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUnsupportedFeatureException.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUntrustedServerException.java rename driver/src/main/java/org/neo4j/driver/internal/bolt/{api/exception => basicimpl}/MessageIgnoredException.java (82%) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/AuthTokenManagerExecutionException.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapperTest.java diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/Neo4jException.java b/driver/src/main/java/org/neo4j/driver/exceptions/Neo4jException.java index 84b1c7e987..7695cd355f 100644 --- a/driver/src/main/java/org/neo4j/driver/exceptions/Neo4jException.java +++ b/driver/src/main/java/org/neo4j/driver/exceptions/Neo4jException.java @@ -238,6 +238,7 @@ public Optional gqlCause() { return findFirstGqlCause(this, Neo4jException.class); } + @SuppressWarnings("DuplicatedCode") private static Optional findFirstGqlCause(Throwable throwable, Class targetCls) { var cause = throwable.getCause(); if (cause == null) { diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/SecurityRetryableException.java b/driver/src/main/java/org/neo4j/driver/exceptions/SecurityRetryableException.java index a1e8979115..c851356931 100644 --- a/driver/src/main/java/org/neo4j/driver/exceptions/SecurityRetryableException.java +++ b/driver/src/main/java/org/neo4j/driver/exceptions/SecurityRetryableException.java @@ -25,7 +25,7 @@ * Indicates that the contained {@link SecurityException} is a {@link RetryableException}, which is determined by the * {@link org.neo4j.driver.AuthTokenManager#handleSecurityException(AuthToken, SecurityException)} method. *

- * The original {@link java.lang.SecurityException} is always available as a {@link Throwable#getCause()}. The + * The original {@link SecurityException} is always available as a {@link Throwable#getCause()}. The * {@link SecurityRetryableException#code()} and {@link SecurityRetryableException#getMessage()} supply the values from * the original exception. * diff --git a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java index 9f664d7d8a..bdf96552b3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java @@ -39,6 +39,9 @@ import org.neo4j.driver.Driver; import org.neo4j.driver.Logging; import org.neo4j.driver.MetricsAdapter; +import org.neo4j.driver.internal.adaptedbolt.AdaptingDriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.ErrorMapper; import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.DefaultDomainNameResolver; @@ -158,7 +161,7 @@ private InternalDriver createDriver( AuthTokenManager authTokenManager, boolean ownsEventLoopGroup, Supplier rediscoverySupplier) { - BoltConnectionProvider boltConnectionProvider = null; + DriverBoltConnectionProvider boltConnectionProvider = null; try { boltConnectionProvider = createBoltConnectionProvider(uri, config, eventLoopGroup, routingSettings, rediscoverySupplier); @@ -204,28 +207,33 @@ private Function> createBoltServerAddr .collect(Collectors.toCollection(LinkedHashSet::new)); } - private BoltConnectionProvider createBoltConnectionProvider( + private DriverBoltConnectionProvider createBoltConnectionProvider( URI uri, Config config, EventLoopGroup eventLoopGroup, RoutingSettings routingSettings, Supplier rediscoverySupplier) { - BoltConnectionProvider boltConnectionProvider; + DriverBoltConnectionProvider boltConnectionProvider; var clock = createClock(); var loggingProvider = new BoltLoggingProvider(config.logging()); Supplier pooledBoltConnectionProviderSupplier = () -> createPooledBoltConnectionProvider(config, eventLoopGroup, clock, loggingProvider); + var errorMapper = ErrorMapper.getInstance(); if (uri.getScheme().startsWith("bolt")) { assertNoRoutingContext(uri, routingSettings); - boltConnectionProvider = pooledBoltConnectionProviderSupplier.get(); + boltConnectionProvider = new AdaptingDriverBoltConnectionProvider( + pooledBoltConnectionProviderSupplier.get(), errorMapper, false); } else { - boltConnectionProvider = createRoutedBoltConnectionProvider( - config, - pooledBoltConnectionProviderSupplier, - routingSettings, - rediscoverySupplier, - clock, - loggingProvider); + boltConnectionProvider = new AdaptingDriverBoltConnectionProvider( + createRoutedBoltConnectionProvider( + config, + pooledBoltConnectionProviderSupplier, + routingSettings, + rediscoverySupplier, + clock, + loggingProvider), + errorMapper, + true); } return boltConnectionProvider; } @@ -308,7 +316,7 @@ protected Clock createClock() { */ protected SessionFactory createSessionFactory( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, Config config, AuthTokenManager authTokenManager) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalResult.java b/driver/src/main/java/org/neo4j/driver/internal/InternalResult.java index 1951973e0b..7c556f2adf 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/InternalResult.java +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalResult.java @@ -28,16 +28,16 @@ import org.neo4j.driver.async.ResultCursor; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.NoSuchRecordException; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.summary.ResultSummary; public class InternalResult implements Result { - private final BoltConnection connection; + private final DriverBoltConnection connection; private final ResultCursor cursor; - public InternalResult(BoltConnection connection, ResultCursor cursor) { + public InternalResult(DriverBoltConnection connection, ResultCursor cursor) { this.connection = connection; this.cursor = cursor; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java b/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java index 77a255d752..ec2423c26d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java @@ -30,8 +30,8 @@ import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.TransactionWork; import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.telemetry.ApiTelemetryWork; @@ -208,7 +208,7 @@ private Transaction beginTransaction( private void terminateConnectionOnThreadInterrupt(String reason) { // try to get current connection if it has been acquired - BoltConnection connection = null; + DriverBoltConnection connection = null; try { connection = Futures.getNow(session.connectionAsync()); } catch (Throwable ignore) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java index da340c583b..18f8c342c7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java @@ -33,9 +33,9 @@ import org.neo4j.driver.NotificationConfig; import org.neo4j.driver.SessionConfig; import org.neo4j.driver.Value; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.bolt.api.DatabaseNameUtil; import org.neo4j.driver.internal.bolt.api.SecurityPlan; @@ -45,7 +45,7 @@ public class SessionFactoryImpl implements SessionFactory { private final BoltSecurityPlanManager securityPlanManager; - private final BoltConnectionProvider connectionProvider; + private final DriverBoltConnectionProvider connectionProvider; private final RetryLogic retryLogic; private final Logging logging; private final boolean leakedSessionsLoggingEnabled; @@ -54,7 +54,7 @@ public class SessionFactoryImpl implements SessionFactory { SessionFactoryImpl( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, Config config, AuthTokenManager authTokenManager) { @@ -163,7 +163,7 @@ public CompletionStage supportsSessionAuth() { private NetworkSession createSession( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, DatabaseName databaseName, AccessMode mode, @@ -214,7 +214,7 @@ private NetworkSession createSession( authTokenManager); } - public BoltConnectionProvider getConnectionProvider() { + public DriverBoltConnectionProvider getConnectionProvider() { return connectionProvider; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnection.java new file mode 100644 index 0000000000..3db3649b4b --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnection.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.AccessMode; +import org.neo4j.driver.internal.bolt.api.AuthData; +import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.bolt.api.BoltConnectionState; +import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; +import org.neo4j.driver.internal.bolt.api.BoltServerAddress; +import org.neo4j.driver.internal.bolt.api.DatabaseName; +import org.neo4j.driver.internal.bolt.api.NotificationConfig; +import org.neo4j.driver.internal.bolt.api.TelemetryApi; +import org.neo4j.driver.internal.bolt.api.TransactionType; + +final class AdaptingDriverBoltConnection implements DriverBoltConnection { + private final BoltConnection connection; + private final ErrorMapper errorMapper; + + AdaptingDriverBoltConnection(BoltConnection connection, ErrorMapper errorMapper) { + this.connection = Objects.requireNonNull(connection); + this.errorMapper = Objects.requireNonNull(errorMapper); + } + + @Override + public CompletionStage onLoop() { + return connection.onLoop().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage route( + DatabaseName databaseName, String impersonatedUser, Set bookmarks) { + return connection + .route(databaseName, impersonatedUser, bookmarks) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage beginTransaction( + DatabaseName databaseName, + AccessMode accessMode, + String impersonatedUser, + Set bookmarks, + TransactionType transactionType, + Duration txTimeout, + Map txMetadata, + String txType, + NotificationConfig notificationConfig) { + return connection + .beginTransaction( + databaseName, + accessMode, + impersonatedUser, + bookmarks, + transactionType, + txTimeout, + txMetadata, + txType, + notificationConfig) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage runInAutoCommitTransaction( + DatabaseName databaseName, + AccessMode accessMode, + String impersonatedUser, + Set bookmarks, + String query, + Map parameters, + Duration txTimeout, + Map txMetadata, + NotificationConfig notificationConfig) { + return connection + .runInAutoCommitTransaction( + databaseName, + accessMode, + impersonatedUser, + bookmarks, + query, + parameters, + txTimeout, + txMetadata, + notificationConfig) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage run(String query, Map parameters) { + return connection + .run(query, parameters) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage pull(long qid, long request) { + return connection + .pull(qid, request) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage discard(long qid, long number) { + return connection + .discard(qid, number) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage commit() { + return connection.commit().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage rollback() { + return connection.rollback().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage reset() { + return connection.reset().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage logoff() { + return connection.logoff().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage logon(Map authMap) { + return connection.logon(authMap).exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage telemetry(TelemetryApi telemetryApi) { + return connection + .telemetry(telemetryApi) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(ignored -> this); + } + + @Override + public CompletionStage clear() { + return connection.clear().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this); + } + + @Override + public CompletionStage flush(DriverResponseHandler handler) { + return connection + .flush(new AdaptingDriverResponseHandler(handler, errorMapper)) + .exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage forceClose(String reason) { + return connection.forceClose(reason).exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage close() { + return connection.close().exceptionally(errorMapper::mapAndTrow); + } + + @Override + public BoltConnectionState state() { + return connection.state(); + } + + @Override + public CompletionStage authData() { + return connection.authData().exceptionally(errorMapper::mapAndTrow); + } + + @Override + public String serverAgent() { + return connection.serverAgent(); + } + + @Override + public BoltServerAddress serverAddress() { + return connection.serverAddress(); + } + + @Override + public BoltProtocolVersion protocolVersion() { + return connection.protocolVersion(); + } + + @Override + public boolean telemetrySupported() { + return connection.telemetrySupported(); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnectionProvider.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnectionProvider.java new file mode 100644 index 0000000000..dd40cfd2bc --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverBoltConnectionProvider.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.AccessMode; +import org.neo4j.driver.internal.bolt.api.BoltAgent; +import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; +import org.neo4j.driver.internal.bolt.api.BoltServerAddress; +import org.neo4j.driver.internal.bolt.api.DatabaseName; +import org.neo4j.driver.internal.bolt.api.MetricsListener; +import org.neo4j.driver.internal.bolt.api.NotificationConfig; +import org.neo4j.driver.internal.bolt.api.RoutingContext; +import org.neo4j.driver.internal.bolt.api.SecurityPlan; + +public class AdaptingDriverBoltConnectionProvider implements DriverBoltConnectionProvider { + private final BoltConnectionProvider delegate; + private final ErrorMapper errorMapper; + private final boolean routed; + + public AdaptingDriverBoltConnectionProvider( + BoltConnectionProvider delegate, ErrorMapper errorMapper, boolean routed) { + this.delegate = Objects.requireNonNull(delegate); + this.errorMapper = Objects.requireNonNull(errorMapper); + this.routed = routed; + } + + @Override + public CompletionStage init( + BoltServerAddress address, + RoutingContext routingContext, + BoltAgent boltAgent, + String userAgent, + int connectTimeoutMillis, + MetricsListener metricsListener) { + return delegate.init(address, routingContext, boltAgent, userAgent, connectTimeoutMillis, metricsListener) + .exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage connect( + SecurityPlan securityPlan, + DatabaseName databaseName, + Supplier>> authMapStageSupplier, + AccessMode mode, + Set bookmarks, + String impersonatedUser, + BoltProtocolVersion minVersion, + NotificationConfig notificationConfig, + Consumer databaseNameConsumer) { + return delegate.connect( + securityPlan, + databaseName, + authMapStageSupplier, + mode, + bookmarks, + impersonatedUser, + minVersion, + notificationConfig, + databaseNameConsumer) + .exceptionally(errorMapper::mapAndTrow) + .thenApply(boltConnection -> new AdaptingDriverBoltConnection( + boltConnection, + routed ? new RoutedErrorMapper(boltConnection.serverAddress(), mode) : errorMapper)); + } + + @Override + public CompletionStage verifyConnectivity(SecurityPlan securityPlan, Map authMap) { + return delegate.verifyConnectivity(securityPlan, authMap).exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage supportsMultiDb(SecurityPlan securityPlan, Map authMap) { + return delegate.supportsMultiDb(securityPlan, authMap).exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage supportsSessionAuth(SecurityPlan securityPlan, Map authMap) { + return delegate.supportsSessionAuth(securityPlan, authMap).exceptionally(errorMapper::mapAndTrow); + } + + @Override + public CompletionStage close() { + return delegate.close().exceptionally(errorMapper::mapAndTrow); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverResponseHandler.java new file mode 100644 index 0000000000..86f1fa5315 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverResponseHandler.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.util.Objects; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; +import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; +import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogoffSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogonSummary; +import org.neo4j.driver.internal.bolt.api.summary.PullSummary; +import org.neo4j.driver.internal.bolt.api.summary.ResetSummary; +import org.neo4j.driver.internal.bolt.api.summary.RollbackSummary; +import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; +import org.neo4j.driver.internal.bolt.api.summary.RunSummary; +import org.neo4j.driver.internal.bolt.api.summary.TelemetrySummary; + +final class AdaptingDriverResponseHandler implements ResponseHandler { + private final DriverResponseHandler delegate; + private final ErrorMapper errorMapper; + + AdaptingDriverResponseHandler(DriverResponseHandler delegate, ErrorMapper errorMapper) { + this.delegate = Objects.requireNonNull(delegate); + this.errorMapper = Objects.requireNonNull(errorMapper); + } + + @Override + public void onError(Throwable throwable) { + delegate.onError(errorMapper.map(throwable)); + } + + @Override + public void onBeginSummary(BeginSummary summary) { + delegate.onBeginSummary(summary); + } + + @Override + public void onRunSummary(RunSummary summary) { + delegate.onRunSummary(summary); + } + + @Override + public void onRecord(Value[] fields) { + delegate.onRecord(fields); + } + + @Override + public void onPullSummary(PullSummary summary) { + delegate.onPullSummary(summary); + } + + @Override + public void onDiscardSummary(DiscardSummary summary) { + delegate.onDiscardSummary(summary); + } + + @Override + public void onCommitSummary(CommitSummary summary) { + delegate.onCommitSummary(summary); + } + + @Override + public void onRollbackSummary(RollbackSummary summary) { + delegate.onRollbackSummary(summary); + } + + @Override + public void onResetSummary(ResetSummary summary) { + delegate.onResetSummary(summary); + } + + @Override + public void onRouteSummary(RouteSummary summary) { + delegate.onRouteSummary(summary); + } + + @Override + public void onLogoffSummary(LogoffSummary summary) { + delegate.onLogoffSummary(summary); + } + + @Override + public void onLogonSummary(LogonSummary summary) { + delegate.onLogonSummary(summary); + } + + @Override + public void onTelemetrySummary(TelemetrySummary summary) { + delegate.onTelemetrySummary(summary); + } + + @Override + public void onIgnored() { + delegate.onIgnored(); + } + + @Override + public void onComplete() { + delegate.onComplete(); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/BasicResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/BasicResponseHandler.java new file mode 100644 index 0000000000..b55b0e4822 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/BasicResponseHandler.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; +import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; +import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogoffSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogonSummary; +import org.neo4j.driver.internal.bolt.api.summary.PullSummary; +import org.neo4j.driver.internal.bolt.api.summary.ResetSummary; +import org.neo4j.driver.internal.bolt.api.summary.RollbackSummary; +import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; +import org.neo4j.driver.internal.bolt.api.summary.RunSummary; +import org.neo4j.driver.internal.bolt.api.summary.TelemetrySummary; + +public class BasicResponseHandler implements DriverResponseHandler { + private final CompletableFuture summariesFuture = new CompletableFuture<>(); + private final List valuesList = new ArrayList<>(); + + private BeginSummary beginSummary; + private RunSummary runSummary; + private PullSummary pullSummary; + private DiscardSummary discardSummary; + private CommitSummary commitSummary; + private RollbackSummary rollbackSummary; + private ResetSummary resetSummary; + private RouteSummary routeSummary; + private LogoffSummary logoffSummary; + private LogonSummary logonSummary; + private TelemetrySummary telemetrySummary; + private int ignored; + private Throwable error; + + public CompletionStage summaries() { + return summariesFuture; + } + + @Override + public void onError(Throwable throwable) { + if (throwable instanceof CompletionException) { + throwable = throwable.getCause(); + } + if (error == null) { + error = throwable; + } else { + if (error instanceof Neo4jException && !(throwable instanceof Neo4jException)) { + // higher order error has occurred + error = throwable; + } + } + } + + @Override + public void onBeginSummary(BeginSummary summary) { + beginSummary = summary; + } + + @Override + public void onRunSummary(RunSummary summary) { + runSummary = summary; + } + + @Override + public void onRecord(Value[] fields) { + valuesList.add(fields); + } + + @Override + public void onPullSummary(PullSummary summary) { + pullSummary = summary; + } + + @Override + public void onDiscardSummary(DiscardSummary summary) { + discardSummary = summary; + } + + @Override + public void onCommitSummary(CommitSummary summary) { + commitSummary = summary; + } + + @Override + public void onRollbackSummary(RollbackSummary summary) { + rollbackSummary = summary; + } + + @Override + public void onResetSummary(ResetSummary summary) { + resetSummary = summary; + } + + @Override + public void onRouteSummary(RouteSummary summary) { + routeSummary = summary; + } + + @Override + public void onLogoffSummary(LogoffSummary summary) { + logoffSummary = summary; + } + + @Override + public void onLogonSummary(LogonSummary summary) { + logonSummary = summary; + } + + @Override + public void onTelemetrySummary(TelemetrySummary summary) { + telemetrySummary = summary; + } + + @Override + public void onIgnored() { + ignored++; + } + + @Override + public void onComplete() { + if (error != null) { + summariesFuture.completeExceptionally(error); + } else { + summariesFuture.complete(new Summaries( + beginSummary, + runSummary, + valuesList, + pullSummary, + discardSummary, + commitSummary, + rollbackSummary, + resetSummary, + routeSummary, + logoffSummary, + logonSummary, + telemetrySummary, + ignored)); + } + } + + public record Summaries( + BeginSummary beginSummary, + RunSummary runSummary, + List valuesList, + PullSummary pullSummary, + DiscardSummary discardSummary, + CommitSummary commitSummary, + RollbackSummary rollbackSummary, + ResetSummary resetSummary, + RouteSummary routeSummary, + LogoffSummary logoffSummary, + LogonSummary logonSummary, + TelemetrySummary telemetrySummary, + int ignored) {} +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnection.java new file mode 100644 index 0000000000..76d29a4e08 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnection.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.time.Duration; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.AccessMode; +import org.neo4j.driver.internal.bolt.api.AuthData; +import org.neo4j.driver.internal.bolt.api.BoltConnectionState; +import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; +import org.neo4j.driver.internal.bolt.api.BoltServerAddress; +import org.neo4j.driver.internal.bolt.api.DatabaseName; +import org.neo4j.driver.internal.bolt.api.NotificationConfig; +import org.neo4j.driver.internal.bolt.api.TelemetryApi; +import org.neo4j.driver.internal.bolt.api.TransactionType; + +public interface DriverBoltConnection { + CompletionStage onLoop(); + + CompletionStage route( + DatabaseName databaseName, String impersonatedUser, Set bookmarks); + + CompletionStage beginTransaction( + DatabaseName databaseName, + AccessMode accessMode, + String impersonatedUser, + Set bookmarks, + TransactionType transactionType, + Duration txTimeout, + Map txMetadata, + String txType, + NotificationConfig notificationConfig); + + CompletionStage runInAutoCommitTransaction( + DatabaseName databaseName, + AccessMode accessMode, + String impersonatedUser, + Set bookmarks, + String query, + Map parameters, + Duration txTimeout, + Map txMetadata, + NotificationConfig notificationConfig); + + CompletionStage run(String query, Map parameters); + + CompletionStage pull(long qid, long request); + + CompletionStage discard(long qid, long number); + + CompletionStage commit(); + + CompletionStage rollback(); + + CompletionStage reset(); + + CompletionStage logoff(); + + CompletionStage logon(Map authMap); + + CompletionStage telemetry(TelemetryApi telemetryApi); + + CompletionStage clear(); + + CompletionStage flush(DriverResponseHandler handler); + + CompletionStage forceClose(String reason); + + CompletionStage close(); + + // ----- MUTABLE DATA ----- + + BoltConnectionState state(); + + CompletionStage authData(); + + // ----- IMMUTABLE DATA ----- + + String serverAgent(); + + BoltServerAddress serverAddress(); + + BoltProtocolVersion protocolVersion(); + + boolean telemetrySupported(); +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnectionProvider.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnectionProvider.java new file mode 100644 index 0000000000..2a6711d8ae --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverBoltConnectionProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.AccessMode; +import org.neo4j.driver.internal.bolt.api.BoltAgent; +import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; +import org.neo4j.driver.internal.bolt.api.BoltServerAddress; +import org.neo4j.driver.internal.bolt.api.DatabaseName; +import org.neo4j.driver.internal.bolt.api.MetricsListener; +import org.neo4j.driver.internal.bolt.api.NotificationConfig; +import org.neo4j.driver.internal.bolt.api.RoutingContext; +import org.neo4j.driver.internal.bolt.api.SecurityPlan; + +public interface DriverBoltConnectionProvider { + @SuppressWarnings("UnusedReturnValue") + CompletionStage init( + BoltServerAddress address, + RoutingContext routingContext, + BoltAgent boltAgent, + String userAgent, + int connectTimeoutMillis, + MetricsListener metricsListener); + + CompletionStage connect( + SecurityPlan securityPlan, + DatabaseName databaseName, + Supplier>> authMapStageSupplier, + AccessMode mode, + Set bookmarks, + String impersonatedUser, + BoltProtocolVersion minVersion, + NotificationConfig notificationConfig, + Consumer databaseNameConsumer); + + CompletionStage verifyConnectivity(SecurityPlan securityPlan, Map authMap); + + CompletionStage supportsMultiDb(SecurityPlan securityPlan, Map authMap); + + CompletionStage supportsSessionAuth(SecurityPlan securityPlan, Map authMap); + + CompletionStage close(); +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverResponseHandler.java new file mode 100644 index 0000000000..58bdc4237c --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverResponseHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; +import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; +import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogoffSummary; +import org.neo4j.driver.internal.bolt.api.summary.LogonSummary; +import org.neo4j.driver.internal.bolt.api.summary.PullSummary; +import org.neo4j.driver.internal.bolt.api.summary.ResetSummary; +import org.neo4j.driver.internal.bolt.api.summary.RollbackSummary; +import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; +import org.neo4j.driver.internal.bolt.api.summary.RunSummary; +import org.neo4j.driver.internal.bolt.api.summary.TelemetrySummary; + +public interface DriverResponseHandler { + + void onError(Throwable throwable); + + default void onBeginSummary(BeginSummary summary) { + // ignored + } + + default void onRunSummary(RunSummary summary) { + // ignored + } + + default void onRecord(Value[] fields) { + // ignored + } + + default void onPullSummary(PullSummary summary) { + // ignored + } + + default void onDiscardSummary(DiscardSummary summary) { + // ignored + } + + default void onCommitSummary(CommitSummary summary) { + // ignored + } + + default void onRollbackSummary(RollbackSummary summary) { + // ignored + } + + default void onResetSummary(ResetSummary summary) { + // ignored + } + + default void onRouteSummary(RouteSummary summary) { + // ignored + } + + default void onLogoffSummary(LogoffSummary summary) { + // ignored + } + + default void onLogonSummary(LogonSummary summary) { + // ignored + } + + default void onTelemetrySummary(TelemetrySummary summary) { + // ignored + } + + default void onIgnored() { + // ignored + } + + default void onComplete() { + // ignored + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapper.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapper.java new file mode 100644 index 0000000000..bdf469025d --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapper.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import java.util.Map; +import java.util.concurrent.CompletionException; +import javax.net.ssl.SSLHandshakeException; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.AuthenticationException; +import org.neo4j.driver.exceptions.AuthorizationExpiredException; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; +import org.neo4j.driver.exceptions.DatabaseException; +import org.neo4j.driver.exceptions.DiscoveryException; +import org.neo4j.driver.exceptions.FatalDiscoveryException; +import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.exceptions.ProtocolException; +import org.neo4j.driver.exceptions.SecurityException; +import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.exceptions.TokenExpiredException; +import org.neo4j.driver.exceptions.TransactionTerminatedException; +import org.neo4j.driver.exceptions.TransientException; +import org.neo4j.driver.exceptions.UnsupportedFeatureException; +import org.neo4j.driver.exceptions.UntrustedServerException; +import org.neo4j.driver.internal.bolt.api.GqlStatusError; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionAcquisitionException; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionReadTimeoutException; +import org.neo4j.driver.internal.bolt.api.exception.BoltDiscoveryException; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltGqlErrorException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltTransientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUntrustedServerException; +import org.neo4j.driver.internal.util.Futures; + +public class ErrorMapper { + private static final ErrorMapper INSTANCE = new ErrorMapper(); + + public static ErrorMapper getInstance() { + return INSTANCE; + } + + protected ErrorMapper() {} + + T mapAndTrow(Throwable throwable) { + throwable = map(throwable); + if (throwable instanceof RuntimeException runtimeException) { + throw runtimeException; + } else { + throw new CompletionException(throwable); + } + } + + Throwable map(Throwable throwable) { + throwable = Futures.completionExceptionCause(throwable); + var result = throwable; + try { + if (throwable instanceof BoltFailureException boltFailureException) { + result = mapBoltFailureException(boltFailureException); + } else if (throwable instanceof BoltGqlErrorException boltGqlErrorException) { + result = mapGqlCause(boltGqlErrorException); + } else if (throwable instanceof BoltConnectionReadTimeoutException) { + result = ConnectionReadTimeoutException.INSTANCE; + } else if (throwable instanceof BoltServiceUnavailableException boltServiceUnavailableException) { + result = mapServiceUnavailable(boltServiceUnavailableException); + } else if (throwable instanceof BoltProtocolException) { + result = new ProtocolException(throwable.getMessage(), throwable); + } else if (throwable instanceof BoltUnsupportedFeatureException) { + result = new UnsupportedFeatureException(throwable.getMessage(), throwable); + } else if (throwable instanceof BoltUntrustedServerException) { + result = new UntrustedServerException(throwable.getMessage()); + } else if (throwable instanceof SSLHandshakeException) { + result = new SecurityException("Failed to establish secured connection with the server", throwable); + } else if (throwable instanceof BoltClientException) { + result = new ClientException( + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(throwable.getMessage()), + "N/A", + throwable.getMessage(), + GqlStatusError.DIAGNOSTIC_RECORD, + throwable); + } else if (throwable instanceof BoltTransientException) { + result = new TransientException( + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(throwable.getMessage()), + "N/A", + throwable.getMessage(), + GqlStatusError.DIAGNOSTIC_RECORD, + throwable); + } else if (throwable instanceof BoltConnectionAcquisitionException) { + result = new SessionExpiredException(throwable.getMessage(), throwable); + } + } catch (Throwable ignored) { + } + return result; + } + + protected Throwable mapBoltFailureException(BoltFailureException boltFailureException) { + var code = boltFailureException.code(); + var nested = boltFailureException.gqlCause().map(this::mapGqlCause).orElse(null); + return switch (extractErrorClass(code)) { + case "ClientError" -> { + if ("Security".equals(extractErrorSubClass(code))) { + if (code.equalsIgnoreCase("Neo.ClientError.Security.Unauthorized")) { + yield mapToNeo4jException(AuthenticationException::new, boltFailureException, nested); + } else if (code.equalsIgnoreCase("Neo.ClientError.Security.AuthorizationExpired")) { + yield mapToNeo4jException(AuthorizationExpiredException::new, boltFailureException, nested); + } else if (code.equalsIgnoreCase("Neo.ClientError.Security.TokenExpired")) { + yield mapToNeo4jException(TokenExpiredException::new, boltFailureException, nested); + } else { + yield mapToNeo4jException(SecurityException::new, boltFailureException, nested); + } + } else { + if (code.equalsIgnoreCase("Neo.ClientError.Database.DatabaseNotFound")) { + yield mapToNeo4jException(FatalDiscoveryException::new, boltFailureException, nested); + } else if (code.equalsIgnoreCase("Neo.ClientError.Transaction.Terminated")) { + yield mapToNeo4jException(TransactionTerminatedException::new, boltFailureException, nested); + } else { + yield mapToNeo4jException(ClientException::new, boltFailureException, nested); + } + } + } + case "TransientError" -> { + // Since 5.0 these 2 errors have been moved to ClientError class. + // This mapping is required if driver is connection to earlier server versions. + if ("Neo.TransientError.Transaction.Terminated".equals(code)) { + yield new TransactionTerminatedException( + boltFailureException.gqlStatus(), + boltFailureException.statusDescription(), + "Neo.ClientError.Transaction.Terminated", + boltFailureException.getMessage(), + boltFailureException.diagnosticRecord(), + nested); + } else if ("Neo.TransientError.Transaction.LockClientStopped".equals(code)) { + yield new ClientException( + boltFailureException.gqlStatus(), + boltFailureException.statusDescription(), + "Neo.ClientError.Transaction.LockClientStopped", + boltFailureException.getMessage(), + boltFailureException.diagnosticRecord(), + nested); + } else { + yield mapToNeo4jException(TransientException::new, boltFailureException, nested); + } + } + default -> mapToNeo4jException(DatabaseException::new, boltFailureException, nested); + }; + } + + protected Throwable mapGqlCause(BoltGqlErrorException boltGqlErrorException) { + return new Neo4jException( + boltGqlErrorException.gqlStatus(), + boltGqlErrorException.statusDescription(), + "N/A", + boltGqlErrorException.getMessage(), + boltGqlErrorException.diagnosticRecord(), + boltGqlErrorException.gqlCause().map(this::mapGqlCause).orElse(null)); + } + + protected Throwable mapServiceUnavailable(BoltServiceUnavailableException boltServiceUnavailableException) { + Throwable result = null; + + // A BoltServiceUnavailableException exception having suppressed BoltDiscoveryException must be mapped to + // ServiceUnavailableException with suppressed DiscoveryException. + for (var suppressed : boltServiceUnavailableException.getSuppressed()) { + if (suppressed instanceof BoltDiscoveryException boltDiscoveryException) { + if (result == null) { + result = new ServiceUnavailableException(boltServiceUnavailableException.getMessage()); + } + // The DiscoveryException must have the original exception sequence. + result.addSuppressed( + new DiscoveryException(boltDiscoveryException.getMessage(), boltDiscoveryException)); + } + } + + if (result == null) { + result = new ServiceUnavailableException( + boltServiceUnavailableException.getMessage(), boltServiceUnavailableException); + } + + return result; + } + + private static String extractErrorClass(String code) { + var parts = code.split("\\."); + if (parts.length < 2) { + return ""; + } + return parts[1]; + } + + private static String extractErrorSubClass(String code) { + var parts = code.split("\\."); + if (parts.length < 3) { + return ""; + } + return parts[2]; + } + + private T mapToNeo4jException( + Neo4jExceptionBuilder builder, BoltFailureException boltFailureException, Throwable cause) { + return builder.build( + boltFailureException.gqlStatus(), + boltFailureException.statusDescription(), + boltFailureException.code(), + boltFailureException.getMessage(), + boltFailureException.diagnosticRecord(), + cause); + } + + @FunctionalInterface + private interface Neo4jExceptionBuilder { + T build( + String gqlStatus, + String statusDescription, + String code, + String message, + Map diagnosticRecord, + Throwable cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/RoutedErrorMapper.java b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/RoutedErrorMapper.java new file mode 100644 index 0000000000..a0e4923b99 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/RoutedErrorMapper.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import static java.lang.String.format; + +import java.util.Objects; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.internal.bolt.api.AccessMode; +import org.neo4j.driver.internal.bolt.api.BoltServerAddress; +import org.neo4j.driver.internal.bolt.api.GqlStatusError; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; + +class RoutedErrorMapper extends ErrorMapper { + private final BoltServerAddress address; + private final AccessMode accessMode; + + RoutedErrorMapper(BoltServerAddress address, AccessMode accessMode) { + this.address = Objects.requireNonNull(address); + this.accessMode = Objects.requireNonNull(accessMode); + } + + @Override + protected Throwable mapBoltFailureException(BoltFailureException boltFailureException) { + Throwable result; + if ("Neo.ClientError.Cluster.NotALeader".equals(boltFailureException.code()) + || "Neo.ClientError.General.ForbiddenOnReadOnlyDatabase".equals(boltFailureException.code())) { + result = switch (accessMode) { + case READ -> { + var message = "Write queries cannot be performed in READ access mode."; + yield new ClientException( + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(message), + "N/A", + message, + GqlStatusError.DIAGNOSTIC_RECORD, + boltFailureException + .gqlCause() + .map(this::mapGqlCause) + .orElse(null)); + } + case WRITE -> new SessionExpiredException( + format("Server at %s no longer accepts writes", address), boltFailureException);}; + } else { + result = super.mapBoltFailureException(boltFailureException); + } + return result; + } + + @Override + protected Throwable mapServiceUnavailable(BoltServiceUnavailableException boltServiceUnavailableException) { + return new SessionExpiredException( + format("Server at %s is no longer available", address), boltServiceUnavailableException); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithAuthTokenManager.java b/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithAuthTokenManager.java index 0e02bfacc4..6bacdc1ce7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithAuthTokenManager.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithAuthTokenManager.java @@ -21,20 +21,20 @@ import org.neo4j.driver.AuthTokenManager; import org.neo4j.driver.exceptions.SecurityException; import org.neo4j.driver.exceptions.SecurityRetryableException; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.security.InternalAuthToken; final class BoltConnectionWithAuthTokenManager extends DelegatingBoltConnection { private final AuthTokenManager authTokenManager; - public BoltConnectionWithAuthTokenManager(BoltConnection delegate, AuthTokenManager authTokenManager) { + public BoltConnectionWithAuthTokenManager(DriverBoltConnection delegate, AuthTokenManager authTokenManager) { super(delegate); this.authTokenManager = Objects.requireNonNull(authTokenManager); } @Override - public CompletionStage flush(ResponseHandler handler) { + public CompletionStage flush(DriverResponseHandler handler) { return delegate.flush(new ErrorMappingResponseHandler(handler, this::mapSecurityError)); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithCloseTracking.java b/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithCloseTracking.java index 837125e299..2770c169a6 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithCloseTracking.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/BoltConnectionWithCloseTracking.java @@ -18,12 +18,12 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; final class BoltConnectionWithCloseTracking extends DelegatingBoltConnection { private final AtomicBoolean open = new AtomicBoolean(true); - BoltConnectionWithCloseTracking(BoltConnection delegate) { + BoltConnectionWithCloseTracking(DriverBoltConnection delegate) { super(delegate); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingBoltConnection.java index c1e1136a5a..9cb510e2f7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingBoltConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingBoltConnection.java @@ -22,38 +22,38 @@ import java.util.Set; import java.util.concurrent.CompletionStage; import org.neo4j.driver.Value; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.AuthData; -import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.BoltConnectionState; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.bolt.api.NotificationConfig; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.TransactionType; -public abstract class DelegatingBoltConnection implements BoltConnection { - protected final BoltConnection delegate; +public abstract class DelegatingBoltConnection implements DriverBoltConnection { + protected final DriverBoltConnection delegate; - protected DelegatingBoltConnection(BoltConnection delegate) { + protected DelegatingBoltConnection(DriverBoltConnection delegate) { this.delegate = Objects.requireNonNull(delegate); } @Override - public CompletionStage onLoop() { + public CompletionStage onLoop() { return delegate.onLoop().thenApply(ignored -> this); } @Override - public CompletionStage route( + public CompletionStage route( DatabaseName databaseName, String impersonatedUser, Set bookmarks) { return delegate.route(databaseName, impersonatedUser, bookmarks).thenApply(ignored -> this); } @Override - public CompletionStage beginTransaction( + public CompletionStage beginTransaction( DatabaseName databaseName, AccessMode accessMode, String impersonatedUser, @@ -77,7 +77,7 @@ public CompletionStage beginTransaction( } @Override - public CompletionStage runInAutoCommitTransaction( + public CompletionStage runInAutoCommitTransaction( DatabaseName databaseName, AccessMode accessMode, String impersonatedUser, @@ -101,57 +101,57 @@ public CompletionStage runInAutoCommitTransaction( } @Override - public CompletionStage run(String query, Map parameters) { + public CompletionStage run(String query, Map parameters) { return delegate.run(query, parameters).thenApply(ignored -> this); } @Override - public CompletionStage pull(long qid, long request) { + public CompletionStage pull(long qid, long request) { return delegate.pull(qid, request).thenApply(ignored -> this); } @Override - public CompletionStage discard(long qid, long number) { + public CompletionStage discard(long qid, long number) { return delegate.discard(qid, number).thenApply(ignored -> this); } @Override - public CompletionStage commit() { + public CompletionStage commit() { return delegate.commit().thenApply(ignored -> this); } @Override - public CompletionStage rollback() { + public CompletionStage rollback() { return delegate.rollback().thenApply(ignored -> this); } @Override - public CompletionStage reset() { + public CompletionStage reset() { return delegate.reset().thenApply(ignored -> this); } @Override - public CompletionStage logoff() { + public CompletionStage logoff() { return delegate.logoff().thenApply(ignored -> this); } @Override - public CompletionStage logon(Map authMap) { + public CompletionStage logon(Map authMap) { return delegate.logon(authMap).thenApply(ignored -> this); } @Override - public CompletionStage telemetry(TelemetryApi telemetryApi) { + public CompletionStage telemetry(TelemetryApi telemetryApi) { return delegate.telemetry(telemetryApi).thenApply(ignored -> this); } @Override - public CompletionStage clear() { + public CompletionStage clear() { return delegate.clear().thenApply(ignored -> this); } @Override - public CompletionStage flush(ResponseHandler handler) { + public CompletionStage flush(DriverResponseHandler handler) { return delegate.flush(handler); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingResponseHandler.java index f1a0ce9402..e063fbaf50 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/DelegatingResponseHandler.java @@ -18,7 +18,7 @@ import java.util.Objects; import org.neo4j.driver.Value; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; @@ -31,10 +31,10 @@ import org.neo4j.driver.internal.bolt.api.summary.RunSummary; import org.neo4j.driver.internal.bolt.api.summary.TelemetrySummary; -abstract class DelegatingResponseHandler implements ResponseHandler { - protected final ResponseHandler delegate; +abstract class DelegatingResponseHandler implements DriverResponseHandler { + protected final DriverResponseHandler delegate; - DelegatingResponseHandler(ResponseHandler delegate) { + DelegatingResponseHandler(DriverResponseHandler delegate) { this.delegate = Objects.requireNonNull(delegate); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ErrorMappingResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/ErrorMappingResponseHandler.java index ce65e5064c..b6e1bb0084 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ErrorMappingResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/ErrorMappingResponseHandler.java @@ -18,12 +18,12 @@ import java.util.Objects; import java.util.function.Function; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; final class ErrorMappingResponseHandler extends DelegatingResponseHandler { private final Function errorMapper; - ErrorMappingResponseHandler(ResponseHandler delegate, Function errorMapper) { + ErrorMappingResponseHandler(DriverResponseHandler delegate, Function errorMapper) { super(delegate); this.errorMapper = Objects.requireNonNull(errorMapper); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java index e6cb6df4fb..8ff45c2684 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java @@ -28,7 +28,7 @@ import org.neo4j.driver.BookmarkManager; import org.neo4j.driver.Logging; import org.neo4j.driver.NotificationConfig; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.security.BoltSecurityPlanManager; @@ -39,7 +39,7 @@ public class LeakLoggingNetworkSession extends NetworkSession { public LeakLoggingNetworkSession( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, DatabaseName databaseName, AccessMode mode, diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java index 09db8fbc04..153156e6e8 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java @@ -55,14 +55,14 @@ import org.neo4j.driver.internal.DatabaseBookmark; import org.neo4j.driver.internal.FailableCursor; import org.neo4j.driver.internal.NotificationConfigMapper; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.bolt.api.DatabaseNameUtil; import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.NotificationConfig; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.exception.MinVersionAcquisitionException; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; @@ -79,7 +79,7 @@ public class NetworkSession { private final BoltSecurityPlanManager securityPlanManager; - private final BoltConnectionProvider boltConnectionProvider; + private final DriverBoltConnectionProvider boltConnectionProvider; private final NetworkSessionConnectionContext connectionContext; private final AccessMode mode; private final RetryLogic retryLogic; @@ -102,7 +102,7 @@ public class NetworkSession { public NetworkSession( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider boltConnectionProvider, + DriverBoltConnectionProvider boltConnectionProvider, RetryLogic retryLogic, DatabaseName databaseName, AccessMode mode, @@ -319,7 +319,7 @@ public CompletionStage resetAsync() { var future = new CompletableFuture(); return connection .reset() - .thenCompose(conn -> conn.flush(new ResponseHandler() { + .thenCompose(conn -> conn.flush(new DriverResponseHandler() { @Override public void onError(Throwable throwable) { future.completeExceptionally(throwable); @@ -356,7 +356,7 @@ public CompletionStage releaseConnectionAsync() { }); } - public CompletionStage connectionAsync() { + public CompletionStage connectionAsync() { return connectionStage.thenApply(Function.identity()); } @@ -465,7 +465,7 @@ private CompletionStage acquireConnection(Acces (name) -> connectionContext .databaseNameFuture() .complete(name == null ? DatabaseNameUtil.defaultDatabase() : name)) - .thenApply(connection -> (BoltConnection) new BoltConnectionWithAuthTokenManager( + .thenApply(connection -> (DriverBoltConnection) new BoltConnectionWithAuthTokenManager( connection, overrideAuthToken != null ? new AuthTokenManager() { @@ -647,10 +647,10 @@ public AuthToken overrideAuthToken() { } } - public static class RunRxResponseHandler implements ResponseHandler { + public static class RunRxResponseHandler implements DriverResponseHandler { final CompletableFuture cursorFuture = new CompletableFuture<>(); private final Logging logging; - private final BoltConnection connection; + private final DriverBoltConnection connection; private final Query query; private final Consumer bookmarkConsumer; private final AtomicBoolean runFailed; @@ -660,7 +660,7 @@ public static class RunRxResponseHandler implements ResponseHandler { public RunRxResponseHandler( Logging logging, - BoltConnection connection, + DriverBoltConnection connection, Query query, Consumer bookmarkConsumer, AtomicBoolean runFailed) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareBoltConnection.java index 20d1556c45..02a10d11e9 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareBoltConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareBoltConnection.java @@ -23,8 +23,8 @@ import java.util.function.Consumer; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.util.Futures; final class TerminationAwareBoltConnection extends DelegatingBoltConnection { @@ -35,7 +35,7 @@ final class TerminationAwareBoltConnection extends DelegatingBoltConnection { public TerminationAwareBoltConnection( Logging logging, - BoltConnection delegate, + DriverBoltConnection delegate, TerminationAwareStateLockingExecutor executor, Consumer throwableConsumer) { super(delegate); @@ -45,15 +45,15 @@ public TerminationAwareBoltConnection( this.throwableConsumer = Objects.requireNonNull(throwableConsumer); } - public CompletionStage clearAndReset() { - var future = new CompletableFuture(); + public CompletionStage clearAndReset() { + var future = new CompletableFuture(); var thisVal = this; delegate.onLoop() .thenCompose(connection -> executor.execute(ignored -> connection .clear() - .thenCompose(BoltConnection::reset) - .thenCompose(conn -> conn.flush(new ResponseHandler() { + .thenCompose(DriverBoltConnection::reset) + .thenCompose(conn -> conn.flush(new DriverResponseHandler() { Throwable throwable = null; @Override @@ -83,7 +83,7 @@ public void onComplete() { } @Override - public CompletionStage flush(ResponseHandler handler) { + public CompletionStage flush(DriverResponseHandler handler) { return delegate.onLoop() .thenCompose(connection -> executor.execute(causeOfTermination -> { if (causeOfTermination == null) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareResponseHandler.java index 2e6de3b31e..2c1715bdff 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/TerminationAwareResponseHandler.java @@ -21,7 +21,7 @@ import java.util.function.Function; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.util.Futures; final class TerminationAwareResponseHandler extends DelegatingResponseHandler { @@ -31,7 +31,7 @@ final class TerminationAwareResponseHandler extends DelegatingResponseHandler { TerminationAwareResponseHandler( Logging logging, - ResponseHandler delegate, + DriverResponseHandler delegate, TerminationAwareStateLockingExecutor executor, Consumer throwableConsumer) { super(delegate); diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/UnmanagedTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/UnmanagedTransaction.java index 8dceb85656..46e3c0234e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/UnmanagedTransaction.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/UnmanagedTransaction.java @@ -44,13 +44,13 @@ import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.exceptions.TransactionTerminatedException; import org.neo4j.driver.internal.DatabaseBookmark; +import org.neo4j.driver.internal.adaptedbolt.BasicResponseHandler; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.AccessMode; -import org.neo4j.driver.internal.bolt.api.BasicResponseHandler; -import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.NotificationConfig; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TransactionType; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; @@ -119,7 +119,7 @@ private enum State { private final ApiTelemetryWork apiTelemetryWork; public UnmanagedTransaction( - BoltConnection connection, + DriverBoltConnection connection, DatabaseName databaseName, AccessMode accessMode, String impersonatedUser, @@ -142,7 +142,7 @@ public UnmanagedTransaction( } protected UnmanagedTransaction( - BoltConnection connection, + DriverBoltConnection connection, DatabaseName databaseName, AccessMode accessMode, String impersonatedUser, @@ -261,26 +261,28 @@ public boolean isOpen() { public void markTerminated(Throwable cause) { var throwable = Futures.completionExceptionCause(cause); executeWithLock(lock, () -> { - if (state == State.TERMINATED) { - if (throwable != null) { - addSuppressedWhenNotCaptured(causeOfTermination, throwable); + if (isOpen() && commitFuture == null && rollbackFuture == null) { + if (state == State.TERMINATED) { + if (throwable != null) { + addSuppressedWhenNotCaptured(causeOfTermination, throwable); + } + } else { + state = State.TERMINATED; + causeOfTermination = throwable != null + ? throwable + : new TransactionTerminatedException( + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(EXPLICITLY_TERMINATED_MSG), + "N/A", + EXPLICITLY_TERMINATED_MSG, + GqlStatusError.DIAGNOSTIC_RECORD, + null); } - } else { - state = State.TERMINATED; - causeOfTermination = throwable != null - ? throwable - : new TransactionTerminatedException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(EXPLICITLY_TERMINATED_MSG), - "N/A", - EXPLICITLY_TERMINATED_MSG, - GqlStatusError.DIAGNOSTIC_RECORD, - null); } }); } - public BoltConnection connection() { + public DriverBoltConnection connection() { return connection; } @@ -576,7 +578,7 @@ private CompletionStage closeAsync(boolean commit, boolean completeWithNul return stage; } - private static class BeginResponseHandler implements ResponseHandler { + private static class BeginResponseHandler implements DriverResponseHandler { final CompletableFuture summaryFuture = new CompletableFuture<>(); private final ApiTelemetryWork apiTelemetryWork; private Throwable error; @@ -639,12 +641,12 @@ public void onComplete() { } } - private static class RunRxResponseHandler implements ResponseHandler { + private static class RunRxResponseHandler implements DriverResponseHandler { final CompletableFuture cursorFuture = new CompletableFuture<>(); private final Logging logging; private final ApiTelemetryWork apiTelemetryWork; private final CompletableFuture beginFuture; - private final BoltConnection connection; + private final DriverBoltConnection connection; private final Query query; private Throwable error; private RunSummary runSummary; @@ -654,7 +656,7 @@ private RunRxResponseHandler( Logging logging, ApiTelemetryWork apiTelemetryWork, CompletableFuture beginFuture, - BoltConnection connection, + DriverBoltConnection connection, Query query) { this.logging = logging; this.apiTelemetryWork = apiTelemetryWork; diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/BasicResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/BasicResponseHandler.java index 7160ea63ec..c1630b1079 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/BasicResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/BasicResponseHandler.java @@ -22,7 +22,7 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.internal.bolt.api.exception.BoltException; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; @@ -65,7 +65,7 @@ public void onError(Throwable throwable) { if (error == null) { error = throwable; } else { - if (error instanceof Neo4jException && !(throwable instanceof Neo4jException)) { + if (error instanceof BoltException && !(throwable instanceof BoltException)) { // higher order error has occurred error = throwable; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltClientException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltClientException.java new file mode 100644 index 0000000000..0968fd3863 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltClientException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltClientException extends BoltException { + @Serial + private static final long serialVersionUID = -5218004917132451861L; + + public BoltClientException(String message) { + super(message); + } + + public BoltClientException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionAcquisitionException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionAcquisitionException.java new file mode 100644 index 0000000000..7ef8e5cc86 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionAcquisitionException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltConnectionAcquisitionException extends BoltException { + @Serial + private static final long serialVersionUID = -5218004917132451861L; + + public BoltConnectionAcquisitionException(String message) { + super(message); + } + + public BoltConnectionAcquisitionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionReadTimeoutException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionReadTimeoutException.java new file mode 100644 index 0000000000..b0ddd59f49 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltConnectionReadTimeoutException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltConnectionReadTimeoutException extends BoltServiceUnavailableException { + @Serial + private static final long serialVersionUID = -5218004917132451861L; + + public BoltConnectionReadTimeoutException(String message) { + super(message); + } + + public BoltConnectionReadTimeoutException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltDiscoveryException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltDiscoveryException.java new file mode 100644 index 0000000000..9478cfd86e --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltDiscoveryException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltDiscoveryException extends BoltException { + @Serial + private static final long serialVersionUID = -177641311561760099L; + + public BoltDiscoveryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltException.java new file mode 100644 index 0000000000..9f09b02fbb --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltException extends RuntimeException { + @Serial + private static final long serialVersionUID = -5218004917132451861L; + + public BoltException(String message) { + super(message); + } + + public BoltException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltFailureException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltFailureException.java new file mode 100644 index 0000000000..662dbe4aba --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltFailureException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; +import java.util.Map; +import java.util.Objects; +import org.neo4j.driver.Value; + +public class BoltFailureException extends BoltGqlErrorException { + @Serial + private static final long serialVersionUID = -1731084000671105197L; + + private final String code; + + public BoltFailureException( + String code, + String message, + String gqlStatus, + String statusDescription, + Map diagnosticRecord, + Throwable cause) { + super(message, gqlStatus, statusDescription, diagnosticRecord, cause); + this.code = Objects.requireNonNull(code); + } + + public String code() { + return code; + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltGqlErrorException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltGqlErrorException.java new file mode 100644 index 0000000000..a09ade0764 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltGqlErrorException.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.neo4j.driver.Value; + +public class BoltGqlErrorException extends BoltException { + @Serial + private static final long serialVersionUID = -1731084000671105197L; + + private final String gqlStatus; + private final String statusDescription; + private final Map diagnosticRecord; + + public BoltGqlErrorException( + String message, + String gqlStatus, + String statusDescription, + Map diagnosticRecord, + Throwable cause) { + super(message, cause); + this.gqlStatus = Objects.requireNonNull(gqlStatus); + this.statusDescription = Objects.requireNonNull(statusDescription); + this.diagnosticRecord = Objects.requireNonNull(diagnosticRecord); + } + + public String gqlStatus() { + return gqlStatus; + } + + public String statusDescription() { + return statusDescription; + } + + public Map diagnosticRecord() { + return diagnosticRecord; + } + + public Optional gqlCause() { + return findFirstGqlCause(this, BoltGqlErrorException.class); + } + + @SuppressWarnings("DuplicatedCode") + private static Optional findFirstGqlCause(Throwable throwable, Class targetCls) { + var cause = throwable.getCause(); + if (cause == null) { + return Optional.empty(); + } + if (cause.getClass().isAssignableFrom(targetCls)) { + return Optional.of(targetCls.cast(cause)); + } else { + return findFirstGqlCause(cause, targetCls); + } + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltProtocolException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltProtocolException.java new file mode 100644 index 0000000000..113632f5df --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltProtocolException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltProtocolException extends BoltException { + @Serial + private static final long serialVersionUID = 7170273806952291048L; + + public BoltProtocolException(String message) { + super(message); + } + + public BoltProtocolException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltServiceUnavailableException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltServiceUnavailableException.java new file mode 100644 index 0000000000..6faae2eb2d --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltServiceUnavailableException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltServiceUnavailableException extends BoltException { + @Serial + private static final long serialVersionUID = 1108615355108108484L; + + public BoltServiceUnavailableException(String message) { + super(message); + } + + public BoltServiceUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltTransientException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltTransientException.java new file mode 100644 index 0000000000..cd06b84188 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltTransientException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltTransientException extends BoltException { + @Serial + private static final long serialVersionUID = -1731084000671105197L; + + public BoltTransientException(String message) { + super(message); + } + + public BoltTransientException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUnsupportedFeatureException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUnsupportedFeatureException.java new file mode 100644 index 0000000000..860c8ba8cb --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUnsupportedFeatureException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltUnsupportedFeatureException extends BoltException { + @Serial + private static final long serialVersionUID = 5397629610198032003L; + + public BoltUnsupportedFeatureException(String message) { + super(message); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUntrustedServerException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUntrustedServerException.java new file mode 100644 index 0000000000..2609f78de1 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/BoltUntrustedServerException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.api.exception; + +import java.io.Serial; + +public class BoltUntrustedServerException extends BoltException { + @Serial + private static final long serialVersionUID = -5218004917132451861L; + + public BoltUntrustedServerException(String message) { + super(message); + } + + public BoltUntrustedServerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MinVersionAcquisitionException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MinVersionAcquisitionException.java index 82e3497e4e..b6be98b9e6 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MinVersionAcquisitionException.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MinVersionAcquisitionException.java @@ -17,10 +17,9 @@ package org.neo4j.driver.internal.bolt.api.exception; import java.io.Serial; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; -public class MinVersionAcquisitionException extends Neo4jException { +public class MinVersionAcquisitionException extends BoltException { @Serial private static final long serialVersionUID = 2620821821322630443L; diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/BoltConnectionImpl.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/BoltConnectionImpl.java index 9c26dd09f4..cf38d92668 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/BoltConnectionImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/BoltConnectionImpl.java @@ -34,12 +34,6 @@ import java.util.stream.Collectors; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; -import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; -import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.ProtocolException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.UnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.AuthData; import org.neo4j.driver.internal.bolt.api.BoltConnection; @@ -53,7 +47,12 @@ import org.neo4j.driver.internal.bolt.api.RoutingContext; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.TransactionType; -import org.neo4j.driver.internal.bolt.api.exception.MessageIgnoredException; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionReadTimeoutException; +import org.neo4j.driver.internal.bolt.api.exception.BoltException; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; @@ -376,7 +375,7 @@ public void onSummary(Void summary) { public CompletionStage telemetry(TelemetryApi telemetryApi) { return executeInEventLoop(() -> { if (!telemetrySupported()) { - throw new UnsupportedFeatureException("telemetry not supported"); + throw new BoltUnsupportedFeatureException("telemetry not supported"); } else { messageWriters.add(handler -> protocol.telemetry(connection, telemetryApi.getValue(), new MessageHandler<>() { @@ -419,7 +418,7 @@ public CompletionStage flush(ResponseHandler handler) { throwable = FutureUtil.completionExceptionCause(throwable); if (throwable instanceof CodecException && throwable.getCause() instanceof IOException) { - var serviceError = new ServiceUnavailableException( + var serviceError = new BoltServiceUnavailableException( "Connection to the database failed", throwable.getCause()); forceClose("Connection has been closed due to encoding error") .whenComplete((ignored1, ignored2) -> @@ -433,7 +432,7 @@ public CompletionStage flush(ResponseHandler handler) { } }); } else { - throw new ServiceUnavailableException("Connection is closed"); + throw new BoltServiceUnavailableException("Connection is closed"); } }) .thenCompose(ignored -> flushFuture); @@ -526,18 +525,20 @@ private CompletionStage executeInEventLoop(Runnable runnable) { } private void updateState(Throwable throwable) { - if (throwable instanceof ServiceUnavailableException) { - if (throwable instanceof ConnectionReadTimeoutException) { + if (throwable instanceof BoltServiceUnavailableException) { + if (throwable instanceof BoltConnectionReadTimeoutException) { stateRef.compareAndExchange(BoltConnectionState.OPEN, BoltConnectionState.ERROR); } else { stateRef.set(BoltConnectionState.CLOSED); } - } else if (throwable instanceof Neo4jException) { - if (throwable instanceof AuthorizationExpiredException) { + } else if (throwable instanceof BoltFailureException boltFailureException) { + if ("Neo.ClientError.Security.AuthorizationExpired".equals(boltFailureException.code())) { stateRef.compareAndExchange(BoltConnectionState.OPEN, BoltConnectionState.ERROR); } else { stateRef.compareAndExchange(BoltConnectionState.OPEN, BoltConnectionState.FAILURE); } + } else if (throwable instanceof MessageIgnoredException) { + stateRef.compareAndExchange(BoltConnectionState.OPEN, BoltConnectionState.FAILURE); } else { stateRef.updateAndGet(state -> switch (state) { case OPEN, FAILURE, ERROR -> BoltConnectionState.ERROR; @@ -565,9 +566,9 @@ public void onError(Throwable throwable) { if (!(throwable instanceof MessageIgnoredException)) { if (!summariesFuture.isDone()) { runIgnoringError(() -> delegate.onError(throwable)); - if (!(throwable instanceof Neo4jException) - || throwable instanceof ServiceUnavailableException - || throwable instanceof ProtocolException) { + if (!(throwable instanceof BoltException) + || throwable instanceof BoltServiceUnavailableException + || throwable instanceof BoltProtocolException) { // assume unrecoverable error, ensure onComplete expectedSummaries = 1; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MessageIgnoredException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/MessageIgnoredException.java similarity index 82% rename from driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MessageIgnoredException.java rename to driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/MessageIgnoredException.java index 591b2a5847..5c0e46e08d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/api/exception/MessageIgnoredException.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/MessageIgnoredException.java @@ -14,12 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.bolt.api.exception; +package org.neo4j.driver.internal.bolt.basicimpl; import java.io.Serial; -import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.internal.bolt.api.exception.BoltException; -public class MessageIgnoredException extends Neo4jException { +public class MessageIgnoredException extends BoltException { @Serial private static final long serialVersionUID = 8087512561960062490L; diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListener.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListener.java index 7b5b8e6607..c5960da681 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListener.java @@ -24,10 +24,9 @@ import io.netty.channel.ChannelFutureListener; import java.util.concurrent.CompletableFuture; import javax.net.ssl.SSLHandshakeException; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.LoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelActivityLogger; public class ChannelConnectedListener implements ChannelFutureListener { @@ -60,10 +59,8 @@ public void operationComplete(ChannelFuture future) { channel.writeAndFlush(BoltProtocolUtil.handshakeBuf()).addListener(f -> { if (!f.isSuccess()) { var error = f.cause(); - if (error instanceof SSLHandshakeException) { - error = new SecurityException("Failed to establish secured connection with the server", error); - } else { - error = new ServiceUnavailableException( + if (!(error instanceof SSLHandshakeException)) { + error = new BoltServiceUnavailableException( String.format("Unable to write Bolt handshake to %s.", this.address), error); } this.handshakeCompletedFuture.completeExceptionally(error); @@ -75,7 +72,7 @@ public void operationComplete(ChannelFuture future) { } private static Throwable databaseUnavailableError(BoltServerAddress address, Throwable cause) { - return new ServiceUnavailableException( + return new BoltServiceUnavailableException( format( "Unable to connect to %s, ensure the database is running and that there " + "is a working network connection to it.", diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandler.java index 12896667d5..b8281d7ce7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandler.java @@ -26,17 +26,14 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import javax.net.ssl.SSLHandshakeException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelActivityLogger; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelErrorLogger; import org.neo4j.driver.internal.bolt.basicimpl.messaging.BoltProtocol; import org.neo4j.driver.internal.bolt.basicimpl.messaging.MessageFormat; -import org.neo4j.driver.internal.util.ErrorUtil; public class HandshakeHandler extends ReplayingDecoder { private final ChannelPipelineBuilder pipelineBuilder; @@ -74,11 +71,17 @@ public void channelInactive(ChannelHandlerContext ctx) { if (!failed) { // channel became inactive while doing bolt handshake, not because of some previous error - var error = ErrorUtil.newConnectionTerminatedError(); + var error = newConnectionTerminatedError(); fail(ctx, error); } } + public static BoltServiceUnavailableException newConnectionTerminatedError() { + return new BoltServiceUnavailableException("Connection to the database terminated. " + + "Please ensure that your database is listening on the correct host and port and that you have compatible encryption settings both on Neo4j server and driver. " + + "Note that the default encryption setting has changed in Neo4j 4.0."); + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable error) { if (failed) { @@ -109,7 +112,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { private BoltProtocol protocolForVersion(BoltProtocolVersion version) { try { return BoltProtocol.forVersion(version); - } catch (ClientException e) { + } catch (BoltClientException e) { return null; } } @@ -135,39 +138,20 @@ private void fail(ChannelHandlerContext ctx, Throwable error) { } private static Throwable protocolNoSupportedByServerError() { - var message = "The server does not support any of the protocol versions supported by " + return new BoltClientException("The server does not support any of the protocol versions supported by " + "this driver. Ensure that you are using driver and server versions that " - + "are compatible with one another."; - return new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + + "are compatible with one another."); } private static Throwable httpEndpointError() { - var message = "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " - + "(HTTP defaults to port 7474 whereas BOLT defaults to port 7687)"; - return new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + return new BoltClientException( + "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " + + "(HTTP defaults to port 7474 whereas BOLT defaults to port 7687)"); } private static Throwable protocolNoSupportedByDriverError(BoltProtocolVersion suggestedProtocolVersion) { - var message = "Protocol error, server suggested unexpected protocol version: " + suggestedProtocolVersion; - return new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + return new BoltClientException( + "Protocol error, server suggested unexpected protocol version: " + suggestedProtocolVersion); } private static Throwable transformError(Throwable error) { @@ -176,12 +160,10 @@ private static Throwable transformError(Throwable error) { error = error.getCause(); } - if (error instanceof ServiceUnavailableException) { + if (error instanceof BoltServiceUnavailableException || error instanceof SSLHandshakeException) { return error; - } else if (error instanceof SSLHandshakeException) { - return new SecurityException("Failed to establish secured connection with the server", error); } else { - return new ServiceUnavailableException("Failed to establish connection with the server", error); + return new BoltServiceUnavailableException("Failed to establish connection with the server", error); } } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ChannelErrorHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ChannelErrorHandler.java index 8c8829d9f3..5307ce8ead 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ChannelErrorHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ChannelErrorHandler.java @@ -22,13 +22,12 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.CodecException; import java.io.IOException; -import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.api.LoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionReadTimeoutException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.ChannelAttributes; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelActivityLogger; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelErrorLogger; -import org.neo4j.driver.internal.util.ErrorUtil; public class ChannelErrorHandler extends ChannelInboundHandlerAdapter { private final LoggingProvider logging; @@ -61,7 +60,11 @@ public void channelInactive(ChannelHandlerContext ctx) { log.log(System.Logger.Level.DEBUG, "Channel is inactive"); var terminationReason = ChannelAttributes.terminationReason(ctx.channel()); - Throwable error = ErrorUtil.newConnectionTerminatedError(terminationReason); + Throwable error = terminationReason == null + ? new BoltServiceUnavailableException("Connection to the database terminated. " + + "Please ensure that your database is listening on the correct host and port and that you have compatible encryption settings both on Neo4j server and driver. " + + "Note that the default encryption setting has changed in Neo4j 4.0.") + : new BoltServiceUnavailableException("Connection to the database terminated. " + terminationReason); if (!failed) { // channel became inactive not because of a fatal exception that came from exceptionCaught @@ -85,7 +88,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable error) { } private void logUnexpectedErrorWarning(Throwable error) { - if (!(error instanceof ConnectionReadTimeoutException)) { + if (!(error instanceof BoltConnectionReadTimeoutException)) { errorLog.traceOrDebug("Fatal error occurred in the pipeline", error); } } @@ -102,7 +105,7 @@ private static Throwable transformError(Throwable error) { } if (error instanceof IOException) { - return new ServiceUnavailableException("Connection to the database failed", error); + return new BoltServiceUnavailableException("Connection to the database failed", error); } else { return error; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandler.java index b7d6ae66fb..ed85423eef 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandler.java @@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.TimeUnit; -import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; /** * Handler needed to limit amount of time connection performs TLS and Bolt handshakes. @@ -43,7 +43,7 @@ protected void readTimedOut(ChannelHandlerContext ctx) { } } - private ServiceUnavailableException unableToConnectError() { - return new ServiceUnavailableException("Unable to establish connection in " + timeoutMillis + "ms"); + private BoltServiceUnavailableException unableToConnectError() { + return new BoltServiceUnavailableException("Unable to establish connection in " + timeoutMillis + "ms"); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandler.java index e3a85cfd30..0b659d248d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandler.java @@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.TimeUnit; -import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionReadTimeoutException; public class ConnectionReadTimeoutHandler extends ReadTimeoutHandler { private boolean triggered; @@ -31,7 +31,9 @@ public ConnectionReadTimeoutHandler(long timeout, TimeUnit unit) { @Override protected void readTimedOut(ChannelHandlerContext ctx) { if (!triggered) { - ctx.fireExceptionCaught(ConnectionReadTimeoutException.INSTANCE); + ctx.fireExceptionCaught( + new BoltConnectionReadTimeoutException( + "Connection read timed out due to it taking longer than the server-supplied timeout value via configuration hint.")); ctx.close(); triggered = true; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcher.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcher.java index 9d271e8470..d99f9a519a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcher.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcher.java @@ -26,12 +26,13 @@ import org.neo4j.driver.Value; import org.neo4j.driver.internal.bolt.api.GqlError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; -import org.neo4j.driver.internal.bolt.api.exception.MessageIgnoredException; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltGqlErrorException; +import org.neo4j.driver.internal.bolt.basicimpl.MessageIgnoredException; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelActivityLogger; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelErrorLogger; import org.neo4j.driver.internal.bolt.basicimpl.messaging.ResponseMessageHandler; import org.neo4j.driver.internal.bolt.basicimpl.spi.ResponseHandler; -import org.neo4j.driver.internal.util.ErrorUtil; public class InboundMessageDispatcher implements ResponseMessageHandler { private final Channel channel; @@ -101,12 +102,45 @@ public void handleFailureMessage(GqlError gqlError) { log.log(System.Logger.Level.DEBUG, "S: FAILURE %s \"%s\"", gqlError.code(), gqlError.message()); } - var error = ErrorUtil.newNeo4jError(gqlError); + var error = map(gqlError); invokeBeforeLastHandlerHook(); var handler = removeHandler(); handler.onFailure(error); } + private BoltFailureException map(GqlError error) { + var code = error.code(); + + // Since 5.0 these 2 errors have been moved to ClientError class. + // This mapping is required when driver is connected to earlier server versions. + if ("Neo.TransientError.Transaction.Terminated".equals(code)) { + code = "Neo.ClientError.Transaction.Terminated"; + } else if ("Neo.TransientError.Transaction.LockClientStopped".equals(code)) { + code = "Neo.ClientError.Transaction.LockClientStopped"; + } + + return new BoltFailureException( + code, + error.message(), + error.gqlStatus(), + error.statusDescription(), + error.diagnosticRecord(), + mapNested(error.cause())); + } + + private BoltGqlErrorException mapNested(GqlError error) { + BoltGqlErrorException mapped = null; + if (error != null) { + mapped = new BoltGqlErrorException( + error.message(), + error.gqlStatus(), + error.statusDescription(), + error.diagnosticRecord(), + mapNested(error.cause())); + } + return mapped; + } + @Override public void handleIgnoredMessage() { if (log.isLoggable(System.Logger.Level.DEBUG)) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogoffResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogoffResponseHandler.java index efac8d9740..4125652977 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogoffResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogoffResponseHandler.java @@ -21,7 +21,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ProtocolException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; import org.neo4j.driver.internal.bolt.basicimpl.spi.ResponseHandler; public class LogoffResponseHandler implements ResponseHandler { @@ -43,6 +43,6 @@ public void onFailure(Throwable error) { @Override public void onRecord(Value[] fields) { - this.future.completeExceptionally(new ProtocolException("Records are not supported on LOGON")); + this.future.completeExceptionally(new BoltProtocolException("Records are not supported on LOGON")); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogonResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogonResponseHandler.java index eed4abc75d..e774cf4ed4 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogonResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/LogonResponseHandler.java @@ -23,7 +23,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ProtocolException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; import org.neo4j.driver.internal.bolt.basicimpl.spi.ResponseHandler; public class LogonResponseHandler implements ResponseHandler { @@ -57,6 +57,6 @@ public void onFailure(Throwable error) { @Override public void onRecord(Value[] fields) { - future.completeExceptionally(new ProtocolException("Records are not supported on LOGON")); + future.completeExceptionally(new BoltProtocolException("Records are not supported on LOGON")); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocol.java index 1459efedaf..f9c7105908 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocol.java @@ -26,16 +26,15 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.UnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.BoltAgent; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; import org.neo4j.driver.internal.bolt.api.NotificationConfig; import org.neo4j.driver.internal.bolt.api.RoutingContext; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; @@ -121,12 +120,12 @@ CompletionStage run( CompletionStage reset(Connection connection, MessageHandler handler); default CompletionStage logoff(Connection connection, MessageHandler handler) { - return CompletableFuture.failedStage(new UnsupportedFeatureException("logoff not supported")); + return CompletableFuture.failedStage(new BoltUnsupportedFeatureException("logoff not supported")); } default CompletionStage logon( Connection connection, Map authMap, Clock clock, MessageHandler handler) { - return CompletableFuture.failedStage(new UnsupportedFeatureException("logon not supported")); + return CompletableFuture.failedStage(new BoltUnsupportedFeatureException("logon not supported")); } /** @@ -169,13 +168,6 @@ static BoltProtocol forVersion(BoltProtocolVersion version) { } else if (BoltProtocolV57.VERSION.equals(version)) { return BoltProtocolV57.INSTANCE; } - var message = "Unknown protocol version: " + version; - throw new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + throw new BoltClientException("Unknown protocol version: " + version); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/common/CommonValueUnpacker.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/common/CommonValueUnpacker.java index c716fa7c6a..a706de2471 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/common/CommonValueUnpacker.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/common/CommonValueUnpacker.java @@ -39,12 +39,11 @@ import java.util.Map; import java.util.function.Supplier; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.ProtocolException; import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; import org.neo4j.driver.internal.InternalRelationship; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; import org.neo4j.driver.internal.bolt.basicimpl.messaging.ValueUnpacker; import org.neo4j.driver.internal.bolt.basicimpl.packstream.PackInput; import org.neo4j.driver.internal.bolt.basicimpl.packstream.PackStream; @@ -355,32 +354,18 @@ protected Value unpackPath() throws IOException { protected final void ensureCorrectStructSize(TypeConstructor typeConstructor, int expected, long actual) { if (expected != actual) { var structName = typeConstructor.toString(); - var message = String.format( + throw new BoltClientException(String.format( "Invalid message received, serialized %s structures should have %d fields, " + "received %s structure has %d fields.", - structName, expected, structName, actual); - throw new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + structName, expected, structName, actual)); } } protected void ensureCorrectStructSignature(String structName, byte expected, byte actual) { if (expected != actual) { - var message = String.format( + throw new BoltClientException(String.format( "Invalid message received, expected a `%s`, signature 0x%s. Received signature was 0x%s.", - structName, Integer.toHexString(expected), Integer.toHexString(actual)); - throw new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + structName, Integer.toHexString(expected), Integer.toHexString(actual))); } } @@ -466,8 +451,8 @@ private ZonedDateTime newZonedDateTimeUsingUtcBaseline(long epochSecondLocal, in return ZonedDateTime.of(localDateTime, zoneId); } - private ProtocolException instantiateExceptionForUnknownType(byte type) { - return new ProtocolException("Unknown struct type: " + type); + private BoltProtocolException instantiateExceptionForUnknownType(byte type) { + return new BoltProtocolException("Unknown struct type: " + type); } protected int getNodeFields() { diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/request/MultiDatabaseUtil.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/request/MultiDatabaseUtil.java index d44b7236b6..7f4e8720bf 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/request/MultiDatabaseUtil.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/request/MultiDatabaseUtil.java @@ -16,25 +16,17 @@ */ package org.neo4j.driver.internal.bolt.basicimpl.messaging.request; -import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; public final class MultiDatabaseUtil { public static void assertEmptyDatabaseName(DatabaseName databaseName, BoltProtocolVersion boltVersion) { if (databaseName.databaseName().isPresent()) { - var message = String.format( + throw new BoltClientException(String.format( "Database name parameter for selecting database is not supported in Bolt Protocol Version %s. " + "Database name: '%s'", - boltVersion, databaseName.description()); - throw new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + boltVersion, databaseName.description())); } } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3.java index 9ba067ea92..a0293a40c5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3.java @@ -34,8 +34,6 @@ import org.neo4j.driver.Record; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.UnsupportedFeatureException; import org.neo4j.driver.internal.InternalRecord; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.BoltAgent; @@ -47,6 +45,8 @@ import org.neo4j.driver.internal.bolt.api.LoggingProvider; import org.neo4j.driver.internal.bolt.api.NotificationConfig; import org.neo4j.driver.internal.bolt.api.RoutingContext; +import org.neo4j.driver.internal.bolt.api.exception.BoltException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; @@ -316,7 +316,7 @@ public CompletionStage reset(Connection connection, MessageHandler h @Override public CompletionStage telemetry(Connection connection, Integer api, MessageHandler handler) { - return CompletableFuture.failedStage(new UnsupportedFeatureException("telemetry not supported")); + return CompletableFuture.failedStage(new BoltUnsupportedFeatureException("telemetry not supported")); } @SuppressWarnings("DuplicatedCode") @@ -417,10 +417,10 @@ protected boolean includeDateTimeUtcPatchInHello() { return false; } - protected Neo4jException verifyNotificationConfigSupported(NotificationConfig notificationConfig) { - Neo4jException exception = null; + protected BoltException verifyNotificationConfigSupported(NotificationConfig notificationConfig) { + BoltException exception = null; if (notificationConfig != null && !notificationConfig.equals(NotificationConfig.defaultConfig())) { - exception = new UnsupportedFeatureException(String.format( + exception = new BoltUnsupportedFeatureException(String.format( "Notification configuration is not supported on Bolt %s", version().toString())); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v52/BoltProtocolV52.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v52/BoltProtocolV52.java index abc2e735e3..04b23af13d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v52/BoltProtocolV52.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v52/BoltProtocolV52.java @@ -16,9 +16,9 @@ */ package org.neo4j.driver.internal.bolt.basicimpl.messaging.v52; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.NotificationConfig; +import org.neo4j.driver.internal.bolt.api.exception.BoltException; import org.neo4j.driver.internal.bolt.basicimpl.messaging.BoltProtocol; import org.neo4j.driver.internal.bolt.basicimpl.messaging.v51.BoltProtocolV51; @@ -27,7 +27,7 @@ public class BoltProtocolV52 extends BoltProtocolV51 { public static final BoltProtocol INSTANCE = new BoltProtocolV52(); @Override - protected Neo4jException verifyNotificationConfigSupported(NotificationConfig notificationConfig) { + protected BoltException verifyNotificationConfigSupported(NotificationConfig notificationConfig) { return null; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v57/MessageReaderV57.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v57/MessageReaderV57.java index 1b55ff5383..a6b3fe638c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v57/MessageReaderV57.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v57/MessageReaderV57.java @@ -22,8 +22,8 @@ import java.util.Map; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.ProtocolException; import org.neo4j.driver.internal.bolt.api.GqlError; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; import org.neo4j.driver.internal.bolt.basicimpl.messaging.ResponseMessageHandler; import org.neo4j.driver.internal.bolt.basicimpl.messaging.v5.MessageReaderV5; import org.neo4j.driver.internal.bolt.basicimpl.packstream.PackInput; @@ -82,7 +82,7 @@ protected GqlError unpackGqlError(Map params) { var cause = params.get("cause"); if (cause != null) { if (!MAP_TYPE.isTypeOf(cause)) { - throw new ProtocolException("Unexpected type"); + throw new BoltProtocolException("Unexpected type"); } gqlError = unpackGqlError(cause.asMap(Values::value)); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractor.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractor.java index 3e0442dbd1..4da05ad298 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractor.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractor.java @@ -23,7 +23,7 @@ import java.util.Map; import java.util.Set; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.UntrustedServerException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUntrustedServerException; public class MetadataExtractor { public static final int ABSENT_QUERY_ID = -1; @@ -67,11 +67,11 @@ public long extractResultAvailableAfter(Map metadata) { public static Value extractServer(Map metadata) { var versionValue = metadata.get("server"); if (versionValue == null || versionValue.isNull()) { - throw new UntrustedServerException("Server provides no product identifier"); + throw new BoltUntrustedServerException("Server provides no product identifier"); } var serverAgent = versionValue.asString(); if (!serverAgent.startsWith("Neo4j/")) { - throw new UntrustedServerException( + throw new BoltUntrustedServerException( "Server does not identify as a genuine Neo4j instance: '" + serverAgent + "'"); } return versionValue; diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnection.java index 2ff02ec6e1..aec3cc002c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnection.java @@ -23,7 +23,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.AuthData; import org.neo4j.driver.internal.bolt.api.BasicResponseHandler; @@ -36,6 +35,7 @@ import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.TransactionType; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; @@ -181,8 +181,10 @@ public CompletionStage flush(ResponseHandler handler) { @Override public void onError(Throwable throwable) { - if (throwable instanceof AuthorizationExpiredException) { - provider.onExpired(); + if (throwable instanceof BoltFailureException boltFailureException) { + if ("Neo.ClientError.Security.AuthorizationExpired".equals(boltFailureException.code())) { + provider.onExpired(); + } } handler.onError(throwable); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnectionProvider.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnectionProvider.java index 443dc7f148..3d669e9cb8 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnectionProvider.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/pooledimpl/PooledBoltConnectionProvider.java @@ -36,7 +36,6 @@ import java.util.function.Function; import java.util.function.Supplier; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.BasicResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltAgent; @@ -46,12 +45,12 @@ import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; import org.neo4j.driver.internal.bolt.api.MetricsListener; import org.neo4j.driver.internal.bolt.api.NotificationConfig; import org.neo4j.driver.internal.bolt.api.RoutingContext; import org.neo4j.driver.internal.bolt.api.SecurityPlan; +import org.neo4j.driver.internal.bolt.api.exception.BoltTransientException; import org.neo4j.driver.internal.bolt.api.exception.MinVersionAcquisitionException; import org.neo4j.driver.internal.bolt.pooledimpl.util.FutureUtil; @@ -245,14 +244,8 @@ public void connect( acquisitionTimeout, TimeUnit.MILLISECONDS); } else { - var message = "Connection pool pending acquisition queue is full."; - pendingAcquisitionsFull = new TransientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); + pendingAcquisitionsFull = + new BoltTransientException("Connection pool pending acquisition queue is full."); } } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/AuthTokenManagerExecutionException.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/AuthTokenManagerExecutionException.java new file mode 100644 index 0000000000..22d38c9a4b --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/AuthTokenManagerExecutionException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.bolt.routedimpl; + +import java.io.Serial; + +public class AuthTokenManagerExecutionException extends RuntimeException { + @Serial + private static final long serialVersionUID = -7415186795248189131L; + + public AuthTokenManagerExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnection.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnection.java index 044761a1d2..6e5f7df79e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnection.java @@ -24,10 +24,6 @@ import java.util.Set; import java.util.concurrent.CompletionStage; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.SessionExpiredException; -import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.AuthData; import org.neo4j.driver.internal.bolt.api.BoltConnection; @@ -35,11 +31,12 @@ import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.NotificationConfig; import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.TransactionType; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; @@ -307,62 +304,44 @@ public boolean telemetrySupported() { private Throwable handledError(Throwable receivedError, boolean notifyHandler) { var error = FutureUtil.completionExceptionCause(receivedError); - if (error instanceof ServiceUnavailableException exception) { - return handledServiceUnavailableException(exception, notifyHandler); - } else if (error instanceof ClientException exception) { - return handledClientException(exception, notifyHandler); - } else if (error instanceof TransientException exception) { - return handledTransientException(exception, notifyHandler); + if (error instanceof BoltServiceUnavailableException boltServiceUnavailableException) { + return handledServiceUnavailableException(boltServiceUnavailableException, notifyHandler); + } else if (error instanceof BoltFailureException boltFailureException) { + return handledBoltFailureException(boltFailureException, notifyHandler); } else { return error; } } - private Throwable handledServiceUnavailableException(ServiceUnavailableException e, boolean notifyHandler) { + private Throwable handledServiceUnavailableException(BoltServiceUnavailableException e, boolean notifyHandler) { if (notifyHandler) { routingTableHandler.onConnectionFailure(serverAddress()); } - return new SessionExpiredException(format("Server at %s is no longer available", serverAddress()), e); + return new BoltServiceUnavailableException(format("Server at %s is no longer available", serverAddress()), e); } - private Throwable handledTransientException(TransientException e, boolean notifyHandler) { + private Throwable handledBoltFailureException(BoltFailureException e, boolean notifyHandler) { var errorCode = e.code(); - if (Objects.equals(errorCode, "Neo.TransientError.General.DatabaseUnavailable") && notifyHandler) { - routingTableHandler.onConnectionFailure(serverAddress()); - } - return e; - } - - private Throwable handledClientException(ClientException e, boolean notifyHandler) { - if (isFailureToWrite(e)) { + if (Objects.equals(errorCode, "Neo.TransientError.General.DatabaseUnavailable")) { + if (notifyHandler) { + routingTableHandler.onConnectionFailure(serverAddress()); + } + } else if (isFailureToWrite(errorCode)) { // The server is unaware of the session mode, so we have to implement this logic in the driver. // In the future, we might be able to move this logic to the server. switch (accessMode) { - case READ -> { - var message = "Write queries cannot be performed in READ access mode."; - return new ClientException( - GqlStatusError.UNKNOWN.getStatus(), - GqlStatusError.UNKNOWN.getStatusDescription(message), - "N/A", - message, - GqlStatusError.DIAGNOSTIC_RECORD, - null); - } + case READ -> {} case WRITE -> { if (notifyHandler) { routingTableHandler.onWriteFailure(serverAddress()); } - return new SessionExpiredException( - format("Server at %s no longer accepts writes", serverAddress())); } - default -> throw new IllegalArgumentException(serverAddress() + " not supported."); } } return e; } - private static boolean isFailureToWrite(ClientException e) { - var errorCode = e.code(); + private static boolean isFailureToWrite(String errorCode) { return Objects.equals(errorCode, "Neo.ClientError.Cluster.NotALeader") || Objects.equals(errorCode, "Neo.ClientError.General.ForbiddenOnReadOnlyDatabase"); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnectionProvider.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnectionProvider.java index 9820b04e36..cfc54bfa35 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnectionProvider.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/RoutedBoltConnectionProvider.java @@ -27,15 +27,14 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import javax.net.ssl.SSLHandshakeException; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.BoltAgent; import org.neo4j.driver.internal.bolt.api.BoltConnection; @@ -50,6 +49,9 @@ import org.neo4j.driver.internal.bolt.api.NotificationConfig; import org.neo4j.driver.internal.bolt.api.RoutingContext; import org.neo4j.driver.internal.bolt.api.SecurityPlan; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionAcquisitionException; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.routedimpl.cluster.Rediscovery; import org.neo4j.driver.internal.bolt.routedimpl.cluster.RediscoveryImpl; import org.neo4j.driver.internal.bolt.routedimpl.cluster.RoutingTable; @@ -150,6 +152,11 @@ public CompletionStage connect( registry = this.registry; } + Supplier>> supplier = + () -> authMapStageSupplier.get().exceptionally(throwable -> { + throw new AuthTokenManagerExecutionException(throwable); + }); + var handlerRef = new AtomicReference(); var databaseNameFuture = databaseName == null ? new CompletableFuture() @@ -160,13 +167,7 @@ public CompletionStage connect( } }); return registry.ensureRoutingTable( - securityPlan, - databaseNameFuture, - mode, - bookmarks, - impersonatedUser, - authMapStageSupplier, - minVersion) + securityPlan, databaseNameFuture, mode, bookmarks, impersonatedUser, supplier, minVersion) .thenApply(routingTableHandler -> { handlerRef.set(routingTableHandler); return routingTableHandler; @@ -175,13 +176,25 @@ public CompletionStage connect( securityPlan, mode, routingTableHandler.routingTable(), - authMapStageSupplier, + supplier, routingTableHandler.routingTable().database(), Set.of(), impersonatedUser, minVersion, notificationConfig)) - .thenApply(boltConnection -> new RoutedBoltConnection(boltConnection, handlerRef.get(), mode, this)); + .thenApply(boltConnection -> + (BoltConnection) new RoutedBoltConnection(boltConnection, handlerRef.get(), mode, this)) + .exceptionally(throwable -> { + throwable = FutureUtil.completionExceptionCause(throwable); + if (throwable instanceof AuthTokenManagerExecutionException) { + throwable = throwable.getCause(); + } + if (throwable instanceof RuntimeException runtimeException) { + throw runtimeException; + } else { + throw new CompletionException(throwable); + } + }); } @Override @@ -204,8 +217,8 @@ public CompletionStage verifyConnectivity(SecurityPlan securityPlan, Map { if (error != null) { var cause = FutureUtil.completionExceptionCause(error); - if (cause instanceof ServiceUnavailableException) { - throw FutureUtil.asCompletionException(new ServiceUnavailableException( + if (cause instanceof BoltServiceUnavailableException) { + throw FutureUtil.asCompletionException(new BoltServiceUnavailableException( "Unable to connect to database management service, ensure the database is running and that there is a working network connection to it.", cause)); } @@ -262,13 +275,20 @@ private CompletionStage detectFeature( return CompletableFuture.failedFuture(error); } CompletableFuture result = CompletableFuture.completedFuture(null); - Throwable baseError = new ServiceUnavailableException(baseErrorMessagePrefix + addresses); + Throwable baseError = new BoltServiceUnavailableException(baseErrorMessagePrefix + addresses); + + Function isSecurityException = + boltFailureException -> boltFailureException.code().startsWith("Neo.ClientError.Security."); for (var address : addresses) { result = FutureUtil.onErrorContinue(result, baseError, completionError -> { // We fail fast on security errors var error = FutureUtil.completionExceptionCause(completionError); - if (error instanceof SecurityException) { + if (error instanceof BoltFailureException boltFailureException) { + if (isSecurityException.apply(boltFailureException)) { + return CompletableFuture.failedFuture(error); + } + } else if (error instanceof SSLHandshakeException) { return CompletableFuture.failedFuture(error); } return get(address) @@ -292,7 +312,11 @@ private CompletionStage detectFeature( // If we failed with security errors, then we rethrow the security error out, otherwise we throw the chained // errors. var error = FutureUtil.completionExceptionCause(completionError); - if (error instanceof SecurityException) { + if (error instanceof BoltFailureException boltFailureException) { + if (isSecurityException.apply(boltFailureException)) { + return CompletableFuture.failedFuture(error); + } + } else if (error instanceof SSLHandshakeException) { return CompletableFuture.failedFuture(error); } return CompletableFuture.failedFuture(baseError); @@ -344,7 +368,7 @@ private void acquire( log.log(System.Logger.Level.DEBUG, "Selected address: " + address); if (address == null) { - var completionError = new SessionExpiredException( + var completionError = new BoltConnectionAcquisitionException( format(CONNECTION_ACQUISITION_COMPLETION_EXCEPTION_MESSAGE, mode, routingTable)); attemptErrors.forEach(completionError::addSuppressed); log.log(System.Logger.Level.ERROR, CONNECTION_ACQUISITION_COMPLETION_FAILURE_MESSAGE, completionError); @@ -366,7 +390,7 @@ private void acquire( .whenComplete((connection, completionError) -> { var error = FutureUtil.completionExceptionCause(completionError); if (error != null) { - if (error instanceof ServiceUnavailableException) { + if (error instanceof BoltServiceUnavailableException) { var attemptMessage = format(CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE, address); log.log(System.Logger.Level.WARNING, attemptMessage); log.log(System.Logger.Level.DEBUG, attemptMessage, error); diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryImpl.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryImpl.java index c11c130ed2..e4fb1c07ef 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryImpl.java @@ -34,16 +34,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; +import javax.net.ssl.SSLHandshakeException; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.AuthTokenManagerExecutionException; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.DiscoveryException; -import org.neo4j.driver.exceptions.FatalDiscoveryException; -import org.neo4j.driver.exceptions.ProtocolException; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.UnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; @@ -54,8 +46,14 @@ import org.neo4j.driver.internal.bolt.api.LoggingProvider; import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.SecurityPlan; +import org.neo4j.driver.internal.bolt.api.exception.BoltDiscoveryException; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltProtocolException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; import org.neo4j.driver.internal.bolt.api.exception.MinVersionAcquisitionException; import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; +import org.neo4j.driver.internal.bolt.routedimpl.AuthTokenManagerExecutionException; import org.neo4j.driver.internal.bolt.routedimpl.util.FutureUtil; public class RediscoveryImpl implements Rediscovery { @@ -100,7 +98,7 @@ public CompletionStage lookupClusterComposition( BoltProtocolVersion minVersion) { var result = new CompletableFuture(); // if we failed discovery, we will chain all errors into this one. - var baseError = new ServiceUnavailableException( + var baseError = new BoltServiceUnavailableException( String.format(NO_ROUTERS_AVAILABLE, routingTable.database().description())); lookupClusterComposition( securityPlan, @@ -401,7 +399,7 @@ public void onComplete() { if (clusterComposition.routers().isEmpty() || clusterComposition.readers().isEmpty()) { throw new CompletionException( - new ProtocolException( + new BoltProtocolException( "Failed to parse result received from server due to no router or reader found in response.")); } else { return clusterComposition; @@ -441,7 +439,7 @@ private ClusterComposition handleRoutingProcedureError( } // Retryable error happened during discovery. - var discoveryError = new DiscoveryException(format(RECOVERABLE_ROUTING_ERROR, routerAddress), error); + var discoveryError = new BoltDiscoveryException(format(RECOVERABLE_ROUTING_ERROR, routerAddress), error); FutureUtil.combineErrors(baseError, discoveryError); // we record each failure here log.log(System.Logger.Level.WARNING, RECOVERABLE_DISCOVERY_ERROR_WITH_SERVER, routerAddress); log.log( @@ -452,36 +450,64 @@ private ClusterComposition handleRoutingProcedureError( return null; } + @SuppressWarnings("SwitchStatementWithTooFewBranches") private boolean mustAbortDiscovery(Throwable throwable) { var abort = false; - if (!(throwable instanceof AuthorizationExpiredException) && throwable instanceof SecurityException) { - abort = true; - } else if (throwable instanceof FatalDiscoveryException) { - abort = true; + if (throwable instanceof BoltFailureException boltFailureException) { + var code = boltFailureException.code(); + abort = switch (extractErrorClass(code)) { + case "ClientError" -> { + if ("Security".equals(extractErrorSubClass(code))) { + yield !"Neo.ClientError.Security.AuthorizationExpired".equalsIgnoreCase(code); + } else { + if ("Neo.ClientError.Database.DatabaseNotFound".equalsIgnoreCase(code)) { + yield true; + } else { + yield switch (code) { + case TRANSACTION_INVALID_BOOKMARK_CODE, + TRANSACTION_INVALID_BOOKMARK_MIXTURE_CODE, + STATEMENT_ARGUMENT_ERROR_CODE, + REQUEST_INVALID_CODE, + STATEMENT_TYPE_ERROR_CODE -> true; + default -> false; + }; + } + } + } + default -> false;}; } else if (throwable instanceof IllegalStateException && "Connection provider is closed.".equals(throwable.getMessage())) { abort = true; - } else if (throwable instanceof AuthTokenManagerExecutionException) { - abort = true; - } else if (throwable instanceof UnsupportedFeatureException) { + } else if (throwable instanceof BoltUnsupportedFeatureException) { abort = true; - } else if (throwable instanceof ClientException) { - var code = ((ClientException) throwable).code(); - abort = switch (code) { - case TRANSACTION_INVALID_BOOKMARK_CODE, - TRANSACTION_INVALID_BOOKMARK_MIXTURE_CODE, - STATEMENT_ARGUMENT_ERROR_CODE, - REQUEST_INVALID_CODE, - STATEMENT_TYPE_ERROR_CODE -> true; - default -> false;}; } else if (throwable instanceof MinVersionAcquisitionException) { abort = true; + } else if (throwable instanceof SSLHandshakeException) { + abort = true; + } else if (throwable instanceof AuthTokenManagerExecutionException) { + abort = true; } return abort; } + private static String extractErrorClass(String code) { + var parts = code.split("\\."); + if (parts.length < 2) { + return ""; + } + return parts[1]; + } + + private static String extractErrorSubClass(String code) { + var parts = code.split("\\."); + if (parts.length < 3) { + return ""; + } + return parts[2]; + } + @Override public List resolve() throws UnknownHostException { List resolvedAddresses = new LinkedList<>(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/util/FutureUtil.java b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/util/FutureUtil.java index d44fcd9f5e..0bd9cfde53 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/util/FutureUtil.java +++ b/driver/src/main/java/org/neo4j/driver/internal/bolt/routedimpl/util/FutureUtil.java @@ -17,7 +17,6 @@ package org.neo4j.driver.internal.bolt.routedimpl.util; import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.neo4j.driver.internal.util.ErrorUtil.addSuppressed; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -80,6 +79,12 @@ public static CompletionException combineErrors(Throwable error1, Throwable erro } } + public static void addSuppressed(Throwable mainError, Throwable error) { + if (mainError != error) { + mainError.addSuppressed(error); + } + } + public static T joinNowOrElseThrow( CompletableFuture future, Supplier exceptionSupplier) { if (future.isDone()) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/cursor/ResultCursorImpl.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/ResultCursorImpl.java index a4eb94af71..d9a84b4c5c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cursor/ResultCursorImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/ResultCursorImpl.java @@ -39,11 +39,11 @@ import org.neo4j.driver.internal.DatabaseBookmark; import org.neo4j.driver.internal.FailableCursor; import org.neo4j.driver.internal.InternalRecord; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.async.UnmanagedTransaction; -import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.GqlStatusError; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; @@ -55,7 +55,7 @@ import org.neo4j.driver.summary.ResultSummary; public class ResultCursorImpl extends AbstractRecordStateResponseHandler - implements ResultCursor, FailableCursor, ResponseHandler { + implements ResultCursor, FailableCursor, DriverResponseHandler { public static final MetadataExtractor METADATA_EXTRACTOR = new MetadataExtractor("t_last"); private static final ClientException IGNORED_ERROR = new ClientException( GqlStatusError.UNKNOWN.getStatus(), @@ -64,7 +64,7 @@ public class ResultCursorImpl extends AbstractRecordStateResponseHandler "A message has been ignored during result streaming.", GqlStatusError.DIAGNOSTIC_RECORD, null); - private final BoltConnection boltConnection; + private final DriverBoltConnection boltConnection; private final Queue records = new ArrayDeque<>(); private final Query query; private final long fetchSize; @@ -98,7 +98,7 @@ private enum State { } public ResultCursorImpl( - BoltConnection boltConnection, + DriverBoltConnection boltConnection, Query query, long fetchSize, Consumer bookmarkConsumer, diff --git a/driver/src/main/java/org/neo4j/driver/internal/cursor/RxResultCursorImpl.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/RxResultCursorImpl.java index 9061562bb4..173dd72917 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cursor/RxResultCursorImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/RxResultCursorImpl.java @@ -39,10 +39,10 @@ import org.neo4j.driver.exceptions.TransactionNestingException; import org.neo4j.driver.internal.DatabaseBookmark; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.GqlStatusError; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.DiscardSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; @@ -51,7 +51,8 @@ import org.neo4j.driver.summary.GqlStatusObject; import org.neo4j.driver.summary.ResultSummary; -public class RxResultCursorImpl extends AbstractRecordStateResponseHandler implements RxResultCursor, ResponseHandler { +public class RxResultCursorImpl extends AbstractRecordStateResponseHandler + implements RxResultCursor, DriverResponseHandler { private static final MetadataExtractor METADATA_EXTRACTOR = new MetadataExtractor("t_last"); private static final ClientException IGNORED_ERROR = new ClientException( GqlStatusError.UNKNOWN.getStatus(), @@ -79,7 +80,7 @@ public long resultAvailableAfter() { } }; private final Logger log; - private final BoltConnection boltConnection; + private final DriverBoltConnection boltConnection; private final Query query; private final RunSummary runSummary; private final Throwable runError; @@ -108,7 +109,7 @@ private enum State { } public RxResultCursorImpl( - BoltConnection boltConnection, + DriverBoltConnection boltConnection, Query query, RunSummary runSummary, Throwable runError, @@ -259,7 +260,7 @@ public CompletionStage rollback() { var resetFuture = new CompletableFuture(); boltConnection .reset() - .thenCompose(conn -> conn.flush(new ResponseHandler() { + .thenCompose(conn -> conn.flush(new DriverResponseHandler() { Throwable throwable = null; @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java index befa939b4e..69602be051 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java +++ b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java @@ -46,7 +46,7 @@ public class ExponentialBackoffRetryLogic implements RetryLogic { public static final long DEFAULT_MAX_RETRY_TIME_MS = SECONDS.toMillis(30); private static final long INITIAL_RETRY_DELAY_MS = SECONDS.toMillis(1); - private static final double RETRY_DELAY_MULTIPLIER = 2.0; + private static final double RETRY_DELAY_MULTIPLIER = 1; private static final double RETRY_DELAY_JITTER_FACTOR = 0.2; private static final long MAX_RETRY_DELAY = Long.MAX_VALUE / 2; diff --git a/driver/src/main/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWork.java b/driver/src/main/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWork.java index cc7fe6fb1e..a170e9f2a1 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWork.java +++ b/driver/src/main/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWork.java @@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.TelemetryApi; public record ApiTelemetryWork(TelemetryApi telemetryApi, AtomicBoolean enabled, AtomicBoolean acknowledged) { @@ -36,7 +36,7 @@ public void acknowledge() { this.acknowledged.set(true); } - public CompletionStage pipelineTelemetryIfEnabled(BoltConnection connection) { + public CompletionStage pipelineTelemetryIfEnabled(DriverBoltConnection connection) { if (enabled.get() && connection.telemetrySupported() && !(acknowledged.get())) { return connection.telemetry(telemetryApi); } else { diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java b/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java index e403dd413c..da9ec01552 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java +++ b/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java @@ -19,19 +19,9 @@ import java.io.Serial; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; -import org.neo4j.driver.exceptions.AuthenticationException; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.DatabaseException; -import org.neo4j.driver.exceptions.FatalDiscoveryException; import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.exceptions.ResultConsumedException; -import org.neo4j.driver.exceptions.SecurityException; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.TokenExpiredException; -import org.neo4j.driver.exceptions.TransactionTerminatedException; -import org.neo4j.driver.exceptions.TransientException; -import org.neo4j.driver.internal.bolt.api.GqlError; import org.neo4j.driver.internal.bolt.api.GqlStatusError; public final class ErrorUtil { @@ -56,138 +46,6 @@ public static ResultConsumedException newResultConsumedError() { + "or the query runner where the result is created has already been closed."); } - public static Neo4jException newNeo4jError(GqlError gqlError) { - var code = gqlError.code(); - switch (extractErrorClass(code)) { - case "ClientError" -> { - if ("Security".equals(extractErrorSubClass(code))) { - if (code.equalsIgnoreCase("Neo.ClientError.Security.Unauthorized")) { - return new AuthenticationException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else if (code.equalsIgnoreCase("Neo.ClientError.Security.AuthorizationExpired")) { - return new AuthorizationExpiredException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else if (code.equalsIgnoreCase("Neo.ClientError.Security.TokenExpired")) { - return new TokenExpiredException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else { - return new SecurityException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } - } else { - if (code.equalsIgnoreCase("Neo.ClientError.Database.DatabaseNotFound")) { - return new FatalDiscoveryException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else if (code.equalsIgnoreCase("Neo.ClientError.Transaction.Terminated")) { - return new TransactionTerminatedException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else { - return new ClientException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } - } - } - case "TransientError" -> { - // Since 5.0 these 2 errors have been moved to ClientError class. - // This mapping is required if driver is connection to earlier server versions. - if ("Neo.TransientError.Transaction.Terminated".equals(code)) { - return new TransactionTerminatedException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - "Neo.ClientError.Transaction.Terminated", - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else if ("Neo.TransientError.Transaction.LockClientStopped".equals(code)) { - return new ClientException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - "Neo.ClientError.Transaction.LockClientStopped", - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } else { - return new TransientException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } - } - default -> { - return new DatabaseException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - code, - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } - } - } - - public static Neo4jException map(GqlError gqlError) { - if (gqlError == null) { - return null; - } else { - return new Neo4jException( - gqlError.gqlStatus(), - gqlError.statusDescription(), - gqlError.code(), - gqlError.message(), - gqlError.diagnosticRecord(), - map(gqlError.cause())); - } - } - - public static boolean isFatal(Throwable error) { - if (error instanceof Neo4jException) { - if (isProtocolViolationError(((Neo4jException) error))) { - return true; - } - - return !isClientOrTransientError(((Neo4jException) error)); - } - return true; - } - public static void rethrowAsyncException(ExecutionException e) { var error = e.getCause(); @@ -215,32 +73,6 @@ public static void rethrowAsyncException(ExecutionException e) { throw exception; } - private static boolean isProtocolViolationError(Neo4jException error) { - var errorCode = error.code(); - return errorCode != null && errorCode.startsWith("Neo.ClientError.Request"); - } - - private static boolean isClientOrTransientError(Neo4jException error) { - var errorCode = error.code(); - return errorCode != null && (errorCode.contains("ClientError") || errorCode.contains("TransientError")); - } - - private static String extractErrorClass(String code) { - var parts = code.split("\\."); - if (parts.length < 2) { - return ""; - } - return parts[1]; - } - - private static String extractErrorSubClass(String code) { - var parts = code.split("\\."); - if (parts.length < 3) { - return ""; - } - return parts[2]; - } - public static void addSuppressed(Throwable mainError, Throwable error) { if (mainError != error) { mainError.addSuppressed(error); 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 fb3bc47d94..18fb7fecec 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 @@ -43,7 +43,7 @@ import org.neo4j.driver.Values; import org.neo4j.driver.exceptions.ProtocolException; import org.neo4j.driver.internal.InternalNotificationSeverity; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.summary.InternalDatabaseInfo; import org.neo4j.driver.internal.summary.InternalGqlStatusObject; import org.neo4j.driver.internal.summary.InternalInputPosition; @@ -93,7 +93,7 @@ public MetadataExtractor(String resultConsumedAfterMetadataKey) { public ResultSummary extractSummary( Query query, - BoltConnection connection, + DriverBoltConnection connection, long resultAvailableAfter, Map metadata, boolean legacyNotifications, diff --git a/driver/src/test/java/org/neo4j/driver/PackageTests.java b/driver/src/test/java/org/neo4j/driver/PackageTests.java index 8dac42be89..ac6fb5b8cd 100644 --- a/driver/src/test/java/org/neo4j/driver/PackageTests.java +++ b/driver/src/test/java/org/neo4j/driver/PackageTests.java @@ -30,8 +30,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.UntrustedServerException; import org.neo4j.driver.internal.DriverFactory; import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; @@ -47,7 +45,6 @@ import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic; import org.neo4j.driver.internal.types.InternalTypeSystem; import org.neo4j.driver.internal.types.TypeConstructor; -import org.neo4j.driver.internal.util.ErrorUtil; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.types.IsoDuration; import org.neo4j.driver.types.MapAccessor; @@ -103,11 +100,7 @@ void boltLayerShouldBeSelfContained() { InternalPath.class, InternalPath.SelfContainedSegment.class, IsoDuration.class, - InternalTypeSystem.class, - // exceptions - Neo4jException.class, - ErrorUtil.class, - UntrustedServerException.class) + InternalTypeSystem.class) .map(JavaClass.Predicates::assignableTo) .reduce((one, two) -> DescribedPredicate.or(one, two)) .get(); diff --git a/driver/src/test/java/org/neo4j/driver/ParametersTest.java b/driver/src/test/java/org/neo4j/driver/ParametersTest.java index 3cd13873b4..e4eedf1da8 100644 --- a/driver/src/test/java/org/neo4j/driver/ParametersTest.java +++ b/driver/src/test/java/org/neo4j/driver/ParametersTest.java @@ -39,8 +39,8 @@ import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.InternalRecord; import org.neo4j.driver.internal.InternalSession; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.security.BoltSecurityPlanManager; @@ -100,7 +100,7 @@ void shouldNotBePossibleToUseInvalidParametersViaRecord(Object obj, String expec } private Session mockedSession() { - var provider = mock(BoltConnectionProvider.class); + var provider = mock(DriverBoltConnectionProvider.class); var retryLogic = mock(RetryLogic.class); var session = new NetworkSession( BoltSecurityPlanManager.insecure(), diff --git a/driver/src/test/java/org/neo4j/driver/integration/ParametersIT.java b/driver/src/test/java/org/neo4j/driver/integration/ParametersIT.java index 43cebee5c0..c01f149d0b 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ParametersIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ParametersIT.java @@ -414,7 +414,7 @@ private static byte[] randomByteArray(int length) { private static void expectIOExceptionWithMessage(Value value, String message) { var e = assertThrows(ServiceUnavailableException.class, () -> session.run("RETURN {a}", value) .consume()); - var cause = e.getCause(); + var cause = e.getCause().getCause(); assertThat(cause, instanceOf(IOException.class)); assertThat(cause.getMessage(), equalTo(message)); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java index 63ed0c1361..e4a94d0f18 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java @@ -42,9 +42,9 @@ import org.neo4j.driver.Logging; import org.neo4j.driver.MetricsAdapter; import org.neo4j.driver.SessionConfig; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.BootstrapFactory; import org.neo4j.driver.internal.metrics.DevNullMetricsProvider; import org.neo4j.driver.internal.metrics.InternalMetricsProvider; @@ -154,7 +154,7 @@ private static class SessionFactoryCapturingDriverFactory extends DriverFactory @Override protected SessionFactory createSessionFactory( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, Config config, AuthTokenManager authTokenManager) { @@ -180,7 +180,7 @@ protected Bootstrap createBootstrap(int ignored) { @Override protected SessionFactory createSessionFactory( BoltSecurityPlanManager securityPlanManager, - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, RetryLogic retryLogic, Config config, AuthTokenManager authTokenManager) { 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 d379b41e2b..42800e436e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java @@ -48,7 +48,7 @@ import org.neo4j.driver.async.ResultCursor; import org.neo4j.driver.exceptions.NoSuchRecordException; import org.neo4j.driver.exceptions.ResultConsumedException; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; @@ -340,7 +340,7 @@ void shouldDelegateIsOpen(boolean expectedState) { private Result createResult(int numberOfRecords) { var query = new Query(""); - var connection = mock(BoltConnection.class); + var connection = mock(DriverBoltConnection.class); when(connection.serverAddress()).thenReturn(BoltServerAddress.LOCAL_DEFAULT); when(connection.protocolVersion()).thenReturn(new BoltProtocolVersion(4, 3)); when(connection.serverAgent()).thenReturn("Neo4j/4.2.5"); diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java index 93ed98b4dc..fc9cc32ceb 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java @@ -50,31 +50,31 @@ import org.neo4j.driver.Result; import org.neo4j.driver.Transaction; import org.neo4j.driver.Value; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.RollbackSummary; import org.neo4j.driver.internal.value.IntegerValue; class InternalTransactionTest { - private BoltConnection connection; + private DriverBoltConnection connection; private Transaction tx; @BeforeEach @SuppressWarnings("resource") void setUp() { connection = connectionMock(new BoltProtocolVersion(4, 0)); - var connectionProvider = mock(BoltConnectionProvider.class); + var connectionProvider = mock(DriverBoltConnectionProvider.class); given(connection.onLoop()).willReturn(CompletableFuture.completedStage(connection)); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(CompletableFuture.completedFuture(connection)); given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); if (handler != null) { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -109,7 +109,7 @@ void shouldFlushOnRun(Function runReturnOne) { void shouldCommit() { given(connection.commit()).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onCommitSummary(mock(CommitSummary.class)); handler.onComplete(); return CompletableFuture.completedStage(null); @@ -127,7 +127,7 @@ void shouldCommit() { void shouldRollbackByDefault() { given(connection.rollback()).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onRollbackSummary(mock(RollbackSummary.class)); handler.onComplete(); return CompletableFuture.completedStage(null); @@ -144,7 +144,7 @@ void shouldRollbackByDefault() { void shouldRollback() { given(connection.rollback()).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onRollbackSummary(mock(RollbackSummary.class)); handler.onComplete(); return CompletableFuture.completedStage(null); diff --git a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java index b4034e4ff1..edae8e68af 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java @@ -26,9 +26,9 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.AuthTokenManager; import org.neo4j.driver.Config; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.security.BoltSecurityPlanManager; import org.neo4j.driver.internal.util.FixedRetryLogic; @@ -79,7 +79,7 @@ void createsLeakLoggingNetworkSessions() { private static SessionFactory newSessionFactory(Config config) { return new SessionFactoryImpl( BoltSecurityPlanManager.insecure(), - mock(BoltConnectionProvider.class), + mock(DriverBoltConnectionProvider.class), new FixedRetryLogic(0), config, mock(AuthTokenManager.class)); diff --git a/driver/src/test/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapperTest.java b/driver/src/test/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapperTest.java new file mode 100644 index 0000000000..79c87a732f --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/adaptedbolt/ErrorMapperTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * 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.adaptedbolt; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import javax.net.ssl.SSLHandshakeException; +import org.junit.jupiter.api.Test; +import org.neo4j.driver.exceptions.AuthenticationException; +import org.neo4j.driver.exceptions.AuthorizationExpiredException; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.exceptions.DatabaseException; +import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.exceptions.SecurityException; +import org.neo4j.driver.exceptions.TokenExpiredException; +import org.neo4j.driver.exceptions.TransientException; +import org.neo4j.driver.internal.bolt.api.GqlStatusError; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; + +class ErrorMapperTest { + private BoltFailureException newError(String code, String message) { + return new BoltFailureException( + code, + message, + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(message), + GqlStatusError.DIAGNOSTIC_RECORD, + null); + } + + @Test + void shouldCreateAuthenticationException() { + var code = "Neo.ClientError.Security.Unauthorized"; + var message = "Wrong credentials"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(AuthenticationException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateClientException() { + var code = "Neo.ClientError.Transaction.InvalidBookmark"; + var message = "Wrong bookmark"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(ClientException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateTransientException() { + var code = "Neo.TransientError.Transaction.DeadlockDetected"; + var message = "Deadlock occurred"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(TransientException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateDatabaseException() { + var code = "Neo.DatabaseError.Transaction.TransactionLogError"; + var message = "Failed to write the transaction log"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(DatabaseException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateDatabaseExceptionWhenErrorCodeIsWrong() { + var code = "WrongErrorCode"; + var message = "Some really strange error"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(DatabaseException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateAuthorizationExpiredException() { + var code = "Neo.ClientError.Security.AuthorizationExpired"; + var message = "Expired authorization info"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(AuthorizationExpiredException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldCreateTokenExpiredException() { + var code = "Neo.ClientError.Security.TokenExpired"; + var message = "message"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(TokenExpiredException.class)); + assertEquals(code, error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldMapTransientTransactionTerminatedToClientException() { + var code = "Neo.TransientError.Transaction.Terminated"; + var message = "message"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(ClientException.class)); + assertEquals("Neo.ClientError.Transaction.Terminated", error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldMapTransientTransactionLockClientStoppedToClientException() { + var code = "Neo.TransientError.Transaction.LockClientStopped"; + var message = "message"; + + var error = (Neo4jException) ErrorMapper.getInstance().map(newError(code, message)); + + assertThat(error, instanceOf(ClientException.class)); + assertEquals("Neo.ClientError.Transaction.LockClientStopped", error.code()); + assertEquals(message, error.getMessage()); + } + + @Test + void shouldTranslateSSLHandshakeException() { + var sslHandshakeException = new SSLHandshakeException("Invalid certificate"); + + var error = (Neo4jException) ErrorMapper.getInstance().map(sslHandshakeException); + + assertInstanceOf(SecurityException.class, error); + assertEquals(sslHandshakeException, error.getCause()); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java index 04fa888e79..6674c5bd44 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java @@ -77,11 +77,11 @@ import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.RollbackSummary; import org.neo4j.driver.internal.retry.RetryLogic; @@ -89,8 +89,8 @@ import org.neo4j.driver.internal.value.IntegerValue; class InternalAsyncSessionTest { - private BoltConnection connection; - private BoltConnectionProvider connectionProvider; + private DriverBoltConnection connection; + private DriverBoltConnectionProvider connectionProvider; private AsyncSession asyncSession; private NetworkSession session; @@ -99,9 +99,9 @@ void setUp() { connection = connectionMock(new BoltProtocolVersion(4, 0)); given(connection.onLoop()).willReturn(CompletableFuture.completedStage(connection)); given(connection.close()).willReturn(completedFuture(null)); - connectionProvider = mock(BoltConnectionProvider.class); + connectionProvider = mock(DriverBoltConnectionProvider.class); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var database = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseConsumer = (Consumer) invocation.getArguments()[8]; @@ -323,7 +323,7 @@ private void testTxIsRetriedUntilSuccessWhenFunctionThrows(AccessMode mode) { given(connection.commit()).willReturn(CompletableFuture.completedStage(connection)); var failures = 12; var failureHandlerStream = IntStream.range(0, failures) - .mapToObj(ignored -> Stream.>of( + .mapToObj(ignored -> Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -334,7 +334,7 @@ private void testTxIsRetriedUntilSuccessWhenFunctionThrows(AccessMode mode) { })) .flatMap(Function.identity()); var retries = failures + 1; - var successHandlers = Stream.>of( + var successHandlers = Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -365,7 +365,7 @@ private void testTxIsRetriedUntilSuccessWhenCommitThrows(AccessMode mode) { given(connection.commit()).willReturn(CompletableFuture.completedStage(connection)); var failures = 13; var failureHandlerStream = IntStream.range(0, failures) - .mapToObj(ignored -> Stream.>of( + .mapToObj(ignored -> Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -376,7 +376,7 @@ private void testTxIsRetriedUntilSuccessWhenCommitThrows(AccessMode mode) { })) .flatMap(Function.identity()); var retries = failures + 1; - var successHandlers = Stream.>of( + var successHandlers = Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -406,7 +406,7 @@ private void testTxIsRetriedUntilFailureWhenFunctionThrows(AccessMode mode) { given(connection.rollback()).willReturn(CompletableFuture.completedStage(connection)); var failures = 14; var failureHandlerStream = IntStream.range(0, failures) - .mapToObj(ignored -> Stream.>of( + .mapToObj(ignored -> Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); @@ -440,7 +440,7 @@ private void testTxIsRetriedUntilFailureWhenCommitFails(AccessMode mode) { given(connection.commit()).willReturn(CompletableFuture.completedStage(connection)); var failures = 17; var failureHandlerStream = IntStream.range(0, failures) - .mapToObj(ignored -> Stream.>of( + .mapToObj(ignored -> Stream.>of( handler -> { handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java index 90edd0867f..a4a08dc5f3 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java @@ -55,8 +55,8 @@ import org.neo4j.driver.async.ResultCursor; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.DatabaseName; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; @@ -67,16 +67,16 @@ import org.neo4j.driver.internal.value.IntegerValue; class InternalAsyncTransactionTest { - private BoltConnection connection; + private DriverBoltConnection connection; private InternalAsyncSession session; @BeforeEach void setUp() { connection = connectionMock(BoltProtocolV4.INSTANCE.version()); given(connection.onLoop()).willReturn(CompletableFuture.completedStage(connection)); - var connectionProvider = mock(BoltConnectionProvider.class); + var connectionProvider = mock(DriverBoltConnectionProvider.class); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var database = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseConsumer = (Consumer) invocation.getArguments()[8]; @@ -102,9 +102,9 @@ private static Stream>> void shouldFlushOnRun(Function> runReturnOne) { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.run(any(), any())).willAnswer((Answer>) + given(connection.run(any(), any())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); - given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) + given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); setupConnectionAnswers( connection, @@ -130,7 +130,7 @@ void shouldFlushOnRun(Function> void shouldCommit() { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.commit()).willAnswer((Answer>) + given(connection.commit()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); setupConnectionAnswers( connection, @@ -157,7 +157,7 @@ void shouldCommit() { void shouldRollback() { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.rollback()).willAnswer((Answer>) + given(connection.rollback()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); setupConnectionAnswers( connection, @@ -183,7 +183,7 @@ void shouldRollback() { void shouldReleaseConnectionWhenFailedToCommit() { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.commit()).willAnswer((Answer>) + given(connection.commit()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); setupConnectionAnswers( connection, @@ -208,7 +208,7 @@ void shouldReleaseConnectionWhenFailedToCommit() { void shouldReleaseConnectionWhenFailedToRollback() { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.rollback()).willAnswer((Answer>) + given(connection.rollback()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); setupConnectionAnswers( connection, diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java index 13adc087dc..eaa0954536 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java @@ -46,8 +46,8 @@ import org.neo4j.driver.NotificationConfig; import org.neo4j.driver.Query; import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; @@ -129,7 +129,7 @@ private static void finalize(NetworkSession session) throws Exception { finalizeMethod.invoke(session); } - private static LeakLoggingNetworkSession newSession(Logging logging, BoltConnection connection) { + private static LeakLoggingNetworkSession newSession(Logging logging, DriverBoltConnection connection) { return new LeakLoggingNetworkSession( BoltSecurityPlanManager.insecure(), connectionProviderMock(connection), @@ -148,8 +148,8 @@ private static LeakLoggingNetworkSession newSession(Logging logging, BoltConnect AuthTokenManagers.basic(AuthTokens::none)); } - private static BoltConnectionProvider connectionProviderMock(BoltConnection connection) { - var provider = mock(BoltConnectionProvider.class); + private static DriverBoltConnectionProvider connectionProviderMock(DriverBoltConnection connection) { + var provider = mock(DriverBoltConnectionProvider.class); when(provider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.completedFuture(connection)); return provider; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java index 57b5af52e9..2298caddfb 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java @@ -66,11 +66,11 @@ import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.InternalBookmark; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseName; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.TelemetryApi; import org.neo4j.driver.internal.bolt.api.summary.BeginSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; @@ -81,8 +81,8 @@ import org.neo4j.driver.internal.util.FixedRetryLogic; class NetworkSessionTest { - private BoltConnection connection; - private BoltConnectionProvider connectionProvider; + private DriverBoltConnection connection; + private DriverBoltConnectionProvider connectionProvider; private NetworkSession session; @BeforeEach @@ -90,9 +90,9 @@ void setUp() { connection = connectionMock(new BoltProtocolVersion(5, 4)); given(connection.onLoop()).willReturn(CompletableFuture.completedStage(connection)); given(connection.close()).willReturn(completedFuture(null)); - connectionProvider = mock(BoltConnectionProvider.class); + connectionProvider = mock(DriverBoltConnectionProvider.class); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var database = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseConsumer = (Consumer) invocation.getArguments()[8]; @@ -312,9 +312,9 @@ void releasesConnectionWhenTxIsClosed() { given(connection.onLoop()).willReturn(CompletableFuture.completedStage(connection)); given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); - given(connection.run(any(), any())).willAnswer((Answer>) + given(connection.run(any(), any())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); - given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) + given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.rollback()).willReturn(CompletableFuture.completedFuture(connection)); setupConnectionAnswers( @@ -461,7 +461,7 @@ void shouldRunAfterRunFailure() { Mockito.reset(connectionProvider); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(failedFuture(error)) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -495,7 +495,7 @@ void shouldRunAfterBeginTxFailureOnBookmark() { Mockito.reset(connectionProvider); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -503,7 +503,7 @@ void shouldRunAfterBeginTxFailureOnBookmark() { databaseNameConsumer.accept(databaseName); return completedFuture(connection1); }) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -545,7 +545,7 @@ void shouldBeginTxAfterBeginTxFailureOnBookmark() { Mockito.reset(connectionProvider); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -553,7 +553,7 @@ void shouldBeginTxAfterBeginTxFailureOnBookmark() { databaseNameConsumer.accept(databaseName); return completedFuture(connection1); }) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -581,7 +581,7 @@ void shouldBeginTxAfterRunFailureToAcquireConnection() { Mockito.reset(connectionProvider); given(connectionProvider.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(failedFuture(error)) - .willAnswer((Answer>) invocation -> { + .willAnswer((Answer>) invocation -> { var databaseName = (DatabaseName) invocation.getArguments()[1]; @SuppressWarnings("unchecked") var databaseNameConsumer = @@ -667,11 +667,11 @@ void shouldSendTelemetryIfEnabledOnRun(boolean telemetryDisabled) { } } - private void setupSuccessfulBegin(BoltConnection connection) { + private void setupSuccessfulBegin(DriverBoltConnection connection) { given(connection.beginTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) .willReturn(completedFuture(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArguments()[0]; + var handler = (DriverResponseHandler) invocation.getArguments()[0]; handler.onBeginSummary(mock(BeginSummary.class)); handler.onComplete(); return completedFuture(null); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java index 0f996e7f50..49cbb1ac5a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java @@ -64,8 +64,8 @@ import org.neo4j.driver.exceptions.TransactionTerminatedException; import org.neo4j.driver.internal.FailableCursor; import org.neo4j.driver.internal.InternalBookmark; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.AccessMode; -import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.DatabaseNameUtil; import org.neo4j.driver.internal.bolt.api.TelemetryApi; @@ -860,11 +860,11 @@ private record TransactionClosingTestParams( Function> runAction, String expectedMessage) {} - private static UnmanagedTransaction beginTx(BoltConnection connection) { + private static UnmanagedTransaction beginTx(DriverBoltConnection connection) { return beginTx(connection, Collections.emptySet()); } - private static UnmanagedTransaction beginTx(BoltConnection connection, Set initialBookmarks) { + private static UnmanagedTransaction beginTx(DriverBoltConnection connection, Set initialBookmarks) { var apiTelemetryWork = new ApiTelemetryWork(TelemetryApi.UNMANAGED_TRANSACTION); var tx = new UnmanagedTransaction( connection, diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListenerTest.java index f820097e9d..8bea85405d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelConnectedListenerTest.java @@ -38,8 +38,8 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; class ChannelConnectedListenerTest { private final EmbeddedChannel channel = new EmbeddedChannel(); @@ -60,7 +60,7 @@ void shouldFailPromiseWhenChannelConnectionFails() { listener.operationComplete(channelConnectedPromise); - var error = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var error = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(cause, error.getCause()); } @@ -91,7 +91,7 @@ void shouldCompleteHandshakePromiseExceptionallyOnWriteFailure() { assertTrue(handshakeCompletedFuture.isCompletedExceptionally()); var exception = assertThrows(CompletionException.class, handshakeCompletedFuture::join); - assertInstanceOf(ServiceUnavailableException.class, exception.getCause()); + assertInstanceOf(BoltServiceUnavailableException.class, exception.getCause()); } @Test @@ -104,7 +104,7 @@ void shouldCompleteFutureExceptionallyOnFailedPromise() { assertTrue(future.isCompletedExceptionally()); Throwable exception = assertThrows(CompletionException.class, future::join); - assertInstanceOf(ServiceUnavailableException.class, exception.getCause()); + assertInstanceOf(BoltServiceUnavailableException.class, exception.getCause()); exception = exception.getCause(); assertEquals(throwable, exception.getCause()); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelErrorHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelErrorHandlerTest.java index d34b7c9cf0..c95771d3d5 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelErrorHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/ChannelErrorHandlerTest.java @@ -34,8 +34,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.ChannelErrorHandler; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.InboundMessageDispatcher; @@ -61,12 +61,12 @@ void tearDown() { @Test void shouldHandleChannelInactive() { channel.pipeline().fireChannelInactive(); - var exceptionCaptor = ArgumentCaptor.forClass(ServiceUnavailableException.class); + var exceptionCaptor = ArgumentCaptor.forClass(BoltServiceUnavailableException.class); then(messageDispatcher).should().handleChannelInactive(exceptionCaptor.capture()); var error = exceptionCaptor.getValue(); - assertThat(error, instanceOf(ServiceUnavailableException.class)); + assertThat(error, instanceOf(BoltServiceUnavailableException.class)); assertThat(error.getMessage(), startsWith("Connection to the database terminated")); // assertFalse(channel.isOpen()); } @@ -85,7 +85,7 @@ void shouldHandleChannelInactiveAfterExceptionCaught() { var error2 = exception2.getValue(); assertEquals(originalError, error1); - assertThat(error2, instanceOf(ServiceUnavailableException.class)); + assertThat(error2, instanceOf(BoltServiceUnavailableException.class)); assertThat(error2.getMessage(), startsWith("Connection to the database terminated")); // assertFalse(channel.isOpen()); } @@ -97,10 +97,10 @@ void shouldHandleChannelInactiveWhenTerminationReasonSet() { channel.pipeline().fireChannelInactive(); - var exceptionCaptor = ArgumentCaptor.forClass(ServiceUnavailableException.class); + var exceptionCaptor = ArgumentCaptor.forClass(BoltServiceUnavailableException.class); then(messageDispatcher).should().handleChannelInactive(exceptionCaptor.capture()); var error = exceptionCaptor.getValue(); - assertThat(error, instanceOf(ServiceUnavailableException.class)); + assertThat(error, instanceOf(BoltServiceUnavailableException.class)); assertThat(error.getMessage(), startsWith("Connection to the database terminated")); assertThat(error.getMessage(), containsString(terminationReason)); // assertFalse(channel.isOpen()); @@ -142,7 +142,7 @@ void shouldHandleIOException() { var exceptionCaptor = ArgumentCaptor.forClass(RuntimeException.class); then(messageDispatcher).should().handleChannelError(exceptionCaptor.capture()); var error = exceptionCaptor.getValue(); - assertThat(error, instanceOf(ServiceUnavailableException.class)); + assertThat(error, instanceOf(BoltServiceUnavailableException.class)); assertEquals(ioException, error.getCause()); // assertFalse(channel.isOpen()); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandlerTest.java index 21505ea403..38e663a15d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/connection/HandshakeHandlerTest.java @@ -43,12 +43,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.LoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.ChunkDecoder; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.InboundMessageHandler; @@ -86,7 +85,7 @@ void shouldFailGivenPromiseWhenExceptionCaught() { channel.pipeline().fireExceptionCaught(cause); // promise should fail - var error = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var error = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(cause, error.getCause()); // channel should be closed @@ -99,11 +98,11 @@ void shouldFailGivenPromiseWhenServiceUnavailableExceptionCaught() { var handler = newHandler(handshakeCompletedFuture); channel.pipeline().addLast(handler); - var error = new ServiceUnavailableException("Bad error"); + var error = new BoltServiceUnavailableException("Bad error"); channel.pipeline().fireExceptionCaught(error); // promise should fail - var e = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var e = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(error, e); // channel should be closed @@ -122,7 +121,7 @@ void shouldFailGivenPromiseWhenMultipleExceptionsCaught() { channel.pipeline().fireExceptionCaught(error2); // promise should fail - var e1 = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var e1 = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(error1, e1.getCause()); // channel should be closed @@ -142,7 +141,7 @@ void shouldUnwrapDecoderException() { channel.pipeline().fireExceptionCaught(new DecoderException(cause)); // promise should fail - var error = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var error = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(cause, error.getCause()); // channel should be closed @@ -158,7 +157,7 @@ void shouldHandleDecoderExceptionWithoutCause() { var decoderException = new DecoderException("Unable to decode a message"); channel.pipeline().fireExceptionCaught(decoderException); - var error = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var error = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(decoderException, error.getCause()); // channel should be closed @@ -166,7 +165,7 @@ void shouldHandleDecoderExceptionWithoutCause() { } @Test - void shouldTranslateSSLHandshakeException() { + void shouldNotTranslateSSLHandshakeException() { var handshakeCompletedFuture = new CompletableFuture(); var handler = newHandler(handshakeCompletedFuture); channel.pipeline().addLast(handler); @@ -175,8 +174,8 @@ void shouldTranslateSSLHandshakeException() { channel.pipeline().fireExceptionCaught(error); // promise should fail - var e = assertThrows(SecurityException.class, () -> await(handshakeCompletedFuture)); - assertEquals(error, e.getCause()); + var e = assertThrows(SSLHandshakeException.class, () -> await(handshakeCompletedFuture)); + assertEquals(error, e); // channel should be closed assertNull(await(channel.closeFuture())); @@ -235,7 +234,7 @@ void shouldFailGivenPromiseWhenChannelInactive() { channel.pipeline().fireChannelInactive(); // promise should fail - var error = assertThrows(ServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); + var error = assertThrows(BoltServiceUnavailableException.class, () -> await(handshakeCompletedFuture)); assertEquals(ErrorUtil.newConnectionTerminatedError().getMessage(), error.getMessage()); // channel should be closed @@ -254,7 +253,7 @@ private void testFailure(BoltProtocolVersion serverSuggestedVersion, String expe // promise should fail var error = assertThrows(Exception.class, () -> await(handshakeCompletedFuture)); - assertThat(error, instanceOf(ClientException.class)); + assertThat(error, instanceOf(BoltClientException.class)); assertThat(error.getMessage(), startsWith(expectedMessagePrefix)); // channel should be closed diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandlerTest.java index c0aaf40bfc..44800eca52 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectTimeoutHandlerTest.java @@ -22,7 +22,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; class ConnectTimeoutHandlerTest { private final EmbeddedChannel channel = new EmbeddedChannel(); @@ -41,7 +41,7 @@ void shouldFireExceptionOnTimeout() throws Exception { Thread.sleep(timeoutMillis * 4); channel.runPendingTasks(); - var error = assertThrows(ServiceUnavailableException.class, channel::checkException); + var error = assertThrows(BoltServiceUnavailableException.class, channel::checkException); assertEquals(error.getMessage(), "Unable to establish connection in " + timeoutMillis + "ms"); } @@ -54,7 +54,7 @@ void shouldNotFireExceptionMultipleTimes() throws Exception { Thread.sleep(timeoutMillis * 4); channel.runPendingTasks(); - var error = assertThrows(ServiceUnavailableException.class, channel::checkException); + var error = assertThrows(BoltServiceUnavailableException.class, channel::checkException); assertEquals(error.getMessage(), "Unable to establish connection in " + timeoutMillis + "ms"); // sleep even more diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandlerTest.java index b345a31229..03478a6dd6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/ConnectionReadTimeoutHandlerTest.java @@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; -import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; +import org.neo4j.driver.internal.bolt.api.exception.BoltConnectionReadTimeoutException; public class ConnectionReadTimeoutHandlerTest { final ConnectionReadTimeoutHandler handler = new ConnectionReadTimeoutHandler(15L, TimeUnit.SECONDS); @@ -37,7 +37,7 @@ void shouldFireConnectionReadTimeoutExceptionAndCloseChannelOnReadTimeOutOnce() IntStream.range(0, 10).forEach(i -> handler.readTimedOut(context)); // THEN - then(context).should(times(1)).fireExceptionCaught(any(ConnectionReadTimeoutException.class)); + then(context).should(times(1)).fireExceptionCaught(any(BoltConnectionReadTimeoutException.class)); then(context).should(times(1)).close(); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcherTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcherTest.java index fc0841814f..61cecee0c9 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcherTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageDispatcherTest.java @@ -49,10 +49,10 @@ import org.mockito.ArgumentMatchers; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; import org.neo4j.driver.internal.bolt.api.GqlError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelActivityLogger; import org.neo4j.driver.internal.bolt.basicimpl.logging.ChannelErrorLogger; import org.neo4j.driver.internal.bolt.basicimpl.messaging.Message; @@ -351,8 +351,8 @@ private static void verifyFailure(ResponseHandler handler) { @SuppressWarnings("SameParameterValue") private static void verifyFailure( - ResponseHandler handler, String code, String message, Class exceptionCls) { - var captor = ArgumentCaptor.forClass(Neo4jException.class); + ResponseHandler handler, String code, String message, Class exceptionCls) { + var captor = ArgumentCaptor.forClass(BoltFailureException.class); verify(handler).onFailure(captor.capture()); var value = captor.getValue(); assertEquals(code, value.code()); diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageHandlerTest.java index c11b0fe8e3..e0f33bd5fc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/async/inbound/InboundMessageHandlerTest.java @@ -38,8 +38,8 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.ChannelAttributes; import org.neo4j.driver.internal.bolt.basicimpl.messaging.MessageFormat; import org.neo4j.driver.internal.bolt.basicimpl.messaging.response.FailureMessage; @@ -94,7 +94,7 @@ void shouldReadFailureMessage() { channel.writeInbound(writer.asByteBuf(new FailureMessage("Neo.TransientError.General.ReadOnly", "Hi!"))); - var captor = ArgumentCaptor.forClass(Neo4jException.class); + var captor = ArgumentCaptor.forClass(BoltFailureException.class); verify(responseHandler).onFailure(captor.capture()); assertEquals("Neo.TransientError.General.ReadOnly", captor.getValue().code()); assertEquals("Hi!", captor.getValue().getMessage()); diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/HelloResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/HelloResponseHandlerTest.java index fbd8c6dcc7..c74469ecd3 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/HelloResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/handlers/HelloResponseHandlerTest.java @@ -40,8 +40,8 @@ import org.junit.jupiter.api.Test; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.UntrustedServerException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltUntrustedServerException; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.ChannelErrorHandler; import org.neo4j.driver.internal.bolt.basicimpl.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.bolt.basicimpl.async.outbound.OutboundMessageHandler; @@ -85,7 +85,7 @@ void shouldThrowWhenServerVersionNotReturned() { var handler = new HelloResponseHandler(agentFuture, channel, mock(Clock.class), latestAuth); var metadata = metadata(null, "bolt-1"); - assertThrows(UntrustedServerException.class, () -> handler.onSuccess(metadata)); + assertThrows(BoltUntrustedServerException.class, () -> handler.onSuccess(metadata)); assertTrue(agentFuture.isCompletedExceptionally()); // initialization failed assertTrue(channel.closeFuture().isDone()); // channel was closed @@ -98,7 +98,7 @@ void shouldThrowWhenServerVersionIsNull() { var handler = new HelloResponseHandler(agentFuture, channel, mock(Clock.class), latestAuth); var metadata = metadata(Values.NULL, "bolt-x"); - assertThrows(UntrustedServerException.class, () -> handler.onSuccess(metadata)); + assertThrows(BoltUntrustedServerException.class, () -> handler.onSuccess(metadata)); assertTrue(agentFuture.isCompletedExceptionally()); // initialization failed assertTrue(channel.closeFuture().isDone()); // channel was closed @@ -111,7 +111,7 @@ void shouldThrowWhenServerAgentIsUnrecognised() { var handler = new HelloResponseHandler(agentFuture, channel, mock(Clock.class), latestAuth); var metadata = metadata("WrongServerVersion", "bolt-x"); - assertThrows(UntrustedServerException.class, () -> handler.onSuccess(metadata)); + assertThrows(BoltUntrustedServerException.class, () -> handler.onSuccess(metadata)); assertTrue(agentFuture.isCompletedExceptionally()); // initialization failed assertTrue(channel.closeFuture().isDone()); // channel was closed diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocolTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocolTest.java index 20c7d183b6..4bfdfeee96 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/BoltProtocolTest.java @@ -24,8 +24,8 @@ import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.Test; -import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; import org.neo4j.driver.internal.bolt.basicimpl.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.bolt.basicimpl.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.bolt.basicimpl.messaging.v41.BoltProtocolV41; @@ -47,11 +47,11 @@ void shouldCreateProtocolForKnownVersions() { void shouldThrowForUnknownVersion() { assertAll( () -> assertThrows( - ClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(42, 0))), + BoltClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(42, 0))), () -> assertThrows( - ClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(142, 0))), + BoltClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(142, 0))), () -> assertThrows( - ClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(-1, 0)))); + BoltClientException.class, () -> BoltProtocol.forVersion(new BoltProtocolVersion(-1, 0)))); } @Test @@ -59,6 +59,6 @@ void shouldThrowForChannelWithUnknownProtocolVersion() { var channel = new EmbeddedChannel(); setProtocolVersion(channel, new BoltProtocolVersion(42, 0)); - assertThrows(ClientException.class, () -> BoltProtocol.forChannel(channel)); + assertThrows(BoltClientException.class, () -> BoltProtocol.forChannel(channel)); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/MessageFormatTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/MessageFormatTest.java index bf64f0a42f..33bb0813c2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/MessageFormatTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/MessageFormatTest.java @@ -43,8 +43,8 @@ import java.util.concurrent.CompletionException; import org.junit.jupiter.api.Test; import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.BoltProtocolUtil; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.bolt.basicimpl.async.outbound.ChunkAwareByteBufOutput; @@ -128,7 +128,7 @@ public void onRecord(Value[] fields) { // Expect Throwable error = assertThrows(CompletionException.class, errorFuture::join); - error = assertInstanceOf(ClientException.class, error.getCause()); + error = assertInstanceOf(BoltClientException.class, error.getCause()); assertThat( error.getMessage(), startsWith("Invalid message received, serialized NODE structures should have 3 fields, " diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3Test.java index 7fa874083a..dda71f1291 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/messaging/v3/BoltProtocolV3Test.java @@ -60,11 +60,11 @@ import org.mockito.stubbing.Answer; import org.neo4j.driver.Value; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; import org.neo4j.driver.internal.bolt.api.AccessMode; import org.neo4j.driver.internal.bolt.api.GqlError; import org.neo4j.driver.internal.bolt.api.RoutingContext; +import org.neo4j.driver.internal.bolt.api.exception.BoltClientException; import org.neo4j.driver.internal.bolt.api.summary.RouteSummary; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; import org.neo4j.driver.internal.bolt.basicimpl.async.connection.ChannelAttributes; @@ -538,7 +538,7 @@ void shouldReturnFailedStageWithNoConnectionInteractionsOnTelemetry() { } protected void testDatabaseNameSupport(boolean autoCommitTx) { - ClientException e; + BoltClientException e; if (autoCommitTx) { var connection = mock(Connection.class); given(connection.protocol()).willReturn(protocol); @@ -565,7 +565,7 @@ protected void testDatabaseNameSupport(boolean autoCommitTx) { handler, NoopLoggingProvider.INSTANCE) .toCompletableFuture(); - e = (ClientException) + e = (BoltClientException) assertThrows(CompletionException.class, future::join).getCause(); } else { var connection = mock(Connection.class); @@ -592,7 +592,7 @@ protected void testDatabaseNameSupport(boolean autoCommitTx) { handler, NoopLoggingProvider.INSTANCE) .toCompletableFuture(); - e = (ClientException) + e = (BoltClientException) assertThrows(CompletionException.class, future::join).getCause(); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractorTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractorTest.java index 51988cca04..fc2110360b 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractorTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/basicimpl/util/MetadataExtractorTest.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test; import org.neo4j.driver.Values; -import org.neo4j.driver.exceptions.UntrustedServerException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUntrustedServerException; class MetadataExtractorTest { private static final String RESULT_AVAILABLE_AFTER_KEY = "available_after"; @@ -73,13 +73,14 @@ void shouldExtractServer() { @Test void shouldFailToExtractServerVersionWhenMetadataDoesNotContainIt() { - assertThrows(UntrustedServerException.class, () -> extractServer(singletonMap("server", Values.NULL))); - assertThrows(UntrustedServerException.class, () -> extractServer(singletonMap("server", null))); + assertThrows(BoltUntrustedServerException.class, () -> extractServer(singletonMap("server", Values.NULL))); + assertThrows(BoltUntrustedServerException.class, () -> extractServer(singletonMap("server", null))); } @Test void shouldFailToExtractServerVersionFromNonNeo4jProduct() { assertThrows( - UntrustedServerException.class, () -> extractServer(singletonMap("server", value("NotNeo4j/1.2.3")))); + BoltUntrustedServerException.class, + () -> extractServer(singletonMap("server", value("NotNeo4j/1.2.3")))); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryTest.java b/driver/src/test/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryTest.java index ee04d075b6..7164ff8f5e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/bolt/routedimpl/cluster/RediscoveryTest.java @@ -61,13 +61,6 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; import org.mockito.stubbing.Answer; -import org.neo4j.driver.exceptions.AuthTokenManagerExecutionException; -import org.neo4j.driver.exceptions.AuthenticationException; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.exceptions.SessionExpiredException; -import org.neo4j.driver.exceptions.UnsupportedFeatureException; import org.neo4j.driver.internal.bolt.NoopLoggingProvider; import org.neo4j.driver.internal.bolt.api.BoltConnection; import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; @@ -76,9 +69,14 @@ import org.neo4j.driver.internal.bolt.api.ClusterComposition; import org.neo4j.driver.internal.bolt.api.DefaultDomainNameResolver; import org.neo4j.driver.internal.bolt.api.DomainNameResolver; +import org.neo4j.driver.internal.bolt.api.GqlStatusError; import org.neo4j.driver.internal.bolt.api.LoggingProvider; import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.SecurityPlan; +import org.neo4j.driver.internal.bolt.api.exception.BoltFailureException; +import org.neo4j.driver.internal.bolt.api.exception.BoltServiceUnavailableException; +import org.neo4j.driver.internal.bolt.api.exception.BoltUnsupportedFeatureException; +import org.neo4j.driver.internal.bolt.routedimpl.AuthTokenManagerExecutionException; import org.neo4j.driver.internal.util.FakeClock; import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; @@ -117,7 +115,7 @@ void shouldSkipFailingRouters() { Map responsesByAddress = new HashMap<>(); responsesByAddress.put(A, new RuntimeException("Hi!")); // first -> non-fatal failure - responsesByAddress.put(B, new ServiceUnavailableException("Hi!")); // second -> non-fatal failure + responsesByAddress.put(B, new BoltServiceUnavailableException("Hi!")); // second -> non-fatal failure responsesByAddress.put(C, expectedComposition); // third -> valid cluster composition var connectionProviderGetter = connectionProviderGetter(responsesByAddress); @@ -143,7 +141,13 @@ void shouldSkipFailingRouters() { @Test void shouldFailImmediatelyOnAuthError() { - var authError = new AuthenticationException("Neo.ClientError.Security.Unauthorized", "Wrong password"); + var authError = new BoltFailureException( + "Neo.ClientError.Security.Unauthorized", + "Wrong password", + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(""), + GqlStatusError.DIAGNOSTIC_RECORD, + null); Map responsesByAddress = new HashMap<>(); responsesByAddress.put(A, new RuntimeException("Hi!")); // first router -> non-fatal failure @@ -154,7 +158,7 @@ void shouldFailImmediatelyOnAuthError() { var table = routingTableMock(A, B, C); var error = assertThrows( - AuthenticationException.class, + BoltFailureException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, @@ -174,7 +178,14 @@ void shouldUseAnotherRouterOnAuthorizationExpiredException() { Map responsesByAddress = new HashMap<>(); responsesByAddress.put( - A, new AuthorizationExpiredException("Neo.ClientError.Security.AuthorizationExpired", "message")); + A, + new BoltFailureException( + "Neo.ClientError.Security.AuthorizationExpired", + "message", + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(""), + GqlStatusError.DIAGNOSTIC_RECORD, + null)); responsesByAddress.put(B, expectedComposition); var connectionProviderGetter = connectionProviderGetter(responsesByAddress); @@ -204,7 +215,13 @@ void shouldUseAnotherRouterOnAuthorizationExpiredException() { "Neo.ClientError.Transaction.InvalidBookmarkMixture" }) void shouldFailImmediatelyOnBookmarkErrors(String code) { - var error = new ClientException(code, "Invalid"); + var error = new BoltFailureException( + code, + "Invalid", + GqlStatusError.UNKNOWN.getStatus(), + GqlStatusError.UNKNOWN.getStatusDescription(""), + GqlStatusError.DIAGNOSTIC_RECORD, + null); Map responsesByAddress = new HashMap<>(); responsesByAddress.put(A, new RuntimeException("Hi!")); @@ -215,7 +232,7 @@ void shouldFailImmediatelyOnBookmarkErrors(String code) { var table = routingTableMock(A, B, C); var actualError = assertThrows( - ClientException.class, + BoltFailureException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, @@ -261,8 +278,8 @@ void shouldFallbackToInitialRouterWhenKnownRoutersFail() { new ClusterComposition(42, asOrderedSet(C, B, A), asOrderedSet(A, B), asOrderedSet(D, E), null); Map responsesByAddress = new HashMap<>(); - responsesByAddress.put(B, new ServiceUnavailableException("Hi!")); // first -> non-fatal failure - responsesByAddress.put(C, new ServiceUnavailableException("Hi!")); // second -> non-fatal failure + responsesByAddress.put(B, new BoltServiceUnavailableException("Hi!")); // first -> non-fatal failure + responsesByAddress.put(C, new BoltServiceUnavailableException("Hi!")); // second -> non-fatal failure responsesByAddress.put(initialRouter, expectedComposition); // initial -> valid response var connectionProviderGetter = connectionProviderGetter(responsesByAddress); @@ -292,8 +309,8 @@ void shouldResolveInitialRouterAddress() { new ClusterComposition(42, asOrderedSet(A, B), asOrderedSet(A, B), asOrderedSet(A, B), null); Map responsesByAddress = new HashMap<>(); - responsesByAddress.put(B, new ServiceUnavailableException("Hi!")); // first -> non-fatal failure - responsesByAddress.put(C, new ServiceUnavailableException("Hi!")); // second -> non-fatal failure + responsesByAddress.put(B, new BoltServiceUnavailableException("Hi!")); // first -> non-fatal failure + responsesByAddress.put(C, new BoltServiceUnavailableException("Hi!")); // second -> non-fatal failure responsesByAddress.put(D, new IOException("Hi!")); // resolved first -> non-fatal failure responsesByAddress.put(E, expectedComposition); // resolved second -> valid response @@ -330,8 +347,8 @@ void shouldResolveInitialRouterAddressUsingCustomResolver() { }; Map responsesByAddress = new HashMap<>(); - responsesByAddress.put(B, new ServiceUnavailableException("Hi!")); // first -> non-fatal failure - responsesByAddress.put(C, new ServiceUnavailableException("Hi!")); // second -> non-fatal failure + responsesByAddress.put(B, new BoltServiceUnavailableException("Hi!")); // first -> non-fatal failure + responsesByAddress.put(C, new BoltServiceUnavailableException("Hi!")); // second -> non-fatal failure responsesByAddress.put(E, expectedComposition); // resolved second -> valid response var connectionProviderGetter = connectionProviderGetter(responsesByAddress); @@ -388,9 +405,9 @@ void shouldPropagateFailureWhenResolverFails() { @Test void shouldRecordAllErrorsWhenNoRouterRespond() { Map responsesByAddress = new HashMap<>(); - var first = new ServiceUnavailableException("Hi!"); + var first = new BoltServiceUnavailableException("Hi!"); responsesByAddress.put(A, first); // first -> non-fatal failure - var second = new SessionExpiredException("Hi!"); + var second = new BoltServiceUnavailableException("Hi!"); responsesByAddress.put(B, second); // second -> non-fatal failure var third = new IOException("Hi!"); responsesByAddress.put(C, third); // third -> non-fatal failure @@ -400,7 +417,7 @@ void shouldRecordAllErrorsWhenNoRouterRespond() { var table = routingTableMock(A, B, C); var e = assertThrows( - ServiceUnavailableException.class, + BoltServiceUnavailableException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, @@ -476,7 +493,7 @@ void shouldUseKnownRoutersWhenInitialRouterFails() { new ClusterComposition(42, asOrderedSet(D, E), asOrderedSet(E, D), asOrderedSet(A, B), null); Map responsesByAddress = new HashMap<>(); - responsesByAddress.put(initialRouter, new ServiceUnavailableException("Hi")); // initial -> non-fatal error + responsesByAddress.put(initialRouter, new BoltServiceUnavailableException("Hi")); // initial -> non-fatal error responsesByAddress.put(D, new IOException("Hi")); // first known -> non-fatal failure responsesByAddress.put(E, validComposition); // second known -> valid composition @@ -501,7 +518,7 @@ void shouldUseKnownRoutersWhenInitialRouterFails() { @Test void shouldNotLogWhenSingleRetryAttemptFails() { - Map responsesByAddress = singletonMap(A, new ServiceUnavailableException("Hi!")); + Map responsesByAddress = singletonMap(A, new BoltServiceUnavailableException("Hi!")); var connectionProviderGetter = connectionProviderGetter(responsesByAddress); var resolver = resolverMock(A, A); @@ -513,7 +530,7 @@ void shouldNotLogWhenSingleRetryAttemptFails() { var table = routingTableMock(A); var e = assertThrows( - ServiceUnavailableException.class, + BoltServiceUnavailableException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, @@ -549,7 +566,7 @@ void shouldResolveToIP() throws UnknownHostException { @Test void shouldFailImmediatelyOnAuthTokenManagerExecutionException() { - var exception = new AuthTokenManagerExecutionException("message", mock(Throwable.class)); + var exception = new AuthTokenManagerExecutionException(mock(Throwable.class)); Map responsesByAddress = new HashMap<>(); responsesByAddress.put(A, new RuntimeException("Hi!")); // first router -> non-fatal failure @@ -575,7 +592,7 @@ void shouldFailImmediatelyOnAuthTokenManagerExecutionException() { @Test void shouldFailImmediatelyOnUnsupportedFeatureException() { - var exception = new UnsupportedFeatureException("message", mock(Throwable.class)); + var exception = new BoltUnsupportedFeatureException("message"); Map responsesByAddress = new HashMap<>(); responsesByAddress.put(A, new RuntimeException("Hi!")); // first router -> non-fatal failure @@ -586,7 +603,7 @@ void shouldFailImmediatelyOnUnsupportedFeatureException() { var table = routingTableMock(A, B, C); var actualException = assertThrows( - UnsupportedFeatureException.class, + BoltUnsupportedFeatureException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, @@ -612,7 +629,7 @@ void shouldLogScopedIPV6AddressWithStringFormattingLogger() throws UnknownHostEx var table = routingTableMock(true); var pool = mock(BoltConnectionProvider.class); given(pool.connect(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willReturn(failedFuture(new ServiceUnavailableException("not available"))); + .willReturn(failedFuture(new BoltServiceUnavailableException("not available"))); var logging = mock(LoggingProvider.class); var logger = mock(System.Logger.class); given(logging.getLog(any(Class.class))).willReturn(logger); @@ -623,7 +640,7 @@ void shouldLogScopedIPV6AddressWithStringFormattingLogger() throws UnknownHostEx // WHEN & THEN assertThrows( - ServiceUnavailableException.class, + BoltServiceUnavailableException.class, () -> await(rediscovery.lookupClusterComposition( SecurityPlan.INSECURE, table, diff --git a/driver/src/test/java/org/neo4j/driver/internal/cursor/ResultCursorImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/cursor/ResultCursorImplTest.java index b765d82a01..37574e73f4 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cursor/ResultCursorImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cursor/ResultCursorImplTest.java @@ -42,10 +42,10 @@ import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.exceptions.NoSuchRecordException; import org.neo4j.driver.internal.DatabaseBookmark; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; import org.neo4j.driver.internal.bolt.basicimpl.handlers.PullResponseHandlerImpl; @@ -54,10 +54,7 @@ class ResultCursorImplTest { ResultCursorImpl cursor; @Mock - BoltConnection connection; - - @Mock - Consumer throwableConsumer; + DriverBoltConnection connection; @Mock Consumer bookmarkConsumer; @@ -86,7 +83,7 @@ void shouldNextAsync() { cursor.onPullSummary(new PullResponseHandlerImpl.PullSummaryImpl(true, Collections.emptyMap())); given(connection.pull(0, fetchSize)).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onRecord(new Value[0]); return CompletableFuture.completedStage(null); }); @@ -140,7 +137,7 @@ void shouldFailSingleAsync() { given(connection.serverAddress()).willReturn(BoltServerAddress.LOCAL_DEFAULT); given(connection.pull(0, fetchSize)).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onRecord(new Value[0]); var pullSummary = mock(PullSummary.class); given(pullSummary.hasMore()).willReturn(true); @@ -161,7 +158,7 @@ void shouldFailSingleAsyncOnError() { given(connection.pull(0, fetchSize)).willReturn(CompletableFuture.completedStage(connection)); var error = new Neo4jException("code", "message"); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onError(error); handler.onComplete(); return CompletableFuture.completedStage(null); @@ -194,7 +191,7 @@ void shouldFetchMore() { given(connection.serverAddress()).willReturn(BoltServerAddress.LOCAL_DEFAULT); given(connection.pull(0, fetchSize)).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); for (var i = 0; i < fetchSize; i++) { handler.onRecord(new Value[0]); } @@ -219,7 +216,7 @@ void shouldListAsync() { given(connection.serverAddress()).willReturn(BoltServerAddress.LOCAL_DEFAULT); given(connection.pull(0, -1)).willReturn(CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onRecord(new Value[0]); var pullSummary = mock(PullSummary.class); handler.onPullSummary(pullSummary); @@ -238,7 +235,7 @@ void shouldFailListAsyncOnError() { given(connection.pull(0, -1)).willReturn(CompletableFuture.completedStage(connection)); var error = new Neo4jException("code", "message"); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onError(error); handler.onComplete(); return CompletableFuture.completedStage(null); @@ -272,7 +269,7 @@ void shouldFailPeekAsyncOnError() { given(connection.pull(0, fetchSize)).willReturn(CompletableFuture.completedStage(connection)); var error = new Neo4jException("code", "message"); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onError(error); handler.onComplete(); return CompletableFuture.completedStage(null); @@ -306,7 +303,7 @@ void shouldFailConsumeAsyncOnError() { given(connection.discard(0, -1)).willReturn(CompletableFuture.completedStage(connection)); var error = new Neo4jException("code", "message"); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onError(error); handler.onComplete(); return CompletableFuture.completedStage(null); diff --git a/driver/src/test/java/org/neo4j/driver/internal/cursor/RxResultCursorImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/cursor/RxResultCursorImplTest.java index edc710c3e2..470986f13d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cursor/RxResultCursorImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cursor/RxResultCursorImplTest.java @@ -37,14 +37,14 @@ import org.neo4j.driver.Query; import org.neo4j.driver.Record; import org.neo4j.driver.internal.DatabaseBookmark; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; class RxResultCursorImplTest { @Mock - BoltConnection connection; + DriverBoltConnection connection; @Mock Query query; diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java index ccfd541b9f..1b68f6a002 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java @@ -46,10 +46,10 @@ import org.neo4j.driver.Logging; import org.neo4j.driver.Record; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; import org.neo4j.driver.internal.cursor.RxResultCursor; import org.neo4j.driver.internal.cursor.RxResultCursorImpl; @@ -136,12 +136,12 @@ void shouldCancelKeys() { @Test void shouldObtainRecordsAndSummary() { // Given - var boltConnection = mock(BoltConnection.class); + var boltConnection = mock(DriverBoltConnection.class); given(boltConnection.pull(anyLong(), anyLong())).willReturn(CompletableFuture.completedFuture(boltConnection)); given(boltConnection.serverAddress()).willReturn(new BoltServerAddress("localhost")); given(boltConnection.protocolVersion()).willReturn(new BoltProtocolVersion(5, 1)); given(boltConnection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArguments()[0]; + var handler = (DriverResponseHandler) invocation.getArguments()[0]; handler.onRecord(values(1, 1, 1)); handler.onRecord(values(2, 2, 2)); handler.onRecord(values(3, 3, 3)); @@ -169,12 +169,12 @@ void shouldObtainRecordsAndSummary() { @Test void shouldCancelStreamingButObtainSummary() { // Given - var boltConnection = mock(BoltConnection.class); + var boltConnection = mock(DriverBoltConnection.class); given(boltConnection.pull(anyLong(), anyLong())).willReturn(CompletableFuture.completedFuture(boltConnection)); given(boltConnection.serverAddress()).willReturn(new BoltServerAddress("localhost")); given(boltConnection.protocolVersion()).willReturn(new BoltProtocolVersion(5, 1)); given(boltConnection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArguments()[0]; + var handler = (DriverResponseHandler) invocation.getArguments()[0]; handler.onRecord(values(1, 1, 1)); handler.onRecord(values(2, 2, 2)); handler.onRecord(values(3, 3, 3)); @@ -213,13 +213,13 @@ void shouldErrorIfFailedToCreateCursor() { @Test void shouldErrorIfFailedToStream() { // Given - var boltConnection = mock(BoltConnection.class); + var boltConnection = mock(DriverBoltConnection.class); given(boltConnection.pull(anyLong(), anyLong())).willReturn(CompletableFuture.completedFuture(boltConnection)); given(boltConnection.serverAddress()).willReturn(new BoltServerAddress("localhost")); given(boltConnection.protocolVersion()).willReturn(new BoltProtocolVersion(5, 1)); Throwable error = new RuntimeException("Hi"); given(boltConnection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArguments()[0]; + var handler = (DriverResponseHandler) invocation.getArguments()[0]; handler.onError(error); handler.onComplete(); return CompletableFuture.completedFuture(null); @@ -251,11 +251,11 @@ void shouldDelegateIsOpen(boolean expectedState) { then(cursor).should().isDone(); } - private InternalRxResult newRxResult(BoltConnection boltConnection) { + private InternalRxResult newRxResult(DriverBoltConnection boltConnection) { return newRxResult(boltConnection, mock()); } - private InternalRxResult newRxResult(BoltConnection boltConnection, RunSummary runSummary) { + private InternalRxResult newRxResult(DriverBoltConnection boltConnection, RunSummary runSummary) { RxResultCursor cursor = new RxResultCursorImpl( boltConnection, mock(), runSummary, null, databaseBookmark -> {}, false, Logging.none()); return newRxResult(cursor); diff --git a/driver/src/test/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWorkTest.java b/driver/src/test/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWorkTest.java index 8b0b55cbe3..b0462bc306 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWorkTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/telemetry/ApiTelemetryWorkTest.java @@ -25,7 +25,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.Mockito; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.TelemetryApi; class ApiTelemetryWorkTest { @@ -35,7 +35,7 @@ class ApiTelemetryWorkTest { void shouldPipelineTelemetryWhenTelemetryIsEnabledAndConnectionSupportsTelemetry(TelemetryApi telemetryApi) { var apiTelemetryWork = new ApiTelemetryWork(telemetryApi); apiTelemetryWork.setEnabled(true); - var boltConnection = Mockito.mock(BoltConnection.class); + var boltConnection = Mockito.mock(DriverBoltConnection.class); var boltConnectionStage = CompletableFuture.completedFuture(boltConnection); given(boltConnection.telemetrySupported()).willReturn(true); given(boltConnection.telemetry(telemetryApi)).willReturn(boltConnectionStage); @@ -52,7 +52,7 @@ void shouldNotPipelineTelemetryWhenTelemetryIsEnabledAndConnectionDoesNotSupport TelemetryApi telemetryApi) { var apiTelemetryWork = new ApiTelemetryWork(telemetryApi); apiTelemetryWork.setEnabled(true); - var boltConnection = Mockito.mock(BoltConnection.class); + var boltConnection = Mockito.mock(DriverBoltConnection.class); var future = apiTelemetryWork.pipelineTelemetryIfEnabled(boltConnection).toCompletableFuture(); @@ -67,7 +67,7 @@ void shouldNotPipelineTelemetryWhenTelemetryIsEnabledAndConnectionDoesNotSupport void shouldNotPipelineTelemetryWhenTelemetryIsDisabledAndConnectionDoesNotSupportTelemetry( TelemetryApi telemetryApi) { var apiTelemetryWork = new ApiTelemetryWork(telemetryApi); - var boltConnection = Mockito.mock(BoltConnection.class); + var boltConnection = Mockito.mock(DriverBoltConnection.class); var future = apiTelemetryWork.pipelineTelemetryIfEnabled(boltConnection).toCompletableFuture(); @@ -80,7 +80,7 @@ void shouldNotPipelineTelemetryWhenTelemetryIsDisabledAndConnectionDoesNotSuppor @EnumSource(TelemetryApi.class) void shouldNotPipelineTelemetryWhenTelemetryIsDisabledAndConnectionSupportsTelemetry(TelemetryApi telemetryApi) { var apiTelemetryWork = new ApiTelemetryWork(telemetryApi); - var boltConnection = Mockito.mock(BoltConnection.class); + var boltConnection = Mockito.mock(DriverBoltConnection.class); given(boltConnection.telemetrySupported()).willReturn(true); var future = apiTelemetryWork.pipelineTelemetryIfEnabled(boltConnection).toCompletableFuture(); diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java index 0002c65a79..07808bd9b0 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java @@ -18,119 +18,20 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.neo4j.driver.internal.util.ErrorUtil.isFatal; import static org.neo4j.driver.internal.util.ErrorUtil.newConnectionTerminatedError; -import static org.neo4j.driver.internal.util.ErrorUtil.newNeo4jError; import static org.neo4j.driver.internal.util.ErrorUtil.rethrowAsyncException; -import java.io.IOException; import java.net.UnknownHostException; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Test; -import org.neo4j.driver.exceptions.AuthenticationException; -import org.neo4j.driver.exceptions.AuthorizationExpiredException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.DatabaseException; import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.TokenExpiredException; -import org.neo4j.driver.exceptions.TransientException; -import org.neo4j.driver.internal.bolt.api.GqlError; class ErrorUtilTest { - @Test - void shouldCreateAuthenticationException() { - var code = "Neo.ClientError.Security.Unauthorized"; - var message = "Wrong credentials"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(AuthenticationException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldCreateClientException() { - var code = "Neo.ClientError.Transaction.InvalidBookmark"; - var message = "Wrong bookmark"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(ClientException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldCreateTransientException() { - var code = "Neo.TransientError.Transaction.DeadlockDetected"; - var message = "Deadlock occurred"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(TransientException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldCreateDatabaseException() { - var code = "Neo.DatabaseError.Transaction.TransactionLogError"; - var message = "Failed to write the transaction log"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(DatabaseException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldCreateDatabaseExceptionWhenErrorCodeIsWrong() { - var code = "WrongErrorCode"; - var message = "Some really strange error"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(DatabaseException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldTreatNotNeo4jExceptionAsFatal() { - assertTrue(isFatal(new IOException("IO failed!"))); - } - - @Test - void shouldTreatProtocolErrorAsFatal() { - assertTrue(isFatal(new ClientException("Neo.ClientError.Request.Invalid", "Illegal request"))); - assertTrue(isFatal(new ClientException("Neo.ClientError.Request.InvalidFormat", "Wrong format"))); - assertTrue(isFatal(new ClientException("Neo.ClientError.Request.TransactionRequired", "No tx!"))); - } - - @Test - void shouldTreatAuthenticationExceptionAsNonFatal() { - assertFalse(isFatal(new AuthenticationException("Neo.ClientError.Security.Unauthorized", ""))); - } - - @Test - void shouldTreatClientExceptionAsNonFatal() { - assertFalse(isFatal(new ClientException("Neo.ClientError.Transaction.ConstraintsChanged", ""))); - } - - @Test - void shouldTreatDatabaseExceptionAsFatal() { - assertTrue(isFatal(new ClientException("Neo.DatabaseError.Schema.ConstraintCreationFailed", ""))); - } @Test void shouldCreateConnectionTerminatedError() { @@ -152,54 +53,6 @@ void shouldCreateConnectionTerminatedErrorWithReason() { assertThat(error.getMessage(), containsString(reason)); } - @Test - void shouldCreateAuthorizationExpiredException() { - var code = "Neo.ClientError.Security.AuthorizationExpired"; - var message = "Expired authorization info"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(AuthorizationExpiredException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldCreateTokenExpiredException() { - var code = "Neo.ClientError.Security.TokenExpired"; - var message = "message"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(TokenExpiredException.class)); - assertEquals(code, error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldMapTransientTransactionTerminatedToClientException() { - var code = "Neo.TransientError.Transaction.Terminated"; - var message = "message"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(ClientException.class)); - assertEquals("Neo.ClientError.Transaction.Terminated", error.code()); - assertEquals(message, error.getMessage()); - } - - @Test - void shouldMapTransientTransactionLockClientStoppedToClientException() { - var code = "Neo.TransientError.Transaction.LockClientStopped"; - var message = "message"; - - var error = newNeo4jError(new GqlError(code, message)); - - assertThat(error, instanceOf(ClientException.class)); - assertEquals("Neo.ClientError.Transaction.LockClientStopped", error.code()); - assertEquals(message, error.getMessage()); - } - @Test void shouldWrapCheckedExceptionsInNeo4jExceptionWhenRethrowingAsyncException() { var ee = mock(ExecutionException.class); 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 cf62d2b5eb..4eb7be7cfd 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 @@ -47,7 +47,7 @@ import org.neo4j.driver.Value; import org.neo4j.driver.Values; import org.neo4j.driver.exceptions.value.Uncoercible; -import org.neo4j.driver.internal.bolt.api.BoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; import org.neo4j.driver.internal.summary.InternalInputPosition; @@ -471,12 +471,12 @@ private static Query query() { return new Query("RETURN 1"); } - private static BoltConnection connectionMock() { + private static DriverBoltConnection connectionMock() { return connectionMock(BoltServerAddress.LOCAL_DEFAULT); } - private static BoltConnection connectionMock(BoltServerAddress address) { - var connection = mock(BoltConnection.class); + private static DriverBoltConnection connectionMock(BoltServerAddress address) { + var connection = mock(DriverBoltConnection.class); when(connection.serverAddress()).thenReturn(address); when(connection.protocolVersion()).thenReturn(new BoltProtocolVersion(4, 3)); when(connection.serverAgent()).thenReturn("Neo4j/4.2.5"); diff --git a/driver/src/test/java/org/neo4j/driver/testutil/TestUtil.java b/driver/src/test/java/org/neo4j/driver/testutil/TestUtil.java index 7217f239b3..2a4eca5aef 100644 --- a/driver/src/test/java/org/neo4j/driver/testutil/TestUtil.java +++ b/driver/src/test/java/org/neo4j/driver/testutil/TestUtil.java @@ -73,12 +73,12 @@ import org.neo4j.driver.SessionConfig; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.NoOpBookmarkManager; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection; +import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnectionProvider; +import org.neo4j.driver.internal.adaptedbolt.DriverResponseHandler; import org.neo4j.driver.internal.async.NetworkSession; -import org.neo4j.driver.internal.bolt.api.BoltConnection; -import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider; import org.neo4j.driver.internal.bolt.api.BoltProtocolVersion; import org.neo4j.driver.internal.bolt.api.BoltServerAddress; -import org.neo4j.driver.internal.bolt.api.ResponseHandler; import org.neo4j.driver.internal.bolt.api.summary.CommitSummary; import org.neo4j.driver.internal.bolt.api.summary.PullSummary; import org.neo4j.driver.internal.bolt.api.summary.RunSummary; @@ -216,29 +216,29 @@ public static boolean databaseExists(Driver driver, String database) { } } - public static NetworkSession newSession(BoltConnectionProvider connectionProvider, Set bookmarks) { + public static NetworkSession newSession(DriverBoltConnectionProvider connectionProvider, Set bookmarks) { return newSession(connectionProvider, WRITE, bookmarks); } private static NetworkSession newSession( - BoltConnectionProvider connectionProvider, AccessMode mode, Set bookmarks) { + DriverBoltConnectionProvider connectionProvider, AccessMode mode, Set bookmarks) { return newSession(connectionProvider, mode, new FixedRetryLogic(0), bookmarks); } - public static NetworkSession newSession(BoltConnectionProvider connectionProvider, AccessMode mode) { + public static NetworkSession newSession(DriverBoltConnectionProvider connectionProvider, AccessMode mode) { return newSession(connectionProvider, mode, Collections.emptySet()); } - public static NetworkSession newSession(BoltConnectionProvider connectionProvider, RetryLogic logic) { + public static NetworkSession newSession(DriverBoltConnectionProvider connectionProvider, RetryLogic logic) { return newSession(connectionProvider, WRITE, logic, Collections.emptySet()); } - public static NetworkSession newSession(BoltConnectionProvider connectionProvider) { + public static NetworkSession newSession(DriverBoltConnectionProvider connectionProvider) { return newSession(connectionProvider, WRITE, Collections.emptySet()); } public static NetworkSession newSession( - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, AccessMode mode, RetryLogic retryLogic, Set bookmarks) { @@ -246,7 +246,7 @@ public static NetworkSession newSession( } public static NetworkSession newSession( - BoltConnectionProvider connectionProvider, + DriverBoltConnectionProvider connectionProvider, AccessMode mode, RetryLogic retryLogic, Set bookmarks, @@ -270,13 +270,13 @@ public static NetworkSession newSession( } public static void setupConnectionAnswers( - BoltConnection connection, List> handlerConsumers) { + DriverBoltConnection connection, List> handlerConsumers) { given(connection.flush(any())).willAnswer(new Answer>() { private int index; @Override public CompletionStage answer(InvocationOnMock invocation) { - var handler = (ResponseHandler) invocation.getArguments()[0]; + var handler = (DriverResponseHandler) invocation.getArguments()[0]; var consumer = handlerConsumers.get(index++); consumer.accept(handler); return CompletableFuture.completedFuture(null); @@ -284,20 +284,20 @@ public CompletionStage answer(InvocationOnMock invocation) { }); } - public static void verifyAutocommitRunRx(BoltConnection connection, String query) { + public static void verifyAutocommitRunRx(DriverBoltConnection connection, String query) { then(connection) .should() .runInAutoCommitTransaction(any(), any(), any(), any(), eq(query), any(), any(), any(), any()); then(connection).should().flush(any()); } - public static void verifyRunAndPull(BoltConnection connection, String query) { + public static void verifyRunAndPull(DriverBoltConnection connection, String query) { then(connection).should().run(eq(query), any()); then(connection).should().pull(anyLong(), anyLong()); then(connection).should(atLeastOnce()).flush(any()); } - public static void verifyAutocommitRunAndPull(BoltConnection connection, String query) { + public static void verifyAutocommitRunAndPull(DriverBoltConnection connection, String query) { then(connection) .should() .runInAutoCommitTransaction(any(), any(), any(), any(), eq(query), any(), any(), any(), any()); @@ -305,50 +305,50 @@ public static void verifyAutocommitRunAndPull(BoltConnection connection, String then(connection).should().flush(any()); } - public static void verifyCommitTx(BoltConnection connection, VerificationMode mode) { + public static void verifyCommitTx(DriverBoltConnection connection, VerificationMode mode) { verify(connection, mode).commit(); verify(connection, mode).close(); } - public static void verifyCommitTx(BoltConnection connection) { + public static void verifyCommitTx(DriverBoltConnection connection) { verifyCommitTx(connection, times(1)); } - public static void verifyRollbackTx(BoltConnection connection, VerificationMode mode) { + public static void verifyRollbackTx(DriverBoltConnection connection, VerificationMode mode) { verify(connection, mode).rollback(); } - public static void verifyRollbackTx(BoltConnection connection) { + public static void verifyRollbackTx(DriverBoltConnection connection) { verifyRollbackTx(connection, times(1)); verify(connection, atLeastOnce()).close(); } - public static void setupFailingRun(BoltConnection connection, Throwable error) { - given(connection.run(any(), any())).willAnswer((Answer>) + public static void setupFailingRun(DriverBoltConnection connection, Throwable error) { + given(connection.run(any(), any())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); - given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) + given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); handler.onError(error); handler.onComplete(); return CompletableFuture.completedStage(null); }); } - public static void setupFailingCommit(BoltConnection connection) { + public static void setupFailingCommit(DriverBoltConnection connection) { setupFailingCommit(connection, 1); } - public static void setupFailingCommit(BoltConnection connection, int times) { - given(connection.commit()).willAnswer((Answer>) + public static void setupFailingCommit(DriverBoltConnection connection, int times) { + given(connection.commit()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer(new Answer>() { int invoked; @Override public CompletionStage answer(InvocationOnMock invocation) { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); if (invoked++ < times) { handler.onError(new ServiceUnavailableException("")); } else { @@ -360,19 +360,19 @@ public CompletionStage answer(InvocationOnMock invocation) { }); } - public static void setupFailingRollback(BoltConnection connection) { + public static void setupFailingRollback(DriverBoltConnection connection) { setupFailingRollback(connection, 1); } - public static void setupFailingRollback(BoltConnection connection, int times) { - given(connection.rollback()).willAnswer((Answer>) + public static void setupFailingRollback(DriverBoltConnection connection, int times) { + given(connection.rollback()).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer(new Answer>() { int invoked; @Override public CompletionStage answer(InvocationOnMock invocation) { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); if (invoked++ < times) { handler.onError(new ServiceUnavailableException("")); } else { @@ -384,13 +384,13 @@ public CompletionStage answer(InvocationOnMock invocation) { }); } - public static void setupSuccessfulRunAndPull(BoltConnection connection) { - given(connection.run(any(), any())).willAnswer((Answer>) + public static void setupSuccessfulRunAndPull(DriverBoltConnection connection) { + given(connection.run(any(), any())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); - given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) + given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); var runSummary = mock(RunSummary.class); given(runSummary.keys()).willReturn(Collections.emptyList()); handler.onRunSummary(runSummary); @@ -402,14 +402,14 @@ public static void setupSuccessfulRunAndPull(BoltConnection connection) { }); } - public static void setupSuccessfulAutocommitRunAndPull(BoltConnection connection) { + public static void setupSuccessfulAutocommitRunAndPull(DriverBoltConnection connection) { given(connection.runInAutoCommitTransaction(any(), any(), any(), any(), any(), any(), any(), any(), any())) - .willAnswer((Answer>) + .willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); - given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) + given(connection.pull(anyLong(), anyLong())).willAnswer((Answer>) invocation -> CompletableFuture.completedStage(connection)); given(connection.flush(any())).willAnswer((Answer>) invocation -> { - var handler = (ResponseHandler) invocation.getArgument(0); + var handler = (DriverResponseHandler) invocation.getArgument(0); var runSummary = mock(RunSummary.class); given(runSummary.keys()).willReturn(Collections.emptyList()); handler.onRunSummary(runSummary); @@ -421,12 +421,12 @@ public static void setupSuccessfulAutocommitRunAndPull(BoltConnection connection }); } - public static BoltConnection connectionMock() { + public static DriverBoltConnection connectionMock() { return connectionMock(new BoltProtocolVersion(4, 2)); } - public static BoltConnection connectionMock(BoltProtocolVersion protocolVersion) { - var connection = mock(BoltConnection.class); + public static DriverBoltConnection connectionMock(BoltProtocolVersion protocolVersion) { + var connection = mock(DriverBoltConnection.class); when(connection.serverAddress()).thenReturn(BoltServerAddress.LOCAL_DEFAULT); when(connection.protocolVersion()).thenReturn(protocolVersion); return connection;