Skip to content

Commit 6f1bcc4

Browse files
committed
Polish "Add support for Neo4j Java Driver 6.0.0"
See gh-47381
1 parent 2542430 commit 6f1bcc4

File tree

10 files changed

+155
-64
lines changed

10 files changed

+155
-64
lines changed

documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,13 @@ management:
10421042

10431043

10441044

1045+
[[actuator.metrics.supported.neo4j]]
1046+
=== Neo4j Metrics
1047+
1048+
Auto-configuration registers a javadoc:org.neo4j.driver.observation.micrometer.MicrometerObservationProvider[] for the auto-configured javadoc:org.neo4j.driver.Driver[].
1049+
1050+
1051+
10451052
[[actuator.metrics.supported.jetty]]
10461053
=== Jetty Metrics
10471054

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: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@
2121
import java.time.Duration;
2222
import java.util.List;
2323
import java.util.Locale;
24-
import java.util.ServiceLoader;
2524
import java.util.concurrent.TimeUnit;
2625

27-
import io.micrometer.observation.ObservationRegistry;
2826
import org.jspecify.annotations.Nullable;
29-
import org.neo4j.bolt.connection.BoltConnectionProviderFactory;
3027
import org.neo4j.driver.AuthToken;
3128
import org.neo4j.driver.AuthTokenManager;
3229
import org.neo4j.driver.AuthTokens;
@@ -35,7 +32,6 @@
3532
import org.neo4j.driver.Driver;
3633
import org.neo4j.driver.GraphDatabase;
3734
import org.neo4j.driver.internal.Scheme;
38-
import org.neo4j.driver.observation.micrometer.MicrometerObservationProvider;
3935

4036
import org.springframework.beans.factory.ObjectProvider;
4137
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -48,7 +44,6 @@
4844
import org.springframework.boot.neo4j.autoconfigure.Neo4jProperties.Pool;
4945
import org.springframework.boot.neo4j.autoconfigure.Neo4jProperties.Security;
5046
import org.springframework.context.annotation.Bean;
51-
import org.springframework.core.env.Environment;
5247
import org.springframework.util.Assert;
5348
import org.springframework.util.StringUtils;
5449

@@ -67,20 +62,6 @@
6762
@EnableConfigurationProperties(Neo4jProperties.class)
6863
public final class Neo4jAutoConfiguration {
6964

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-
8465
@Bean
8566
@ConditionalOnMissingBean(Neo4jConnectionDetails.class)
8667
PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties,
@@ -90,12 +71,10 @@ PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properti
9071

9172
@Bean
9273
@ConditionalOnMissingBean
93-
Driver neo4jDriver(Neo4jProperties properties, Environment environment, Neo4jConnectionDetails connectionDetails,
94-
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers,
95-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
96-
74+
Driver neo4jDriver(Neo4jProperties properties, Neo4jConnectionDetails connectionDetails,
75+
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
9776
Config config = mapDriverConfig(properties, connectionDetails,
98-
configBuilderCustomizers.orderedStream().toList(), observationRegistryProvider);
77+
configBuilderCustomizers.orderedStream().toList());
9978
AuthTokenManager authTokenManager = connectionDetails.getAuthTokenManager();
10079
if (authTokenManager != null) {
10180
return GraphDatabase.driver(connectionDetails.getUri(), authTokenManager, config);
@@ -105,10 +84,9 @@ Driver neo4jDriver(Neo4jProperties properties, Environment environment, Neo4jCon
10584
}
10685

10786
Config mapDriverConfig(Neo4jProperties properties, Neo4jConnectionDetails connectionDetails,
108-
List<ConfigBuilderCustomizer> customizers,
109-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
87+
List<ConfigBuilderCustomizer> customizers) {
11088
Config.ConfigBuilder builder = Config.builder();
111-
configurePoolSettings(builder, properties.getPool(), observationRegistryProvider);
89+
configurePoolSettings(builder, properties.getPool());
11290
URI uri = connectionDetails.getUri();
11391
String scheme = (uri != null) ? uri.getScheme() : "bolt";
11492
configureDriverSettings(builder, properties, isSimpleScheme(scheme));
@@ -118,16 +96,10 @@ Config mapDriverConfig(Neo4jProperties properties, Neo4jConnectionDetails connec
11896

11997
private boolean isSimpleScheme(String scheme) {
12098
String lowerCaseScheme = scheme.toLowerCase(Locale.ENGLISH);
121-
if (!ServiceLoader.load(BoltConnectionProviderFactory.class)
122-
.stream()
123-
.anyMatch((p) -> p.get().supports(lowerCaseScheme))) {
124-
throw new IllegalArgumentException(String.format("'%s' is not a supported scheme.", scheme));
125-
}
12699
return !Scheme.isSecurityScheme(lowerCaseScheme);
127100
}
128101

129-
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool,
130-
ObjectProvider<ObservationRegistry> observationRegistryProvider) {
102+
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool) {
131103
if (pool.isLogLeakedSessions()) {
132104
builder.withLeakedSessionsLogging();
133105
}
@@ -139,11 +111,6 @@ private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool,
139111
builder.withMaxConnectionLifetime(pool.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
140112
builder.withConnectionAcquisitionTimeout(pool.getConnectionAcquisitionTimeout().toMillis(),
141113
TimeUnit.MILLISECONDS);
142-
observationRegistryProvider.ifAvailable((orp) -> {
143-
if (pool.isMetricsEnabled() && HAS_DRIVER_METRICS) {
144-
builder.withObservationProvider(MicrometerObservationProvider.builder(orp).build());
145-
}
146-
});
147114
}
148115

149116
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 {
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"
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: 2 additions & 11 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;
@@ -105,7 +102,7 @@ void uriWithInvalidSchemesAreDetected(String invalidScheme) {
105102
this.contextRunner.withPropertyValues("spring.neo4j.uri=" + invalidScheme + "://localhost:4711")
106103
.run((ctx) -> assertThat(ctx).hasFailed()
107104
.getFailure()
108-
.hasMessageContaining("'%s' is not a supported scheme.", invalidScheme));
105+
.hasMessageContaining("Unsupported scheme: %s", invalidScheme));
109106
}
110107

111108
@Test
@@ -320,13 +317,7 @@ void securityWithTrustSystemCertificates() {
320317

321318
private Config mapDriverConfig(Neo4jProperties properties, ConfigBuilderCustomizer... customizers) {
322319
return new Neo4jAutoConfiguration().mapDriverConfig(properties,
323-
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers),
324-
new ObjectProvider<>() {
325-
@Override
326-
public Stream<ObservationRegistry> stream() {
327-
return Stream.empty();
328-
}
329-
});
320+
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers));
330321
}
331322

332323
}
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)