org.neo4j.driver.internal.DriverFactory Maven / Gradle / Ivy
Show all versions of neo4j-java-driver Show documentation
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.neo4j.driver.internal;
import static org.neo4j.driver.internal.Scheme.isRoutingScheme;
import static org.neo4j.driver.internal.cluster.IdentityResolver.IDENTITY_RESOLVER;
import static org.neo4j.driver.internal.util.ErrorUtil.addSuppressed;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.net.URI;
import org.neo4j.driver.AuthToken;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Config;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Logger;
import org.neo4j.driver.Logging;
import org.neo4j.driver.MetricsAdapter;
import org.neo4j.driver.internal.async.connection.BootstrapFactory;
import org.neo4j.driver.internal.async.connection.ChannelConnector;
import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl;
import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl;
import org.neo4j.driver.internal.async.pool.PoolSettings;
import org.neo4j.driver.internal.cluster.RoutingContext;
import org.neo4j.driver.internal.cluster.RoutingSettings;
import org.neo4j.driver.internal.cluster.loadbalancing.LeastConnectedLoadBalancingStrategy;
import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancer;
import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancingStrategy;
import org.neo4j.driver.internal.logging.NettyLogging;
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.retry.RetrySettings;
import org.neo4j.driver.internal.security.SecurityPlan;
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.neo4j.driver.internal.spi.ConnectionProvider;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.internal.util.Futures;
import org.neo4j.driver.net.ServerAddressResolver;
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,
AuthToken authToken,
RoutingSettings routingSettings,
RetrySettings retrySettings,
Config config,
SecurityPlan securityPlan) {
return newInstance(uri, authToken, routingSettings, retrySettings, config, null, securityPlan);
}
public final Driver newInstance(
URI uri,
AuthToken authToken,
RoutingSettings routingSettings,
RetrySettings retrySettings,
Config config,
EventLoopGroup eventLoopGroup,
SecurityPlan securityPlan) {
Bootstrap bootstrap;
boolean ownsEventLoopGroup;
if (eventLoopGroup == null) {
bootstrap = createBootstrap(config.eventLoopThreads());
ownsEventLoopGroup = true;
} else {
bootstrap = createBootstrap(eventLoopGroup);
ownsEventLoopGroup = false;
}
authToken = authToken == null ? AuthTokens.none() : authToken;
BoltServerAddress address = new BoltServerAddress(uri);
RoutingSettings newRoutingSettings = routingSettings.withRoutingContext(new RoutingContext(uri));
InternalLoggerFactory.setDefaultFactory(new NettyLogging(config.logging()));
EventExecutorGroup eventExecutorGroup = bootstrap.config().group();
RetryLogic retryLogic = createRetryLogic(retrySettings, eventExecutorGroup, config.logging());
MetricsProvider metricsProvider = getOrCreateMetricsProvider(config, createClock());
ConnectionPool connectionPool = createConnectionPool(
authToken,
securityPlan,
bootstrap,
metricsProvider,
config,
ownsEventLoopGroup,
newRoutingSettings.routingContext());
return createDriver(
uri,
securityPlan,
address,
connectionPool,
eventExecutorGroup,
newRoutingSettings,
retryLogic,
metricsProvider,
config);
}
protected ConnectionPool createConnectionPool(
AuthToken authToken,
SecurityPlan securityPlan,
Bootstrap bootstrap,
MetricsProvider metricsProvider,
Config config,
boolean ownsEventLoopGroup,
RoutingContext routingContext) {
Clock clock = createClock();
ConnectionSettings settings =
new ConnectionSettings(authToken, config.userAgent(), config.connectionTimeoutMillis());
ChannelConnector connector = createConnector(settings, securityPlan, config, clock, routingContext);
PoolSettings poolSettings = new PoolSettings(
config.maxConnectionPoolSize(),
config.connectionAcquisitionTimeoutMillis(),
config.maxConnectionLifetimeMillis(),
config.idleTimeBeforeConnectionTest());
return new ConnectionPoolImpl(
connector,
bootstrap,
poolSettings,
metricsProvider.metricsListener(),
config.logging(),
clock,
ownsEventLoopGroup);
}
protected static MetricsProvider getOrCreateMetricsProvider(Config config, Clock clock) {
MetricsAdapter metricsAdapter = config.metricsAdapter();
// This can actually only happen when someone mocks the config
if (metricsAdapter == null) {
metricsAdapter = config.isMetricsEnabled() ? MetricsAdapter.DEFAULT : MetricsAdapter.DEV_NULL;
}
switch (metricsAdapter) {
case DEV_NULL:
return DevNullMetricsProvider.INSTANCE;
case DEFAULT:
return new InternalMetricsProvider(clock, config.logging());
case MICROMETER:
return MicrometerMetricsProvider.forGlobalRegistry();
}
throw new IllegalStateException("Unknown or unsupported MetricsAdapter: " + metricsAdapter);
}
protected ChannelConnector createConnector(
ConnectionSettings settings,
SecurityPlan securityPlan,
Config config,
Clock clock,
RoutingContext routingContext) {
return new ChannelConnectorImpl(
settings, securityPlan, config.logging(), clock, routingContext, getDomainNameResolver());
}
private InternalDriver createDriver(
URI uri,
SecurityPlan securityPlan,
BoltServerAddress address,
ConnectionPool connectionPool,
EventExecutorGroup eventExecutorGroup,
RoutingSettings routingSettings,
RetryLogic retryLogic,
MetricsProvider metricsProvider,
Config config) {
try {
String scheme = uri.getScheme().toLowerCase();
if (isRoutingScheme(scheme)) {
return createRoutingDriver(
securityPlan,
address,
connectionPool,
eventExecutorGroup,
routingSettings,
retryLogic,
metricsProvider,
config);
} else {
assertNoRoutingContext(uri, routingSettings);
return createDirectDriver(securityPlan, address, connectionPool, retryLogic, metricsProvider, config);
}
} catch (Throwable driverError) {
// we need to close the connection pool if driver creation threw exception
closeConnectionPoolAndSuppressError(connectionPool, driverError);
throw driverError;
}
}
/**
* Creates a new driver for "bolt" scheme.
*
* This method is protected only for testing
*/
protected InternalDriver createDirectDriver(
SecurityPlan securityPlan,
BoltServerAddress address,
ConnectionPool connectionPool,
RetryLogic retryLogic,
MetricsProvider metricsProvider,
Config config) {
ConnectionProvider connectionProvider = new DirectConnectionProvider(address, connectionPool);
SessionFactory sessionFactory = createSessionFactory(connectionProvider, retryLogic, config);
InternalDriver driver = createDriver(securityPlan, sessionFactory, metricsProvider, config);
Logger log = config.logging().getLog(getClass());
log.info("Direct driver instance %s created for server address %s", driver.hashCode(), address);
return driver;
}
/**
* Creates new a new driver for "neo4j" scheme.
*
* This method is protected only for testing
*/
protected InternalDriver createRoutingDriver(
SecurityPlan securityPlan,
BoltServerAddress address,
ConnectionPool connectionPool,
EventExecutorGroup eventExecutorGroup,
RoutingSettings routingSettings,
RetryLogic retryLogic,
MetricsProvider metricsProvider,
Config config) {
ConnectionProvider connectionProvider =
createLoadBalancer(address, connectionPool, eventExecutorGroup, config, routingSettings);
SessionFactory sessionFactory = createSessionFactory(connectionProvider, retryLogic, config);
InternalDriver driver = createDriver(securityPlan, sessionFactory, metricsProvider, config);
Logger log = config.logging().getLog(getClass());
log.info("Routing driver instance %s created for server address %s", driver.hashCode(), address);
return driver;
}
/**
* Creates new {@link Driver}.
*
* This method is protected only for testing
*/
protected InternalDriver createDriver(
SecurityPlan securityPlan, SessionFactory sessionFactory, MetricsProvider metricsProvider, Config config) {
return new InternalDriver(securityPlan, sessionFactory, metricsProvider, config.logging());
}
/**
* Creates new {@link LoadBalancer} for the routing driver.
*
* This method is protected only for testing
*/
protected LoadBalancer createLoadBalancer(
BoltServerAddress address,
ConnectionPool connectionPool,
EventExecutorGroup eventExecutorGroup,
Config config,
RoutingSettings routingSettings) {
LoadBalancingStrategy loadBalancingStrategy =
new LeastConnectedLoadBalancingStrategy(connectionPool, config.logging());
ServerAddressResolver resolver = createResolver(config);
LoadBalancer loadBalancer = new LoadBalancer(
address,
routingSettings,
connectionPool,
eventExecutorGroup,
createClock(),
config.logging(),
loadBalancingStrategy,
resolver,
getDomainNameResolver());
handleNewLoadBalancer(loadBalancer);
return loadBalancer;
}
/**
* Handles new {@link LoadBalancer} instance.
*
* This method is protected for Testkit backend usage only.
*
* @param loadBalancer the new load balancer instance.
*/
protected void handleNewLoadBalancer(LoadBalancer loadBalancer) {}
private static ServerAddressResolver createResolver(Config config) {
ServerAddressResolver configuredResolver = config.resolver();
return configuredResolver != null ? configuredResolver : IDENTITY_RESOLVER;
}
/**
* Creates new {@link Clock}.
*
* This method is protected only for testing
*/
protected Clock createClock() {
return Clock.SYSTEM;
}
/**
* Creates new {@link SessionFactory}.
*
* This method is protected only for testing
*/
protected SessionFactory createSessionFactory(
ConnectionProvider connectionProvider, RetryLogic retryLogic, Config config) {
return new SessionFactoryImpl(connectionProvider, retryLogic, config);
}
/**
* Creates new {@link RetryLogic}.
*
* This method is protected only for testing
*/
protected RetryLogic createRetryLogic(
RetrySettings settings, EventExecutorGroup eventExecutorGroup, Logging logging) {
return new ExponentialBackoffRetryLogic(settings, 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) {
RoutingContext routingContext = routingSettings.routingContext();
if (routingContext.isDefined()) {
throw new IllegalArgumentException(NO_ROUTING_CONTEXT_ERROR_MESSAGE + "'" + uri + "'");
}
}
private static void closeConnectionPoolAndSuppressError(ConnectionPool connectionPool, Throwable mainError) {
try {
Futures.blockingGet(connectionPool.close());
} catch (Throwable closeError) {
addSuppressed(mainError, closeError);
}
}
}