org.neo4j.driver.internal.DriverFactory Maven / Gradle / Ivy
Show all versions of neo4j-java-driver Show documentation
/*
* 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;
import static java.util.Objects.requireNonNull;
import static org.neo4j.driver.internal.IdentityResolver.IDENTITY_RESOLVER;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.util.concurrent.EventExecutorGroup;
import java.net.URI;
import java.time.Clock;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.neo4j.driver.AuthTokenManager;
import org.neo4j.driver.ClientCertificateManager;
import org.neo4j.driver.Config;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Logging;
import org.neo4j.driver.MetricsAdapter;
import org.neo4j.driver.internal.bolt.api.BoltConnectionProvider;
import org.neo4j.driver.internal.bolt.api.BoltServerAddress;
import org.neo4j.driver.internal.bolt.api.DefaultDomainNameResolver;
import org.neo4j.driver.internal.bolt.api.DomainNameResolver;
import org.neo4j.driver.internal.bolt.api.LoggingProvider;
import org.neo4j.driver.internal.bolt.api.RoutingContext;
import org.neo4j.driver.internal.bolt.basicimpl.NettyBoltConnectionProvider;
import org.neo4j.driver.internal.bolt.basicimpl.async.connection.BootstrapFactory;
import org.neo4j.driver.internal.bolt.pooledimpl.PooledBoltConnectionProvider;
import org.neo4j.driver.internal.bolt.routedimpl.RoutedBoltConnectionProvider;
import org.neo4j.driver.internal.bolt.routedimpl.cluster.Rediscovery;
import org.neo4j.driver.internal.metrics.DevNullMetricsProvider;
import org.neo4j.driver.internal.metrics.InternalMetricsProvider;
import org.neo4j.driver.internal.metrics.MetricsProvider;
import org.neo4j.driver.internal.metrics.MicrometerMetricsProvider;
import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic;
import org.neo4j.driver.internal.retry.RetryLogic;
import org.neo4j.driver.internal.security.BoltSecurityPlanManager;
import org.neo4j.driver.internal.security.SecurityPlan;
import org.neo4j.driver.internal.security.SecurityPlans;
import org.neo4j.driver.internal.util.DriverInfoUtil;
import org.neo4j.driver.net.ServerAddress;
public class DriverFactory {
public static final String NO_ROUTING_CONTEXT_ERROR_MESSAGE =
"Routing parameters are not supported with scheme 'bolt'. Given URI: ";
public final Driver newInstance(
URI uri,
AuthTokenManager authTokenManager,
ClientCertificateManager clientCertificateManager,
Config config) {
return newInstance(uri, authTokenManager, clientCertificateManager, config, null, null, null);
}
public final Driver newInstance(
URI uri,
AuthTokenManager authTokenManager,
ClientCertificateManager clientCertificateManager,
Config config,
SecurityPlan securityPlan,
EventLoopGroup eventLoopGroup,
Supplier rediscoverySupplier) {
if (securityPlan == null) {
var settings = new SecuritySettings(config.encrypted(), config.trustStrategy());
securityPlan = SecurityPlans.createSecurityPlan(
settings, uri.getScheme(), clientCertificateManager, config.logging());
}
var securityPlanManager = BoltSecurityPlanManager.from(securityPlan);
return newInstance(uri, authTokenManager, config, securityPlanManager, eventLoopGroup, rediscoverySupplier);
}
@SuppressWarnings("deprecation")
public final Driver newInstance(
URI uri,
AuthTokenManager authTokenManager,
Config config,
BoltSecurityPlanManager securityPlanManager,
EventLoopGroup eventLoopGroup,
Supplier rediscoverySupplier) {
requireNonNull(authTokenManager, "authTokenProvider must not be null");
Bootstrap bootstrap;
boolean ownsEventLoopGroup;
if (eventLoopGroup == null) {
bootstrap = createBootstrap(config.eventLoopThreads());
ownsEventLoopGroup = true;
} else {
bootstrap = createBootstrap(eventLoopGroup);
ownsEventLoopGroup = false;
}
var address = new InternalServerAddress(uri);
var routingSettings = new RoutingSettings(config.routingTablePurgeDelayMillis(), new RoutingContext(uri));
EventExecutorGroup eventExecutorGroup = bootstrap.config().group();
var retryLogic = createRetryLogic(config.maxTransactionRetryTimeMillis(), eventExecutorGroup, config.logging());
var metricsProvider = getOrCreateMetricsProvider(config, createClock());
return createDriver(
uri,
securityPlanManager,
address,
bootstrap.group(),
routingSettings,
retryLogic,
metricsProvider,
config,
authTokenManager,
ownsEventLoopGroup,
rediscoverySupplier);
}
protected static MetricsProvider getOrCreateMetricsProvider(Config config, Clock clock) {
var metricsAdapter = config.metricsAdapter();
// This can actually only happen when someone mocks the config
if (metricsAdapter == null) {
metricsAdapter = config.isMetricsEnabled() ? MetricsAdapter.DEFAULT : MetricsAdapter.DEV_NULL;
}
return switch (metricsAdapter) {
case DEV_NULL -> DevNullMetricsProvider.INSTANCE;
case DEFAULT -> new InternalMetricsProvider(clock, config.logging());
case MICROMETER -> MicrometerMetricsProvider.forGlobalRegistry();
};
}
private InternalDriver createDriver(
URI uri,
BoltSecurityPlanManager securityPlanManager,
ServerAddress address,
EventLoopGroup eventLoopGroup,
RoutingSettings routingSettings,
RetryLogic retryLogic,
MetricsProvider metricsProvider,
Config config,
AuthTokenManager authTokenManager,
boolean ownsEventLoopGroup,
Supplier rediscoverySupplier) {
BoltConnectionProvider boltConnectionProvider = null;
try {
boltConnectionProvider =
createBoltConnectionProvider(uri, config, eventLoopGroup, routingSettings, rediscoverySupplier);
boltConnectionProvider.init(
new BoltServerAddress(address.host(), address.port()),
new RoutingContext(uri),
DriverInfoUtil.boltAgent(),
config.userAgent(),
config.connectionTimeoutMillis(),
metricsProvider.metricsListener());
var sessionFactory = createSessionFactory(
securityPlanManager, boltConnectionProvider, retryLogic, config, authTokenManager);
Supplier> shutdownSupplier = ownsEventLoopGroup
? () -> {
var closeFuture = new CompletableFuture();
eventLoopGroup
.shutdownGracefully(200, 15_000, TimeUnit.MILLISECONDS)
.addListener(future -> closeFuture.complete(null));
return closeFuture;
}
: () -> CompletableFuture.completedStage(null);
var driver = createDriver(securityPlanManager, sessionFactory, metricsProvider, shutdownSupplier, config);
var log = config.logging().getLog(getClass());
if (uri.getScheme().startsWith("bolt")) {
log.info("Direct driver instance %s created for server address %s", driver.hashCode(), address);
} else {
log.info("Routing driver instance %s created for server address %s", driver.hashCode(), address);
}
return driver;
} catch (Throwable driverError) {
if (boltConnectionProvider != null) {
boltConnectionProvider.close().toCompletableFuture().join();
}
throw driverError;
}
}
private Function> createBoltServerAddressResolver(Config config) {
var serverAddressResolver = config.resolver() != null ? config.resolver() : IDENTITY_RESOLVER;
return (boltAddress) ->
serverAddressResolver.resolve(ServerAddress.of(boltAddress.host(), boltAddress.port())).stream()
.map(serverAddress -> new BoltServerAddress(serverAddress.host(), serverAddress.port()))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
private BoltConnectionProvider createBoltConnectionProvider(
URI uri,
Config config,
EventLoopGroup eventLoopGroup,
RoutingSettings routingSettings,
Supplier rediscoverySupplier) {
BoltConnectionProvider boltConnectionProvider;
var clock = createClock();
var loggingProvider = new BoltLoggingProvider(config.logging());
Supplier pooledBoltConnectionProviderSupplier =
() -> createPooledBoltConnectionProvider(config, eventLoopGroup, clock, loggingProvider);
if (uri.getScheme().startsWith("bolt")) {
assertNoRoutingContext(uri, routingSettings);
boltConnectionProvider = pooledBoltConnectionProviderSupplier.get();
} else {
boltConnectionProvider = createRoutedBoltConnectionProvider(
config,
pooledBoltConnectionProviderSupplier,
routingSettings,
rediscoverySupplier,
clock,
loggingProvider);
}
return boltConnectionProvider;
}
private BoltConnectionProvider createRoutedBoltConnectionProvider(
Config config,
Supplier pooledBoltConnectionProviderSupplier,
RoutingSettings routingSettings,
Supplier rediscoverySupplier,
Clock clock,
LoggingProvider loggingProvider) {
var boltServerAddressResolver = createBoltServerAddressResolver(config);
var rediscovery = rediscoverySupplier != null ? rediscoverySupplier.get() : null;
return new RoutedBoltConnectionProvider(
pooledBoltConnectionProviderSupplier,
boltServerAddressResolver,
getDomainNameResolver(),
routingSettings.routingTablePurgeDelayMs(),
rediscovery,
clock,
loggingProvider);
}
private BoltConnectionProvider createPooledBoltConnectionProvider(
Config config, EventLoopGroup eventLoopGroup, Clock clock, LoggingProvider loggingProvider) {
var nettyBoltConnectionProvider = createNettyBoltConnectionProvider(eventLoopGroup, clock, loggingProvider);
return new PooledBoltConnectionProvider(
nettyBoltConnectionProvider,
config.maxConnectionPoolSize(),
config.connectionAcquisitionTimeoutMillis(),
config.maxConnectionLifetimeMillis(),
config.idleTimeBeforeConnectionTest(),
clock,
loggingProvider);
}
private BoltConnectionProvider createNettyBoltConnectionProvider(
EventLoopGroup eventLoopGroup, Clock clock, LoggingProvider loggingProvider) {
return new NettyBoltConnectionProvider(
eventLoopGroup, clock, getDomainNameResolver(), localAddress(), loggingProvider);
}
@SuppressWarnings("SameReturnValue")
protected LocalAddress localAddress() {
return null;
}
/**
* Creates new {@link Driver}.
*
* This method is protected only for testing
*/
protected InternalDriver createDriver(
BoltSecurityPlanManager securityPlanManager,
SessionFactory sessionFactory,
MetricsProvider metricsProvider,
Supplier> shutdownSupplier,
Config config) {
return new InternalDriver(
securityPlanManager,
sessionFactory,
metricsProvider,
config.isTelemetryDisabled(),
config.notificationConfig(),
shutdownSupplier,
config.logging());
}
/**
* Creates new {@link Clock}.
*/
protected Clock createClock() {
return Clock.systemUTC();
}
/**
* Creates new {@link SessionFactory}.
*
* This method is protected only for testing
*/
protected SessionFactory createSessionFactory(
BoltSecurityPlanManager securityPlanManager,
BoltConnectionProvider connectionProvider,
RetryLogic retryLogic,
Config config,
AuthTokenManager authTokenManager) {
return new SessionFactoryImpl(securityPlanManager, connectionProvider, retryLogic, config, authTokenManager);
}
/**
* Creates new {@link RetryLogic}.
*
* This method is protected only for testing
*/
protected RetryLogic createRetryLogic(
long maxTransactionRetryTime, EventExecutorGroup eventExecutorGroup, Logging logging) {
return new ExponentialBackoffRetryLogic(maxTransactionRetryTime, eventExecutorGroup, createClock(), logging);
}
/**
* Creates new {@link Bootstrap}.
*
* This method is protected only for testing
*/
protected Bootstrap createBootstrap(int size) {
return BootstrapFactory.newBootstrap(size);
}
/**
* Creates new {@link Bootstrap}.
*
* This method is protected only for testing
*/
protected Bootstrap createBootstrap(EventLoopGroup eventLoopGroup) {
return BootstrapFactory.newBootstrap(eventLoopGroup);
}
/**
* Provides an instance of {@link DomainNameResolver} that is used for domain name resolution.
*
* This method is protected only for testing
*
* @return the instance of {@link DomainNameResolver}.
*/
protected DomainNameResolver getDomainNameResolver() {
return DefaultDomainNameResolver.getInstance();
}
private static void assertNoRoutingContext(URI uri, RoutingSettings routingSettings) {
var routingContext = routingSettings.routingContext();
if (routingContext.isDefined()) {
throw new IllegalArgumentException(NO_ROUTING_CONTEXT_ERROR_MESSAGE + "'" + uri + "'");
}
}
}