Skip to content

Commit e5d3571

Browse files
committed
Polish "Add support for Neo4j Java Driver 6.0.0"
See spring-projectsgh-47381
1 parent df1c8ce commit e5d3571

File tree

9 files changed

+154
-59
lines changed

9 files changed

+154
-59
lines changed

module/spring-boot-neo4j/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ dependencies {
3535
optional(project(":core:spring-boot-docker-compose"))
3636
optional(project(":core:spring-boot-testcontainers"))
3737
optional(project(":module:spring-boot-health"))
38-
optional("org.testcontainers:neo4j")
38+
optional(project(":module:spring-boot-micrometer-observation"))
3939
optional("org.neo4j.driver:neo4j-java-driver-observation-micrometer")
40+
optional("org.testcontainers:neo4j")
4041

4142
dockerTestImplementation(project(":core:spring-boot-test"))
4243
dockerTestImplementation(project(":test-support:spring-boot-docker-test-support"))
@@ -49,6 +50,7 @@ dependencies {
4950
testImplementation(project(":core:spring-boot-test"))
5051
testImplementation(project(":test-support:spring-boot-test-support"))
5152
testImplementation(testFixtures(project(":core:spring-boot-testcontainers")))
53+
testImplementation("io.micrometer:micrometer-observation-test")
5254
testImplementation("io.projectreactor:reactor-test")
5355

5456
testRuntimeOnly("ch.qos.logback:logback-classic")

module/spring-boot-neo4j/src/main/java/org/springframework/boot/neo4j/autoconfigure/Neo4jAutoConfiguration.java

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.ServiceLoader;
2525
import java.util.concurrent.TimeUnit;
2626

27-
import io.micrometer.observation.ObservationRegistry;
2827
import org.jspecify.annotations.Nullable;
2928
import org.neo4j.bolt.connection.BoltConnectionProviderFactory;
3029
import org.neo4j.driver.AuthToken;
@@ -35,7 +34,6 @@
3534
import org.neo4j.driver.Driver;
3635
import org.neo4j.driver.GraphDatabase;
3736
import org.neo4j.driver.internal.Scheme;
38-
import org.neo4j.driver.observation.micrometer.MicrometerObservationProvider;
3937

4038
import org.springframework.beans.factory.ObjectProvider;
4139
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -48,7 +46,7 @@
4846
import org.springframework.boot.neo4j.autoconfigure.Neo4jProperties.Pool;
4947
import org.springframework.boot.neo4j.autoconfigure.Neo4jProperties.Security;
5048
import org.springframework.context.annotation.Bean;
51-
import org.springframework.core.env.Environment;
49+
import org.springframework.core.io.ResourceLoader;
5250
import org.springframework.util.Assert;
5351
import org.springframework.util.StringUtils;
5452

@@ -67,20 +65,6 @@
6765
@EnableConfigurationProperties(Neo4jProperties.class)
6866
public final class Neo4jAutoConfiguration {
6967

70-
private static final boolean HAS_DRIVER_METRICS;
71-
72-
static {
73-
boolean metricsObservationProviderFound = true;
74-
try {
75-
Class.forName("org.neo4j.driver.observation.micrometer.MicrometerObservationProvider", false,
76-
Neo4jAutoConfiguration.class.getClassLoader());
77-
}
78-
catch (ClassNotFoundException ex) {
79-
metricsObservationProviderFound = false;
80-
}
81-
HAS_DRIVER_METRICS = metricsObservationProviderFound;
82-
}
83-
8468
@Bean
8569
@ConditionalOnMissingBean(Neo4jConnectionDetails.class)
8670
PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties,
@@ -90,12 +74,11 @@ PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properti
9074

9175
@Bean
9276
@ConditionalOnMissingBean
93-
Driver neo4jDriver(Neo4jProperties properties, Environment environment, Neo4jConnectionDetails connectionDetails,
94-
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers,
95-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
96-
77+
Driver neo4jDriver(ResourceLoader resourceLoader, Neo4jProperties properties,
78+
Neo4jConnectionDetails connectionDetails,
79+
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
9780
Config config = mapDriverConfig(properties, connectionDetails,
98-
configBuilderCustomizers.orderedStream().toList(), observationRegistryProvider);
81+
configBuilderCustomizers.orderedStream().toList(), resourceLoader.getClassLoader());
9982
AuthTokenManager authTokenManager = connectionDetails.getAuthTokenManager();
10083
if (authTokenManager != null) {
10184
return GraphDatabase.driver(connectionDetails.getUri(), authTokenManager, config);
@@ -105,29 +88,28 @@ Driver neo4jDriver(Neo4jProperties properties, Environment environment, Neo4jCon
10588
}
10689

10790
Config mapDriverConfig(Neo4jProperties properties, Neo4jConnectionDetails connectionDetails,
108-
List<ConfigBuilderCustomizer> customizers,
109-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
91+
List<ConfigBuilderCustomizer> customizers, @Nullable ClassLoader classLoader) {
11092
Config.ConfigBuilder builder = Config.builder();
111-
configurePoolSettings(builder, properties.getPool(), observationRegistryProvider);
93+
configurePoolSettings(builder, properties.getPool());
11294
URI uri = connectionDetails.getUri();
11395
String scheme = (uri != null) ? uri.getScheme() : "bolt";
114-
configureDriverSettings(builder, properties, isSimpleScheme(scheme));
96+
configureDriverSettings(builder, properties, isSimpleScheme(scheme, classLoader));
11597
customizers.forEach((customizer) -> customizer.customize(builder));
11698
return builder.build();
11799
}
118100

119-
private boolean isSimpleScheme(String scheme) {
101+
private boolean isSimpleScheme(String scheme, @Nullable ClassLoader classLoader) {
120102
String lowerCaseScheme = scheme.toLowerCase(Locale.ENGLISH);
121-
if (!ServiceLoader.load(BoltConnectionProviderFactory.class)
103+
boolean match = ServiceLoader.load(BoltConnectionProviderFactory.class, classLoader)
122104
.stream()
123-
.anyMatch((p) -> p.get().supports(lowerCaseScheme))) {
105+
.anyMatch((factory) -> factory.get().supports(scheme));
106+
if (!match) {
124107
throw new IllegalArgumentException(String.format("'%s' is not a supported scheme.", scheme));
125108
}
126109
return !Scheme.isSecurityScheme(lowerCaseScheme);
127110
}
128111

129-
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool,
130-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
112+
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool) {
131113
if (pool.isLogLeakedSessions()) {
132114
builder.withLeakedSessionsLogging();
133115
}
@@ -139,11 +121,6 @@ private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool,
139121
builder.withMaxConnectionLifetime(pool.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
140122
builder.withConnectionAcquisitionTimeout(pool.getConnectionAcquisitionTimeout().toMillis(),
141123
TimeUnit.MILLISECONDS);
142-
observationRegistryProvider.ifAvailable((orp) -> {
143-
if (pool.isMetricsEnabled() && HAS_DRIVER_METRICS) {
144-
builder.withObservationProvider(MicrometerObservationProvider.builder(orp).build());
145-
}
146-
});
147124
}
148125

149126
private void configureDriverSettings(Config.ConfigBuilder builder, Neo4jProperties properties,

module/spring-boot-neo4j/src/main/java/org/springframework/boot/neo4j/autoconfigure/Neo4jProperties.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,6 @@ public void setKerberosTicket(@Nullable String kerberosTicket) {
150150

151151
public static class Pool {
152152

153-
/**
154-
* Whether to enable metrics.
155-
*/
156-
private boolean metricsEnabled = false;
157-
158153
/**
159154
* Whether to log leaked sessions.
160155
*/
@@ -223,14 +218,6 @@ public void setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeou
223218
this.connectionAcquisitionTimeout = connectionAcquisitionTimeout;
224219
}
225220

226-
public boolean isMetricsEnabled() {
227-
return this.metricsEnabled;
228-
}
229-
230-
public void setMetricsEnabled(boolean metricsEnabled) {
231-
this.metricsEnabled = metricsEnabled;
232-
}
233-
234221
}
235222

236223
public static class Security {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.neo4j.autoconfigure.observation;
18+
19+
import io.micrometer.observation.Observation;
20+
import io.micrometer.observation.ObservationRegistry;
21+
import org.neo4j.driver.Config.ConfigBuilder;
22+
import org.neo4j.driver.observation.micrometer.MicrometerObservationProvider;
23+
24+
import org.springframework.boot.autoconfigure.AutoConfiguration;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27+
import org.springframework.boot.neo4j.autoconfigure.ConfigBuilderCustomizer;
28+
import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
29+
import org.springframework.context.annotation.Bean;
30+
31+
/**
32+
* Auto-configuration for Neo4j observability.
33+
*
34+
* @author Stephane Nicoll
35+
* @since 4.0.0
36+
*/
37+
@AutoConfiguration(before = Neo4jAutoConfiguration.class,
38+
afterName = "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration")
39+
@ConditionalOnBean(ObservationRegistry.class)
40+
@ConditionalOnClass({ ConfigBuilder.class, MicrometerObservationProvider.class, Observation.class })
41+
public final class Neo4jObservationAutoConfiguration {
42+
43+
@Bean
44+
@ConditionalOnBean(ObservationRegistry.class)
45+
ConfigBuilderCustomizer neo4jObservationCustomizer(ObservationRegistry registry) {
46+
return (builder) -> builder.withObservationProvider(MicrometerObservationProvider.builder(registry).build());
47+
}
48+
49+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Auto-configuration for Neo4j observation.
19+
*/
20+
@NullMarked
21+
package org.springframework.boot.neo4j.autoconfigure.observation;
22+
23+
import org.jspecify.annotations.NullMarked;

module/spring-boot-neo4j/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
"description": "Whether to enable Neo4j health check.",
88
"defaultValue": true
99
},
10+
{
11+
"name": "spring.neo4j.pool.metrics-enabled",
12+
"type": "java.lang.Boolean",
13+
"deprecation": {
14+
"reason": "Use 'management.metrics.enable' to restrict certain metrics.",
15+
"level": "error"
16+
}
17+
},
1018
{
1119
"name": "spring.neo4j.uri",
1220
"defaultValue": "bolt://localhost:7687"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration
22
org.springframework.boot.neo4j.autoconfigure.health.Neo4jHealthContributorAutoConfiguration
3+
org.springframework.boot.neo4j.autoconfigure.observation.Neo4jObservationAutoConfiguration

module/spring-boot-neo4j/src/test/java/org/springframework/boot/neo4j/autoconfigure/Neo4jAutoConfigurationTests.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
import java.net.URI;
2222
import java.time.Duration;
2323
import java.util.Arrays;
24-
import java.util.stream.Stream;
2524

26-
import io.micrometer.observation.ObservationRegistry;
2725
import org.junit.jupiter.api.Test;
2826
import org.junit.jupiter.api.io.TempDir;
2927
import org.junit.jupiter.params.ParameterizedTest;
@@ -34,7 +32,6 @@
3432
import org.neo4j.driver.Config.ConfigBuilder;
3533
import org.neo4j.driver.Driver;
3634

37-
import org.springframework.beans.factory.ObjectProvider;
3835
import org.springframework.boot.autoconfigure.AutoConfigurations;
3936
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
4037
import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration.PropertiesNeo4jConnectionDetails;
@@ -321,12 +318,7 @@ void securityWithTrustSystemCertificates() {
321318
private Config mapDriverConfig(Neo4jProperties properties, ConfigBuilderCustomizer... customizers) {
322319
return new Neo4jAutoConfiguration().mapDriverConfig(properties,
323320
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers),
324-
new ObjectProvider<>() {
325-
@Override
326-
public Stream<ObservationRegistry> stream() {
327-
return Stream.empty();
328-
}
329-
});
321+
getClass().getClassLoader());
330322
}
331323

332324
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.neo4j.autoconfigure.observation;
18+
19+
import io.micrometer.observation.tck.TestObservationRegistry;
20+
import org.junit.jupiter.api.Test;
21+
import org.neo4j.driver.Driver;
22+
import org.neo4j.driver.internal.observation.NoopObservationProvider;
23+
import org.neo4j.driver.observation.micrometer.MicrometerObservationProvider;
24+
25+
import org.springframework.boot.autoconfigure.AutoConfigurations;
26+
import org.springframework.boot.neo4j.autoconfigure.Neo4jAutoConfiguration;
27+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Tests for {@link Neo4jObservationAutoConfiguration}.
33+
*
34+
* @author Stephane Nicoll
35+
*/
36+
class Neo4jObservationAutoConfigurationTests {
37+
38+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
39+
.withConfiguration(AutoConfigurations.of(Neo4jObservationAutoConfiguration.class));
40+
41+
@Test
42+
void whenThereIsAnObservationRegistryThenMicrometerObservationProviderIsAdded() {
43+
this.contextRunner.withBean(TestObservationRegistry.class, TestObservationRegistry::create)
44+
.withConfiguration(AutoConfigurations.of(Neo4jAutoConfiguration.class))
45+
.run((context) -> assertThat(context.getBean(Driver.class)).extracting("observationProvider")
46+
.isInstanceOf(MicrometerObservationProvider.class));
47+
}
48+
49+
@Test
50+
void whenThereIsNoObservationRegistryThenConfigBuilderCustomizationBacksOff() {
51+
this.contextRunner.withConfiguration(AutoConfigurations.of(Neo4jAutoConfiguration.class))
52+
.run((context) -> assertThat(context.getBean(Driver.class)).extracting("observationProvider")
53+
.isInstanceOf(NoopObservationProvider.class));
54+
}
55+
56+
}

0 commit comments

Comments
 (0)