Skip to content

Commit

Permalink
Update internal Bolt exception structure
Browse files Browse the repository at this point in the history
The main goal of this update is to refactor internal Bolt exception handling and to separate it from the user facing exceptions.
  • Loading branch information
injectives committed Dec 1, 2024
1 parent c84ca38 commit 3393c00
Show file tree
Hide file tree
Showing 102 changed files with 2,442 additions and 914 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ public Optional<Neo4jException> gqlCause() {
return findFirstGqlCause(this, Neo4jException.class);
}

@SuppressWarnings("DuplicatedCode")
private static <T extends Throwable> Optional<T> findFirstGqlCause(Throwable throwable, Class<T> targetCls) {
var cause = throwable.getCause();
if (cause == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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.
*
Expand Down
32 changes: 20 additions & 12 deletions driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -158,7 +161,7 @@ private InternalDriver createDriver(
AuthTokenManager authTokenManager,
boolean ownsEventLoopGroup,
Supplier<Rediscovery> rediscoverySupplier) {
BoltConnectionProvider boltConnectionProvider = null;
DriverBoltConnectionProvider boltConnectionProvider = null;
try {
boltConnectionProvider =
createBoltConnectionProvider(uri, config, eventLoopGroup, routingSettings, rediscoverySupplier);
Expand Down Expand Up @@ -204,28 +207,33 @@ private Function<BoltServerAddress, Set<BoltServerAddress>> createBoltServerAddr
.collect(Collectors.toCollection(LinkedHashSet::new));
}

private BoltConnectionProvider createBoltConnectionProvider(
private DriverBoltConnectionProvider createBoltConnectionProvider(
URI uri,
Config config,
EventLoopGroup eventLoopGroup,
RoutingSettings routingSettings,
Supplier<Rediscovery> rediscoverySupplier) {
BoltConnectionProvider boltConnectionProvider;
DriverBoltConnectionProvider boltConnectionProvider;
var clock = createClock();
var loggingProvider = new BoltLoggingProvider(config.logging());
Supplier<BoltConnectionProvider> 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;
}
Expand Down Expand Up @@ -308,7 +316,7 @@ protected Clock createClock() {
*/
protected SessionFactory createSessionFactory(
BoltSecurityPlanManager securityPlanManager,
BoltConnectionProvider connectionProvider,
DriverBoltConnectionProvider connectionProvider,
RetryLogic retryLogic,
Config config,
AuthTokenManager authTokenManager) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -54,7 +54,7 @@ public class SessionFactoryImpl implements SessionFactory {

SessionFactoryImpl(
BoltSecurityPlanManager securityPlanManager,
BoltConnectionProvider connectionProvider,
DriverBoltConnectionProvider connectionProvider,
RetryLogic retryLogic,
Config config,
AuthTokenManager authTokenManager) {
Expand Down Expand Up @@ -163,7 +163,7 @@ public CompletionStage<Boolean> supportsSessionAuth() {

private NetworkSession createSession(
BoltSecurityPlanManager securityPlanManager,
BoltConnectionProvider connectionProvider,
DriverBoltConnectionProvider connectionProvider,
RetryLogic retryLogic,
DatabaseName databaseName,
AccessMode mode,
Expand Down Expand Up @@ -214,7 +214,7 @@ private NetworkSession createSession(
authTokenManager);
}

public BoltConnectionProvider getConnectionProvider() {
public DriverBoltConnectionProvider getConnectionProvider() {
return connectionProvider;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<DriverBoltConnection> onLoop() {
return connection.onLoop().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> route(
DatabaseName databaseName, String impersonatedUser, Set<String> bookmarks) {
return connection
.route(databaseName, impersonatedUser, bookmarks)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> beginTransaction(
DatabaseName databaseName,
AccessMode accessMode,
String impersonatedUser,
Set<String> bookmarks,
TransactionType transactionType,
Duration txTimeout,
Map<String, Value> 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<DriverBoltConnection> runInAutoCommitTransaction(
DatabaseName databaseName,
AccessMode accessMode,
String impersonatedUser,
Set<String> bookmarks,
String query,
Map<String, Value> parameters,
Duration txTimeout,
Map<String, Value> txMetadata,
NotificationConfig notificationConfig) {
return connection
.runInAutoCommitTransaction(
databaseName,
accessMode,
impersonatedUser,
bookmarks,
query,
parameters,
txTimeout,
txMetadata,
notificationConfig)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> run(String query, Map<String, Value> parameters) {
return connection
.run(query, parameters)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> pull(long qid, long request) {
return connection
.pull(qid, request)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> discard(long qid, long number) {
return connection
.discard(qid, number)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> commit() {
return connection.commit().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> rollback() {
return connection.rollback().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> reset() {
return connection.reset().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> logoff() {
return connection.logoff().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> logon(Map<String, Value> authMap) {
return connection.logon(authMap).exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> telemetry(TelemetryApi telemetryApi) {
return connection
.telemetry(telemetryApi)
.exceptionally(errorMapper::mapAndTrow)
.thenApply(ignored -> this);
}

@Override
public CompletionStage<DriverBoltConnection> clear() {
return connection.clear().exceptionally(errorMapper::mapAndTrow).thenApply(ignored -> this);
}

@Override
public CompletionStage<Void> flush(DriverResponseHandler handler) {
return connection
.flush(new AdaptingDriverResponseHandler(handler, errorMapper))
.exceptionally(errorMapper::mapAndTrow);
}

@Override
public CompletionStage<Void> forceClose(String reason) {
return connection.forceClose(reason).exceptionally(errorMapper::mapAndTrow);
}

@Override
public CompletionStage<Void> close() {
return connection.close().exceptionally(errorMapper::mapAndTrow);
}

@Override
public BoltConnectionState state() {
return connection.state();
}

@Override
public CompletionStage<AuthData> 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();
}
}
Loading

0 comments on commit 3393c00

Please sign in to comment.