Skip to content

Commit 2ed8a59

Browse files
committed
Update starter for Spring Data Neo4j.
This commit will update the Spring Data Neo4j Spring Boot starter, autoconfig and actuator. It will also bring in autoconfiguration for the Neo4j Java driver to enable users to work with a managed instance of the driver without the high level abstraction of Spring Data.
1 parent 0f264b6 commit 2ed8a59

File tree

70 files changed

+4150
-973
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+4150
-973
lines changed

buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* {@link Task} used to document auto-configuration classes.
3333
*
3434
* @author Andy Wilkinson
35+
* @author Michael J. Simons
3536
*/
3637
public class DocumentConfigurationProperties extends DefaultTask {
3738

@@ -77,8 +78,8 @@ void documentConfigurationProperties() throws IOException {
7778
.addSection("security").withKeyPrefixes("spring.security", "spring.ldap", "spring.session")
7879
.addSection("data-migration").withKeyPrefixes("spring.flyway", "spring.liquibase").addSection("data")
7980
.withKeyPrefixes("spring.couchbase", "spring.elasticsearch", "spring.h2", "spring.influx",
80-
"spring.mongodb", "spring.redis", "spring.dao", "spring.data", "spring.datasource",
81-
"spring.jooq", "spring.jdbc", "spring.jpa", "spring.r2dbc")
81+
"spring.mongodb", "spring.neo4j", "spring.redis", "spring.dao", "spring.data",
82+
"spring.datasource", "spring.jooq", "spring.jdbc", "spring.jpa", "spring.r2dbc")
8283
.addOverride("spring.datasource.dbcp2",
8384
"Commons DBCP2 specific settings bound to an instance of DBCP2's BasicDataSource")
8485
.addOverride("spring.datasource.tomcat",
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
18+
19+
import io.micrometer.core.instrument.MeterRegistry;
20+
21+
import java.util.Collections;
22+
import java.util.Map;
23+
24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
26+
import org.neo4j.driver.Driver;
27+
import org.springframework.boot.actuate.neo4j.Neo4jDriverMetrics;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
30+
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
31+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
32+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
33+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
34+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
35+
import org.springframework.boot.autoconfigure.neo4j.Neo4jDriverAutoConfiguration;
36+
import org.springframework.context.annotation.Bean;
37+
import org.springframework.context.annotation.Configuration;
38+
39+
/**
40+
* {@link EnableAutoConfiguration Auto-configuration} for metrics on all available
41+
* {@link Driver drivers}.
42+
* <p>
43+
* The reason we are doing this dance with the manual binding is the fact that this
44+
* autoconfiguration should work with more than one instance of the driver. If a user has
45+
* multiple instances configured, than each instance should be bound via the binder to
46+
* registry. Without that requirement, we could just add a {@link Bean @Bean} of type
47+
* {@link Neo4jDriverMetrics} to the context and be done.
48+
*
49+
* @author Michael J. Simons
50+
* @since 2.4.0
51+
*/
52+
@Configuration(proxyBeanMethods = false)
53+
@AutoConfigureAfter({ MetricsAutoConfiguration.class, Neo4jDriverAutoConfiguration.class,
54+
SimpleMetricsExportAutoConfiguration.class })
55+
@ConditionalOnClass({ Driver.class, MeterRegistry.class })
56+
@ConditionalOnBean({ Driver.class, MeterRegistry.class })
57+
public class Neo4jDriverMetricsAutoConfiguration {
58+
59+
private static final Log logger = LogFactory.getLog(Neo4jDriverMetricsAutoConfiguration.class);
60+
61+
@Autowired
62+
public void bindDataSourcesToRegistry(Map<String, Driver> drivers, MeterRegistry registry) {
63+
64+
drivers.forEach((name, driver) -> {
65+
if (!Neo4jDriverMetrics.metricsAreEnabled(driver)) {
66+
return;
67+
}
68+
driver.verifyConnectivityAsync()
69+
.thenRunAsync(() -> new Neo4jDriverMetrics(name, driver, Collections.emptyList()).bindTo(registry))
70+
.exceptionally(e -> {
71+
logger.warn("Could not verify connection for " + driver + " and thus not bind to metrics: "
72+
+ e.getMessage());
73+
return null;
74+
});
75+
});
76+
}
77+
78+
}
Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,42 +16,77 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.neo4j;
1818

19-
import java.util.Map;
20-
21-
import org.neo4j.ogm.session.SessionFactory;
22-
2319
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
20+
import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfiguration;
2421
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
22+
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
23+
import org.springframework.boot.actuate.health.Health;
2524
import org.springframework.boot.actuate.health.HealthContributor;
25+
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
2626
import org.springframework.boot.actuate.neo4j.Neo4jHealthIndicator;
27+
import org.springframework.boot.actuate.neo4j.Neo4jReactiveHealthIndicator;
2728
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
29+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2830
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2931
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3032
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3133
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3234
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
35+
import org.springframework.boot.autoconfigure.neo4j.Neo4jDriverAutoConfiguration;
3336
import org.springframework.context.annotation.Bean;
3437
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.core.annotation.Order;
39+
import reactor.core.publisher.Flux;
40+
41+
import java.util.Map;
42+
43+
import org.neo4j.driver.Driver;
3544

3645
/**
3746
* {@link EnableAutoConfiguration Auto-configuration} for {@link Neo4jHealthIndicator}.
47+
* The auto-configuration here is responsible for both imperative and reactive health
48+
* checks. The reactive health check has precedence over the imperative one.
3849
*
3950
* @author Eric Spiegelberg
4051
* @author Stephane Nicoll
52+
* @author Michael J. Simons
4153
* @since 2.0.0
4254
*/
4355
@Configuration(proxyBeanMethods = false)
44-
@ConditionalOnClass(SessionFactory.class)
45-
@ConditionalOnBean(SessionFactory.class)
56+
@ConditionalOnClass({ Driver.class, Health.class })
57+
@ConditionalOnBean(Driver.class)
4658
@ConditionalOnEnabledHealthIndicator("neo4j")
47-
@AutoConfigureAfter(Neo4jDataAutoConfiguration.class)
48-
public class Neo4jHealthContributorAutoConfiguration
49-
extends CompositeHealthContributorConfiguration<Neo4jHealthIndicator, SessionFactory> {
50-
51-
@Bean
52-
@ConditionalOnMissingBean(name = { "neo4jHealthIndicator", "neo4jHealthContributor" })
53-
public HealthContributor neo4jHealthContributor(Map<String, SessionFactory> sessionFactories) {
54-
return createContributor(sessionFactories);
59+
@AutoConfigureBefore(HealthContributorAutoConfiguration.class)
60+
@AutoConfigureAfter({ Neo4jDriverAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
61+
public class Neo4jHealthContributorAutoConfiguration {
62+
63+
@Configuration(proxyBeanMethods = false)
64+
@Order(-20)
65+
static class Neo4jHealthIndicatorConfiguration
66+
extends CompositeHealthContributorConfiguration<Neo4jHealthIndicator, Driver> {
67+
68+
@Bean
69+
// If Neo4jReactiveHealthIndicatorConfiguration kicked in, don't add the
70+
// imperative version as well
71+
@ConditionalOnMissingBean(name = "neo4jHealthContributor")
72+
public HealthContributor neo4jHealthContributor(Map<String, Driver> drivers) {
73+
return createContributor(drivers);
74+
}
75+
76+
}
77+
78+
@Configuration(proxyBeanMethods = false)
79+
@ConditionalOnClass({ Flux.class })
80+
@Order(-30)
81+
static class Neo4jReactiveHealthIndicatorConfiguration
82+
extends CompositeReactiveHealthContributorConfiguration<Neo4jReactiveHealthIndicator, Driver> {
83+
84+
@Bean
85+
@ConditionalOnMissingBean(name = "neo4jHealthContributor")
86+
public ReactiveHealthContributor neo4jHealthContributor(Map<String, Driver> drivers) {
87+
return createComposite(drivers);
88+
}
89+
5590
}
5691

5792
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
18+
19+
import io.micrometer.core.instrument.MeterRegistry;
20+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
21+
import org.junit.jupiter.api.Nested;
22+
import org.junit.jupiter.api.Test;
23+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
24+
import org.springframework.boot.actuate.neo4j.Neo4jDriverMetrics;
25+
import org.springframework.boot.autoconfigure.AutoConfigurations;
26+
import org.springframework.boot.test.context.FilteredClassLoader;
27+
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
28+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
29+
import org.springframework.boot.test.context.runner.ContextConsumer;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
33+
import org.neo4j.driver.Driver;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.mockito.Mockito.spy;
37+
import static org.mockito.Mockito.verify;
38+
import static org.mockito.Mockito.verifyNoMoreInteractions;
39+
import static org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jDriverMocks.mockDriverWithMetrics;
40+
import static org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jDriverMocks.mockDriverWithoutMetrics;
41+
42+
/**
43+
* @author Michael J. Simons
44+
*/
45+
class Neo4jDriverMetricsAutoConfigurationTest {
46+
47+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
48+
AutoConfigurations.of(MetricsAutoConfiguration.class, Neo4jDriverMetricsAutoConfiguration.class));
49+
50+
private final ContextConsumer<AssertableApplicationContext> assertNoInteractionsWithRegistry = ctx -> {
51+
52+
if (ctx.getBeansOfType(MeterRegistry.class).isEmpty()) {
53+
return;
54+
}
55+
56+
MeterRegistry mockedRegistry = ctx.getBean(MeterRegistry.class);
57+
verify(mockedRegistry).config();
58+
verifyNoMoreInteractions(mockedRegistry);
59+
};
60+
61+
@Nested
62+
class NoMatches {
63+
64+
@Test
65+
void shouldRequireAllNeededClasses() {
66+
contextRunner.withUserConfiguration(WithMeterRegistry.class)
67+
.withClassLoader(new FilteredClassLoader(Driver.class)).run(assertNoInteractionsWithRegistry);
68+
69+
contextRunner.withUserConfiguration(WithDriverWithMetrics.class)
70+
.withClassLoader(new FilteredClassLoader(MeterRegistry.class))
71+
.run(assertNoInteractionsWithRegistry);
72+
}
73+
74+
@Test
75+
void shouldRequireAllNeededBeans() {
76+
contextRunner.withUserConfiguration(WithDriverWithMetrics.class).run(assertNoInteractionsWithRegistry);
77+
78+
contextRunner.withUserConfiguration(WithMeterRegistry.class).run(assertNoInteractionsWithRegistry);
79+
}
80+
81+
@Test
82+
void shouldRequireDriverWithMetrics() {
83+
contextRunner.withUserConfiguration(WithDriverWithoutMetrics.class, WithMeterRegistry.class)
84+
.run(assertNoInteractionsWithRegistry);
85+
86+
}
87+
88+
}
89+
90+
@Nested
91+
class Matches {
92+
93+
@Test
94+
void shouldRequireDriverWithMetrics() {
95+
contextRunner.withUserConfiguration(WithDriverWithMetrics.class, WithMeterRegistry.class).run(ctx -> {
96+
97+
// Wait a bit to let the completable future of the test that mocks
98+
// connectiviy complete.
99+
Thread.sleep(500L);
100+
101+
MeterRegistry meterRegistry = ctx.getBean(MeterRegistry.class);
102+
assertThat(meterRegistry.getMeters()).extracting(m -> m.getId().getName())
103+
.filteredOn(s -> s.startsWith(Neo4jDriverMetrics.PREFIX)).isNotEmpty();
104+
});
105+
}
106+
107+
}
108+
109+
@Configuration(proxyBeanMethods = false)
110+
static class WithDriverWithMetrics {
111+
112+
@Bean
113+
Driver driver() {
114+
return mockDriverWithMetrics();
115+
}
116+
117+
}
118+
119+
@Configuration(proxyBeanMethods = false)
120+
static class WithDriverWithoutMetrics {
121+
122+
@Bean
123+
Driver driver() {
124+
return mockDriverWithoutMetrics();
125+
}
126+
127+
}
128+
129+
@Configuration(proxyBeanMethods = false)
130+
static class WithMeterRegistry {
131+
132+
@Bean
133+
MeterRegistry meterRegistry() {
134+
135+
MeterRegistry meterRegistry = spy(SimpleMeterRegistry.class);
136+
return meterRegistry;
137+
}
138+
139+
}
140+
141+
}

0 commit comments

Comments
 (0)