Add support for Neo4j Java Driver 6.0.0
See gh-47381 Signed-off-by: Gerrit Meier <meistermeier@gmail.com>
This commit is contained in:
parent
43b06ca9b9
commit
2542430e5b
|
@ -36,6 +36,7 @@ dependencies {
|
|||
optional(project(":core:spring-boot-testcontainers"))
|
||||
optional(project(":module:spring-boot-health"))
|
||||
optional("org.testcontainers:neo4j")
|
||||
optional("org.neo4j.driver:neo4j-java-driver-observation-micrometer")
|
||||
|
||||
dockerTestImplementation(project(":core:spring-boot-test"))
|
||||
dockerTestImplementation(project(":test-support:spring-boot-docker-test-support"))
|
||||
|
|
|
@ -21,9 +21,12 @@ import java.net.URI;
|
|||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.neo4j.bolt.connection.BoltConnectionProviderFactory;
|
||||
import org.neo4j.driver.AuthToken;
|
||||
import org.neo4j.driver.AuthTokenManager;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
|
@ -32,6 +35,7 @@ import org.neo4j.driver.Config.TrustStrategy;
|
|||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.GraphDatabase;
|
||||
import org.neo4j.driver.internal.Scheme;
|
||||
import org.neo4j.driver.observation.micrometer.MicrometerObservationProvider;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
|
@ -63,6 +67,20 @@ import org.springframework.util.StringUtils;
|
|||
@EnableConfigurationProperties(Neo4jProperties.class)
|
||||
public final class Neo4jAutoConfiguration {
|
||||
|
||||
private static final boolean HAS_DRIVER_METRICS;
|
||||
|
||||
static {
|
||||
boolean metricsObservationProviderFound = true;
|
||||
try {
|
||||
Class.forName("org.neo4j.driver.observation.micrometer.MicrometerObservationProvider", false,
|
||||
Neo4jAutoConfiguration.class.getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
metricsObservationProviderFound = false;
|
||||
}
|
||||
HAS_DRIVER_METRICS = metricsObservationProviderFound;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Neo4jConnectionDetails.class)
|
||||
PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties,
|
||||
|
@ -73,10 +91,11 @@ public final class Neo4jAutoConfiguration {
|
|||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Driver neo4jDriver(Neo4jProperties properties, Environment environment, Neo4jConnectionDetails connectionDetails,
|
||||
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
|
||||
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers,
|
||||
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
|
||||
|
||||
Config config = mapDriverConfig(properties, connectionDetails,
|
||||
configBuilderCustomizers.orderedStream().toList());
|
||||
configBuilderCustomizers.orderedStream().toList(), observationRegistryProvider);
|
||||
AuthTokenManager authTokenManager = connectionDetails.getAuthTokenManager();
|
||||
if (authTokenManager != null) {
|
||||
return GraphDatabase.driver(connectionDetails.getUri(), authTokenManager, config);
|
||||
|
@ -86,29 +105,29 @@ public final class Neo4jAutoConfiguration {
|
|||
}
|
||||
|
||||
Config mapDriverConfig(Neo4jProperties properties, Neo4jConnectionDetails connectionDetails,
|
||||
List<ConfigBuilderCustomizer> customizers) {
|
||||
List<ConfigBuilderCustomizer> customizers,
|
||||
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
|
||||
Config.ConfigBuilder builder = Config.builder();
|
||||
configurePoolSettings(builder, properties.getPool());
|
||||
configurePoolSettings(builder, properties.getPool(), observationRegistryProvider);
|
||||
URI uri = connectionDetails.getUri();
|
||||
String scheme = (uri != null) ? uri.getScheme() : "bolt";
|
||||
configureDriverSettings(builder, properties, isSimpleScheme(scheme));
|
||||
builder.withLogging(new Neo4jSpringJclLogging());
|
||||
customizers.forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private boolean isSimpleScheme(String scheme) {
|
||||
String lowerCaseScheme = scheme.toLowerCase(Locale.ENGLISH);
|
||||
try {
|
||||
Scheme.validateScheme(lowerCaseScheme);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (!ServiceLoader.load(BoltConnectionProviderFactory.class)
|
||||
.stream()
|
||||
.anyMatch((p) -> p.get().supports(lowerCaseScheme))) {
|
||||
throw new IllegalArgumentException(String.format("'%s' is not a supported scheme.", scheme));
|
||||
}
|
||||
return lowerCaseScheme.equals("bolt") || lowerCaseScheme.equals("neo4j");
|
||||
return !Scheme.isSecurityScheme(lowerCaseScheme);
|
||||
}
|
||||
|
||||
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool) {
|
||||
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool,
|
||||
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
|
||||
if (pool.isLogLeakedSessions()) {
|
||||
builder.withLeakedSessionsLogging();
|
||||
}
|
||||
|
@ -120,12 +139,11 @@ public final class Neo4jAutoConfiguration {
|
|||
builder.withMaxConnectionLifetime(pool.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
|
||||
builder.withConnectionAcquisitionTimeout(pool.getConnectionAcquisitionTimeout().toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
if (pool.isMetricsEnabled()) {
|
||||
builder.withDriverMetrics();
|
||||
}
|
||||
else {
|
||||
builder.withoutDriverMetrics();
|
||||
observationRegistryProvider.ifAvailable((orp) -> {
|
||||
if (pool.isMetricsEnabled() && HAS_DRIVER_METRICS) {
|
||||
builder.withObservationProvider(MicrometerObservationProvider.builder(orp).build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void configureDriverSettings(Config.ConfigBuilder builder, Neo4jProperties properties,
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.springframework.boot.neo4j.autoconfigure;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.neo4j.driver.Logger;
|
||||
import org.neo4j.driver.Logging;
|
||||
|
||||
/**
|
||||
* Shim to use Spring JCL implementation, delegating all the hard work of deciding the
|
||||
* underlying system to Spring and Spring Boot.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class Neo4jSpringJclLogging implements Logging {
|
||||
|
||||
/**
|
||||
* This prefix gets added to the log names the driver requests to add some namespace
|
||||
* around it in a bigger application scenario.
|
||||
*/
|
||||
private static final String AUTOMATIC_PREFIX = "org.neo4j.driver.";
|
||||
|
||||
@Override
|
||||
public Logger getLog(String name) {
|
||||
String requestedLog = name;
|
||||
if (!requestedLog.startsWith(AUTOMATIC_PREFIX)) {
|
||||
requestedLog = AUTOMATIC_PREFIX + name;
|
||||
}
|
||||
Log springJclLog = LogFactory.getLog(requestedLog);
|
||||
return new SpringJclLogger(springJclLog);
|
||||
}
|
||||
|
||||
private static final class SpringJclLogger implements Logger {
|
||||
|
||||
private final Log delegate;
|
||||
|
||||
SpringJclLogger(Log delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Throwable cause) {
|
||||
this.delegate.error(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... params) {
|
||||
this.delegate.info(String.format(format, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... params) {
|
||||
this.delegate.warn(String.format(format, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String message, Throwable cause) {
|
||||
this.delegate.warn(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object... params) {
|
||||
if (isDebugEnabled()) {
|
||||
this.delegate.debug(String.format(format, params));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Throwable throwable) {
|
||||
if (isDebugEnabled()) {
|
||||
this.delegate.debug(message, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object... params) {
|
||||
if (isTraceEnabled()) {
|
||||
this.delegate.trace(String.format(format, params));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return this.delegate.isTraceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return this.delegate.isDebugEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -21,7 +21,9 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -32,6 +34,7 @@ import org.neo4j.driver.Config;
|
|||
import org.neo4j.driver.Config.ConfigBuilder;
|
||||
import org.neo4j.driver.Driver;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration.PropertiesNeo4jConnectionDetails;
|
||||
|
@ -217,13 +220,6 @@ class Neo4jAutoConfigurationTests {
|
|||
.withMessage("Cannot specify both username ('Farin') and kerberos ticket ('AABBCCDDEE')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void poolWithMetricsEnabled() {
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
properties.getPool().setMetricsEnabled(true);
|
||||
assertThat(mapDriverConfig(properties).isMetricsEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void poolWithLogLeakedSessions() {
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
|
@ -322,14 +318,15 @@ class Neo4jAutoConfigurationTests {
|
|||
.isEqualTo(Config.TrustStrategy.Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void driverConfigShouldBeConfiguredToUseUseSpringJclLogging() {
|
||||
assertThat(mapDriverConfig(new Neo4jProperties()).logging()).isInstanceOf(Neo4jSpringJclLogging.class);
|
||||
}
|
||||
|
||||
private Config mapDriverConfig(Neo4jProperties properties, ConfigBuilderCustomizer... customizers) {
|
||||
return new Neo4jAutoConfiguration().mapDriverConfig(properties,
|
||||
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers));
|
||||
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers),
|
||||
new ObjectProvider<>() {
|
||||
@Override
|
||||
public Stream<ObservationRegistry> stream() {
|
||||
return Stream.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ class Neo4jPropertiesTests {
|
|||
void poolSettingsHaveConsistentDefaults() {
|
||||
Config defaultConfig = Config.defaultConfig();
|
||||
Pool pool = new Neo4jProperties().getPool();
|
||||
assertThat(pool.isMetricsEnabled()).isEqualTo(defaultConfig.isMetricsEnabled());
|
||||
assertThat(pool.isLogLeakedSessions()).isEqualTo(defaultConfig.logLeakedSessions());
|
||||
assertThat(pool.getMaxConnectionPoolSize()).isEqualTo(defaultConfig.maxConnectionPoolSize());
|
||||
assertDuration(pool.getIdleTimeBeforeConnectionTest(), defaultConfig.idleTimeBeforeConnectionTest());
|
||||
|
|
|
@ -1621,7 +1621,7 @@ bom {
|
|||
]
|
||||
}
|
||||
}
|
||||
library("Neo4j Java Driver", "5.28.9") {
|
||||
library("Neo4j Java Driver", "6.0.0") {
|
||||
alignWith {
|
||||
version {
|
||||
from "org.springframework.data:spring-data-neo4j"
|
||||
|
@ -1630,7 +1630,8 @@ bom {
|
|||
}
|
||||
group("org.neo4j.driver") {
|
||||
modules = [
|
||||
"neo4j-java-driver"
|
||||
"neo4j-java-driver",
|
||||
"neo4j-java-driver-observation-micrometer"
|
||||
]
|
||||
}
|
||||
links {
|
||||
|
|
Loading…
Reference in New Issue