diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
index bf42411621..3898b33774 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
@@ -86,6 +86,7 @@ dependencies {
optional("org.liquibase:liquibase-core")
optional("org.mongodb:mongodb-driver-reactivestreams")
optional("org.mongodb:mongodb-driver-sync")
+ optional("org.neo4j.driver:neo4j-java-driver")
optional("org.springframework:spring-jdbc")
optional("org.springframework:spring-jms")
optional("org.springframework:spring-messaging")
@@ -96,7 +97,6 @@ dependencies {
optional("org.springframework.data:spring-data-couchbase")
optional("org.springframework.data:spring-data-ldap")
optional("org.springframework.data:spring-data-mongodb")
- optional("org.springframework.data:spring-data-neo4j")
optional("org.springframework.data:spring-data-redis")
optional("org.springframework.data:spring-data-elasticsearch")
optional("org.springframework.data:spring-data-solr")
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfiguration.java
deleted file mode 100644
index 17f5f2c838..0000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfiguration.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
-
-import io.micrometer.core.instrument.MeterRegistry;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.neo4j.driver.Driver;
-import org.springframework.boot.actuate.neo4j.Neo4jDriverMetrics;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
-import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.neo4j.Neo4jDriverAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * {@link EnableAutoConfiguration Auto-configuration} for metrics on all available
- * {@link Driver drivers}.
- *
- * The reason we are doing this dance with the manual binding is the fact that this
- * autoconfiguration should work with more than one instance of the driver. If a user has
- * multiple instances configured, than each instance should be bound via the binder to
- * registry. Without that requirement, we could just add a {@link Bean @Bean} of type
- * {@link Neo4jDriverMetrics} to the context and be done.
- *
- * @author Michael J. Simons
- * @since 2.4.0
- */
-@Configuration(proxyBeanMethods = false)
-@AutoConfigureAfter({ MetricsAutoConfiguration.class, Neo4jDriverAutoConfiguration.class,
- SimpleMetricsExportAutoConfiguration.class })
-@ConditionalOnClass({ Driver.class, MeterRegistry.class })
-@ConditionalOnBean({ Driver.class, MeterRegistry.class })
-public class Neo4jDriverMetricsAutoConfiguration {
-
- private static final Log logger = LogFactory.getLog(Neo4jDriverMetricsAutoConfiguration.class);
-
- @Autowired
- public void bindDataSourcesToRegistry(Map drivers, MeterRegistry registry) {
-
- drivers.forEach((name, driver) -> {
- if (!Neo4jDriverMetrics.metricsAreEnabled(driver)) {
- return;
- }
- driver.verifyConnectivityAsync()
- .thenRunAsync(() -> new Neo4jDriverMetrics(name, driver, Collections.emptyList()).bindTo(registry))
- .exceptionally(e -> {
- logger.warn("Could not verify connection for " + driver + " and thus not bind to metrics: "
- + e.getMessage());
- return null;
- });
- });
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfiguration.java
index 48a8ee9382..7730ddf629 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfiguration.java
@@ -16,36 +16,24 @@
package org.springframework.boot.actuate.autoconfigure.neo4j;
-import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
-import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfiguration;
+import org.neo4j.driver.Driver;
+
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
-import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
-import org.springframework.boot.actuate.health.Health;
-import org.springframework.boot.actuate.health.HealthContributor;
-import org.springframework.boot.actuate.health.ReactiveHealthContributor;
+import org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthContributorConfigurations.Neo4jConfiguration;
+import org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthContributorConfigurations.Neo4jReactiveConfiguration;
import org.springframework.boot.actuate.neo4j.Neo4jHealthIndicator;
import org.springframework.boot.actuate.neo4j.Neo4jReactiveHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.neo4j.Neo4jDriverAutoConfiguration;
-import org.springframework.context.annotation.Bean;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
-import reactor.core.publisher.Flux;
-
-import java.util.Map;
-
-import org.neo4j.driver.Driver;
+import org.springframework.context.annotation.Import;
/**
- * {@link EnableAutoConfiguration Auto-configuration} for {@link Neo4jHealthIndicator}.
- * The auto-configuration here is responsible for both imperative and reactive health
- * checks. The reactive health check has precedence over the imperative one.
+ * {@link EnableAutoConfiguration Auto-configuration} for
+ * {@link Neo4jReactiveHealthIndicator} and {@link Neo4jHealthIndicator}.
*
* @author Eric Spiegelberg
* @author Stephane Nicoll
@@ -53,40 +41,11 @@ import org.neo4j.driver.Driver;
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
-@ConditionalOnClass({ Driver.class, Health.class })
+@ConditionalOnClass(Driver.class)
@ConditionalOnBean(Driver.class)
@ConditionalOnEnabledHealthIndicator("neo4j")
-@AutoConfigureBefore(HealthContributorAutoConfiguration.class)
-@AutoConfigureAfter({ Neo4jDriverAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
+@AutoConfigureAfter(Neo4jAutoConfiguration.class)
+@Import({ Neo4jReactiveConfiguration.class, Neo4jConfiguration.class })
public class Neo4jHealthContributorAutoConfiguration {
- @Configuration(proxyBeanMethods = false)
- @Order(-20)
- static class Neo4jHealthIndicatorConfiguration
- extends CompositeHealthContributorConfiguration {
-
- @Bean
- // If Neo4jReactiveHealthIndicatorConfiguration kicked in, don't add the
- // imperative version as well
- @ConditionalOnMissingBean(name = "neo4jHealthContributor")
- public HealthContributor neo4jHealthContributor(Map drivers) {
- return createContributor(drivers);
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass({ Flux.class })
- @Order(-30)
- static class Neo4jReactiveHealthIndicatorConfiguration
- extends CompositeReactiveHealthContributorConfiguration {
-
- @Bean
- @ConditionalOnMissingBean(name = "neo4jHealthContributor")
- public ReactiveHealthContributor neo4jHealthContributor(Map drivers) {
- return createComposite(drivers);
- }
-
- }
-
}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorConfigurations.java
new file mode 100644
index 0000000000..e53e14d27c
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorConfigurations.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
+
+import java.util.Map;
+
+import org.neo4j.driver.Driver;
+import reactor.core.publisher.Flux;
+
+import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
+import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfiguration;
+import org.springframework.boot.actuate.health.HealthContributor;
+import org.springframework.boot.actuate.health.ReactiveHealthContributor;
+import org.springframework.boot.actuate.neo4j.Neo4jHealthIndicator;
+import org.springframework.boot.actuate.neo4j.Neo4jReactiveHealthIndicator;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Health contributor options for Neo4j.
+ *
+ * @author Michael J. Simons
+ * @author Stephane Nicoll
+ */
+class Neo4jHealthContributorConfigurations {
+
+ @Configuration(proxyBeanMethods = false)
+ static class Neo4jConfiguration extends CompositeHealthContributorConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(name = { "neo4jHealthIndicator", "neo4jHealthContributor" })
+ HealthContributor neo4jHealthContributor(Map drivers) {
+ return createContributor(drivers);
+ }
+
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(Flux.class)
+ static class Neo4jReactiveConfiguration
+ extends CompositeReactiveHealthContributorConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(name = { "neo4jHealthIndicator", "neo4jHealthContributor" })
+ ReactiveHealthContributor neo4jHealthContributor(Map drivers) {
+ return createContributor(drivers);
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
index c7fdf0c932..5de0389b31 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
@@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoCo
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.util.ApplicationContextTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
@@ -69,7 +70,7 @@ class SpringApplicationHierarchyTests {
@EnableAutoConfiguration(exclude = { ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class, CassandraAutoConfiguration.class,
CassandraDataAutoConfiguration.class, MongoDataAutoConfiguration.class,
- MongoReactiveDataAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
+ MongoReactiveDataAutoConfiguration.class, Neo4jAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
Neo4jRepositoriesAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class, FlywayAutoConfiguration.class, MetricsAutoConfiguration.class })
static class Parent {
@@ -80,7 +81,7 @@ class SpringApplicationHierarchyTests {
@EnableAutoConfiguration(exclude = { ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class, CassandraAutoConfiguration.class,
CassandraDataAutoConfiguration.class, MongoDataAutoConfiguration.class,
- MongoReactiveDataAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
+ MongoReactiveDataAutoConfiguration.class, Neo4jAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
Neo4jRepositoriesAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class, FlywayAutoConfiguration.class, MetricsAutoConfiguration.class })
static class Child {
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfigurationTest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfigurationTest.java
deleted file mode 100644
index 02eff7faeb..0000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfigurationTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
-
-import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
-import org.springframework.boot.actuate.neo4j.Neo4jDriverMetrics;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.FilteredClassLoader;
-import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.boot.test.context.runner.ContextConsumer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import org.neo4j.driver.Driver;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jDriverMocks.mockDriverWithMetrics;
-import static org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jDriverMocks.mockDriverWithoutMetrics;
-
-/**
- * @author Michael J. Simons
- */
-class Neo4jDriverMetricsAutoConfigurationTest {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
- AutoConfigurations.of(MetricsAutoConfiguration.class, Neo4jDriverMetricsAutoConfiguration.class));
-
- private final ContextConsumer assertNoInteractionsWithRegistry = ctx -> {
-
- if (ctx.getBeansOfType(MeterRegistry.class).isEmpty()) {
- return;
- }
-
- MeterRegistry mockedRegistry = ctx.getBean(MeterRegistry.class);
- verify(mockedRegistry).config();
- verifyNoMoreInteractions(mockedRegistry);
- };
-
- @Nested
- class NoMatches {
-
- @Test
- void shouldRequireAllNeededClasses() {
- contextRunner.withUserConfiguration(WithMeterRegistry.class)
- .withClassLoader(new FilteredClassLoader(Driver.class)).run(assertNoInteractionsWithRegistry);
-
- contextRunner.withUserConfiguration(WithDriverWithMetrics.class)
- .withClassLoader(new FilteredClassLoader(MeterRegistry.class))
- .run(assertNoInteractionsWithRegistry);
- }
-
- @Test
- void shouldRequireAllNeededBeans() {
- contextRunner.withUserConfiguration(WithDriverWithMetrics.class).run(assertNoInteractionsWithRegistry);
-
- contextRunner.withUserConfiguration(WithMeterRegistry.class).run(assertNoInteractionsWithRegistry);
- }
-
- @Test
- void shouldRequireDriverWithMetrics() {
- contextRunner.withUserConfiguration(WithDriverWithoutMetrics.class, WithMeterRegistry.class)
- .run(assertNoInteractionsWithRegistry);
-
- }
-
- }
-
- @Nested
- class Matches {
-
- @Test
- void shouldRequireDriverWithMetrics() {
- contextRunner.withUserConfiguration(WithDriverWithMetrics.class, WithMeterRegistry.class).run(ctx -> {
-
- // Wait a bit to let the completable future of the test that mocks
- // connectiviy complete.
- Thread.sleep(500L);
-
- MeterRegistry meterRegistry = ctx.getBean(MeterRegistry.class);
- assertThat(meterRegistry.getMeters()).extracting(m -> m.getId().getName())
- .filteredOn(s -> s.startsWith(Neo4jDriverMetrics.PREFIX)).isNotEmpty();
- });
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- static class WithDriverWithMetrics {
-
- @Bean
- Driver driver() {
- return mockDriverWithMetrics();
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- static class WithDriverWithoutMetrics {
-
- @Bean
- Driver driver() {
- return mockDriverWithoutMetrics();
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- static class WithMeterRegistry {
-
- @Bean
- MeterRegistry meterRegistry() {
-
- MeterRegistry meterRegistry = spy(SimpleMeterRegistry.class);
- return meterRegistry;
- }
-
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMocks.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMocks.java
deleted file mode 100644
index e8331e2e0b..0000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMocks.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.autoconfigure.neo4j;
-
-import java.util.Collections;
-import java.util.concurrent.CompletableFuture;
-
-import org.neo4j.driver.ConnectionPoolMetrics;
-import org.neo4j.driver.Driver;
-import org.neo4j.driver.Metrics;
-import org.neo4j.driver.exceptions.ClientException;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * Some predefined mocks, only to be used internally for tests.
- *
- * @author Michael J. Simons
- */
-final class Neo4jDriverMocks {
-
- public static Driver mockDriverWithMetrics() {
- ConnectionPoolMetrics p1 = mock(ConnectionPoolMetrics.class);
- when(p1.id()).thenReturn("p1");
-
- Metrics metrics = mock(Metrics.class);
- when(metrics.connectionPoolMetrics()).thenReturn(Collections.singletonList(p1));
-
- Driver driver = mock(Driver.class);
- when(driver.metrics()).thenReturn(metrics);
-
- when(driver.verifyConnectivityAsync()).thenReturn(CompletableFuture.completedFuture(null));
-
- return driver;
- }
-
- public static Driver mockDriverWithoutMetrics() {
-
- Driver driver = mock(Driver.class);
- when(driver.metrics()).thenThrow(ClientException.class);
- return driver;
- }
-
- private Neo4jDriverMocks() {
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfigurationTests.java
index b2f6976bc3..8e18f2e380 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfigurationTests.java
@@ -1,14 +1,11 @@
/*
- * Copyright (c) 2019-2020 "Neo4j,"
- * Neo4j Sweden AB [https://neo4j.com]
- *
- * This file is part of Neo4j.
+ * Copyright 2012-2020 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
+ * 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,
@@ -18,14 +15,14 @@
*/
package org.springframework.boot.actuate.autoconfigure.neo4j;
-import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Driver;
+import reactor.core.publisher.Flux;
+
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
-import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.PingHealthIndicator;
import org.springframework.boot.actuate.neo4j.Neo4jHealthIndicator;
import org.springframework.boot.actuate.neo4j.Neo4jReactiveHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@@ -33,14 +30,10 @@ import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import reactor.core.publisher.Flux;
-
-import org.neo4j.driver.Driver;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
-import static org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jDriverMocks.mockDriverWithoutMetrics;
/**
* Tests for {@link Neo4jHealthContributorAutoConfiguration}.
@@ -55,106 +48,61 @@ class Neo4jHealthContributorAutoConfigurationTests {
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
Neo4jHealthContributorAutoConfiguration.class));
- @Nested
- class NoMatches {
-
- @Test
- void shouldRespectManagementDecisions() {
- contextRunner.withUserConfiguration(WithDriver.class)
- .withPropertyValues("management.health.neo4j.enabled=false")
- .run(ctx -> assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class)
- .hasSingleBean(PingHealthIndicator.class));
- }
-
- @Test
- void shouldRequireHealthClass() {
- contextRunner.withUserConfiguration(WithDriver.class).withClassLoader(new FilteredClassLoader(Health.class))
- .run(ctx -> assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
- }
-
- @Test
- void shouldRequireDriverClass() {
- contextRunner.withUserConfiguration(WithDriver.class).withClassLoader(new FilteredClassLoader(Driver.class))
- .run(ctx -> assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
- }
-
- @Test
- void shouldRequireDriverBean() {
- contextRunner.run(ctx -> assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
- }
-
- @Test
- void shouldRequireHealthIndicatorClasses() {
- contextRunner.withUserConfiguration(WithDriver.class)
- .withClassLoader(new FilteredClassLoader(Health.class, Flux.class))
- .run(ctx -> assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
-
- contextRunner.withUserConfiguration(WithDriver.class).withClassLoader(new FilteredClassLoader(Flux.class))
- .run(ctx -> assertThat(ctx).hasSingleBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
- }
-
- @Test
- void defaultIndicatorCanBeReplaced() {
- contextRunner.withUserConfiguration(WithDriver.class, WithCustomIndicator.class).run((context) -> {
- assertThat(context).hasBean("neo4jHealthIndicator");
- Health health = context.getBean("neo4jHealthIndicator", HealthIndicator.class).health();
- assertThat(health.getDetails()).containsOnly(entry("test", true));
- });
- }
-
- }
-
- @Nested
- class Matches {
-
- @Test
- void shouldCreateHealthIndicator() {
- contextRunner.withUserConfiguration(WithDriver.class).withClassLoader(new FilteredClassLoader(Flux.class))
- .run(ctx -> assertThat(ctx).hasSingleBean(Neo4jHealthIndicator.class)
- .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
- }
-
+ @Test
+ void runShouldCreateHealthIndicator() {
+ this.contextRunner.withUserConfiguration(Neo4jConfiguration.class).run((context) -> assertThat(context)
+ .hasSingleBean(Neo4jReactiveHealthIndicator.class).doesNotHaveBean(Neo4jHealthIndicator.class));
}
@Test
- void reactiveHealthCheckShouldHavePrecedence() {
- contextRunner.withUserConfiguration(WithDriver.class).run(ctx -> {
- assertThat(ctx).doesNotHaveBean(Neo4jHealthIndicator.class).hasBean("neo4jHealthContributor");
+ void runWithoutReactorShouldCreateHealthIndicator() {
+ this.contextRunner.withUserConfiguration(Neo4jConfiguration.class)
+ .withClassLoader(new FilteredClassLoader(Flux.class)).run((context) -> assertThat(context)
+ .hasSingleBean(Neo4jHealthIndicator.class).doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
+ }
- assertThat(ctx).getBean("neo4jHealthContributor").isInstanceOf(CompositeReactiveHealthContributor.class)
- .satisfies(o -> {
- CompositeReactiveHealthContributor chc = (CompositeReactiveHealthContributor) o;
- assertThat(chc.getContributor("driver")).isInstanceOf(Neo4jReactiveHealthIndicator.class);
- });
- }
+ @Test
+ void runWhenDisabledShouldNotCreateIndicator() {
+ this.contextRunner.withUserConfiguration(Neo4jConfiguration.class)
+ .withPropertyValues("management.health.neo4j.enabled=false")
+ .run((context) -> assertThat(context).doesNotHaveBean(Neo4jHealthIndicator.class)
+ .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
+ }
- );
+ @Test
+ void defaultIndicatorCanBeReplaced() {
+ this.contextRunner.withUserConfiguration(Neo4jConfiguration.class, CustomIndicatorConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasBean("neo4jHealthIndicator");
+ Health health = context.getBean("neo4jHealthIndicator", HealthIndicator.class).health();
+ assertThat(health.getDetails()).containsOnly(entry("test", true));
+ });
+ }
+
+ @Test
+ void shouldRequireDriverBean() {
+ this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(Neo4jHealthIndicator.class)
+ .doesNotHaveBean(Neo4jReactiveHealthIndicator.class));
}
@Configuration(proxyBeanMethods = false)
- static class WithDriver {
+ static class Neo4jConfiguration {
@Bean
Driver driver() {
- return mockDriverWithoutMetrics();
+ return mock(Driver.class);
}
}
@Configuration(proxyBeanMethods = false)
- static class WithCustomIndicator {
+ static class CustomIndicatorConfiguration {
@Bean
HealthIndicator neo4jHealthIndicator() {
return new AbstractHealthIndicator() {
- protected void doHealthCheck(Health.Builder builder) throws Exception {
+ protected void doHealthCheck(Health.Builder builder) {
builder.up().withDetail("test", true);
}
};
diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle
index 9f188d3827..8545696e91 100644
--- a/spring-boot-project/spring-boot-actuator/build.gradle
+++ b/spring-boot-project/spring-boot-actuator/build.gradle
@@ -46,6 +46,7 @@ dependencies {
optional("org.liquibase:liquibase-core")
optional("org.mongodb:mongodb-driver-reactivestreams")
optional("org.mongodb:mongodb-driver-sync")
+ optional("org.neo4j.driver:neo4j-java-driver")
optional("org.springframework:spring-jdbc")
optional("org.springframework:spring-messaging")
optional("org.springframework:spring-webflux")
@@ -57,7 +58,6 @@ dependencies {
optional("org.springframework.data:spring-data-elasticsearch")
optional("org.springframework.data:spring-data-ldap")
optional("org.springframework.data:spring-data-mongodb")
- optional("org.springframework.data:spring-data-neo4j")
optional("org.springframework.data:spring-data-redis")
optional("org.springframework.data:spring-data-rest-webmvc")
optional("org.springframework.data:spring-data-solr")
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetrics.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetrics.java
deleted file mode 100644
index 65ebb6395f..0000000000
--- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetrics.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.neo4j;
-
-import java.util.function.Consumer;
-
-import io.micrometer.core.instrument.FunctionCounter;
-import io.micrometer.core.instrument.Gauge;
-import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.core.instrument.Tag;
-import io.micrometer.core.instrument.Tags;
-import io.micrometer.core.instrument.binder.MeterBinder;
-import org.neo4j.driver.ConnectionPoolMetrics;
-import org.neo4j.driver.Driver;
-import org.neo4j.driver.Metrics;
-import org.neo4j.driver.exceptions.ClientException;
-
-import org.springframework.util.Assert;
-
-/**
- * This is a {@link MeterBinder} that binds all available Neo4j driver metrics to
- * Micrometer.
- *
- * @author Michael J. Simons
- * @since 2.4.0
- */
-public final class Neo4jDriverMetrics implements MeterBinder {
-
- /**
- * Prefixed used for driver metrics.
- */
- public static final String PREFIX = "neo4j.driver.connections";
-
- private static final String BASE_UNIT_CONNECTIONS = "connections";
-
- private final Driver driver;
-
- private final Iterable tags;
-
- public Neo4jDriverMetrics(String name, Driver driver, Iterable tags) {
-
- Assert.notNull(name, "Bean name must not be null");
- Assert.notNull(driver, "Driver must not be null");
- Assert.notNull(tags, "Tags must not be null (but may be empty)");
- this.driver = driver;
- this.tags = Tags.concat(tags, "name", name);
- }
-
- @Override
- public void bindTo(MeterRegistry meterRegistry) {
-
- Metrics metrics = this.driver.metrics();
- metrics.connectionPoolMetrics().forEach(this.getPoolMetricsBinder(meterRegistry));
- }
-
- Consumer getPoolMetricsBinder(MeterRegistry meterRegistry) {
- return (poolMetrics) -> {
- Iterable poolTags = Tags.concat(this.tags, "poolId", poolMetrics.id());
-
- FunctionCounter.builder(PREFIX + ".acquired", poolMetrics, ConnectionPoolMetrics::acquired).tags(poolTags)
- .baseUnit(BASE_UNIT_CONNECTIONS).description("The amount of connections that have been acquired.")
- .register(meterRegistry);
-
- FunctionCounter.builder(PREFIX + ".closed", poolMetrics, ConnectionPoolMetrics::closed).tags(poolTags)
- .baseUnit(BASE_UNIT_CONNECTIONS).description("The amount of connections have been closed.")
- .register(meterRegistry);
-
- FunctionCounter.builder(PREFIX + ".created", poolMetrics, ConnectionPoolMetrics::created).tags(poolTags)
- .baseUnit(BASE_UNIT_CONNECTIONS).description("The amount of connections have ever been created.")
- .register(meterRegistry);
-
- FunctionCounter.builder(PREFIX + ".failedToCreate", poolMetrics, ConnectionPoolMetrics::failedToCreate)
- .tags(poolTags).baseUnit(BASE_UNIT_CONNECTIONS)
- .description("The amount of connections have been failed to create.").register(meterRegistry);
-
- Gauge.builder(PREFIX + ".idle", poolMetrics, ConnectionPoolMetrics::idle).tags(poolTags)
- .baseUnit(BASE_UNIT_CONNECTIONS).description("The amount of connections that are currently idle.")
- .register(meterRegistry);
-
- Gauge.builder(PREFIX + ".inUse", poolMetrics, ConnectionPoolMetrics::inUse).tags(poolTags)
- .baseUnit(BASE_UNIT_CONNECTIONS).description("The amount of connections that are currently in-use.")
- .register(meterRegistry);
-
- FunctionCounter
- .builder(PREFIX + ".timedOutToAcquire", poolMetrics, ConnectionPoolMetrics::timedOutToAcquire)
- .tags(poolTags).baseUnit(BASE_UNIT_CONNECTIONS)
- .description(
- "The amount of failures to acquire a connection from a pool within maximum connection acquisition timeout.")
- .register(meterRegistry);
- };
- }
-
- /**
- * Utility method to check whether driver metrics are enabled without throwing an
- * exception.
- * @param driver the bean to check whether it has metrics enabled or not
- * @return true, if the given bean exposes metrics
- */
- public static boolean metricsAreEnabled(Driver driver) {
-
- try {
- driver.metrics();
- return true;
- }
- catch (ClientException ex) {
- return false;
- }
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthDetailsHandler.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthDetailsHandler.java
new file mode 100644
index 0000000000..5bf908a3cb
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthDetailsHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012-2020 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.actuate.neo4j;
+
+import org.neo4j.driver.summary.DatabaseInfo;
+import org.neo4j.driver.summary.ResultSummary;
+import org.neo4j.driver.summary.ServerInfo;
+
+import org.springframework.boot.actuate.health.Health.Builder;
+import org.springframework.util.StringUtils;
+
+/**
+ * Handle health check details for a Neo4j server.
+ *
+ * @author Stephane Nicoll
+ */
+class Neo4jHealthDetailsHandler {
+
+ /**
+ * Add health details for the specified {@link ResultSummary} and {@code edition}.
+ * @param builder the {@link Builder} to use
+ * @param edition the edition of the server
+ * @param resultSummary server information
+ */
+ void addHealthDetails(Builder builder, String edition, ResultSummary resultSummary) {
+ ServerInfo serverInfo = resultSummary.server();
+ builder.up().withDetail("server", serverInfo.version() + "@" + serverInfo.address()).withDetail("edition",
+ edition);
+ DatabaseInfo databaseInfo = resultSummary.database();
+ if (StringUtils.hasText(databaseInfo.name())) {
+ builder.withDetail("database", databaseInfo.name());
+ }
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java
index 1239594154..782be4cb2b 100644
--- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java
@@ -24,14 +24,11 @@ import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.SessionConfig;
import org.neo4j.driver.exceptions.SessionExpiredException;
-import org.neo4j.driver.summary.DatabaseInfo;
import org.neo4j.driver.summary.ResultSummary;
-import org.neo4j.driver.summary.ServerInfo;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.util.StringUtils;
/**
* {@link HealthIndicator} that tests the status of a Neo4j by executing a Cypher
@@ -51,11 +48,6 @@ public class Neo4jHealthIndicator extends AbstractHealthIndicator {
*/
static final String CYPHER = "CALL dbms.components() YIELD name, edition WHERE name = 'Neo4j Kernel' RETURN edition";
- /**
- * Message indicating that the health check failed.
- */
- static final String MESSAGE_HEALTH_CHECK_FAILED = "Neo4j health check failed";
-
/**
* Message logged before retrying a health check.
*/
@@ -67,65 +59,41 @@ public class Neo4jHealthIndicator extends AbstractHealthIndicator {
static final SessionConfig DEFAULT_SESSION_CONFIG = SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE)
.build();
- /**
- * The driver for this health indicator instance.
- */
private final Driver driver;
+ private final Neo4jHealthDetailsHandler healthDetailsHandler;
+
public Neo4jHealthIndicator(Driver driver) {
- super(MESSAGE_HEALTH_CHECK_FAILED);
+ super("Neo4j health check failed");
this.driver = driver;
+ this.healthDetailsHandler = new Neo4jHealthDetailsHandler();
}
@Override
protected void doHealthCheck(Health.Builder builder) {
-
try {
- ResultSummaryWithEdition resultSummaryWithEdition;
- // Retry one time when the session has been expired
try {
- resultSummaryWithEdition = runHealthCheckQuery();
+ runHealthCheckQuery(builder);
}
- catch (SessionExpiredException sessionExpiredException) {
+ catch (SessionExpiredException ex) {
+ // Retry one time when the session has been expired
logger.warn(MESSAGE_SESSION_EXPIRED);
- resultSummaryWithEdition = runHealthCheckQuery();
+ runHealthCheckQuery(builder);
}
- buildStatusUp(resultSummaryWithEdition, builder);
}
catch (Exception ex) {
builder.down().withException(ex);
}
}
- /**
- * Applies the given {@link ResultSummary} to the {@link Health.Builder builder}
- * without actually calling {@code build}.
- * @param resultSummaryWithEdition the result summary returned by the server
- * @param builder the health builder to be modified
- * @return the modified health builder
- */
- static Health.Builder buildStatusUp(ResultSummaryWithEdition resultSummaryWithEdition, Health.Builder builder) {
- ServerInfo serverInfo = resultSummaryWithEdition.resultSummary.server();
- DatabaseInfo databaseInfo = resultSummaryWithEdition.resultSummary.database();
-
- builder.up().withDetail("server", serverInfo.version() + "@" + serverInfo.address()).withDetail("edition",
- resultSummaryWithEdition.edition);
-
- if (StringUtils.hasText(databaseInfo.name())) {
- builder.withDetail("database", databaseInfo.name());
- }
-
- return builder;
- }
-
- ResultSummaryWithEdition runHealthCheckQuery() {
+ private void runHealthCheckQuery(Health.Builder builder) {
// We use WRITE here to make sure UP is returned for a server that supports
// all possible workloads
try (Session session = this.driver.session(DEFAULT_SESSION_CONFIG)) {
Result result = session.run(CYPHER);
String edition = result.single().get("edition").asString();
ResultSummary resultSummary = result.consume();
- return new ResultSummaryWithEdition(resultSummary, edition);
+ this.healthDetailsHandler.addHealthDetails(builder, edition, resultSummary);
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java
index 535fcc4043..77b8d51bcb 100644
--- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java
@@ -22,7 +22,9 @@ import org.neo4j.driver.Driver;
import org.neo4j.driver.exceptions.SessionExpiredException;
import org.neo4j.driver.reactive.RxResult;
import org.neo4j.driver.reactive.RxSession;
+import org.neo4j.driver.summary.ResultSummary;
import reactor.core.publisher.Mono;
+import reactor.util.function.Tuple2;
import reactor.util.retry.Retry;
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
@@ -34,19 +36,20 @@ import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
* statement and extracting server and database information.
*
* @author Michael J. Simons
+ * @author Stephane Nicoll
* @since 2.4.0
*/
public final class Neo4jReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
private static final Log logger = LogFactory.getLog(Neo4jReactiveHealthIndicator.class);
- /**
- * The driver for this health indicator instance.
- */
private final Driver driver;
+ private final Neo4jHealthDetailsHandler healthDetailsHandler;
+
public Neo4jReactiveHealthIndicator(Driver driver) {
this.driver = driver;
+ this.healthDetailsHandler = new Neo4jHealthDetailsHandler();
}
@Override
@@ -54,17 +57,19 @@ public final class Neo4jReactiveHealthIndicator extends AbstractReactiveHealthIn
return runHealthCheckQuery()
.doOnError(SessionExpiredException.class,
(e) -> logger.warn(Neo4jHealthIndicator.MESSAGE_SESSION_EXPIRED))
- .retryWhen(Retry.max(1).filter(SessionExpiredException.class::isInstance))
- .map((r) -> Neo4jHealthIndicator.buildStatusUp(r, builder).build());
+ .retryWhen(Retry.max(1).filter(SessionExpiredException.class::isInstance)).map((result) -> {
+ this.healthDetailsHandler.addHealthDetails(builder, result.getT1(), result.getT2());
+ return builder.build();
+ });
}
- Mono runHealthCheckQuery() {
+ Mono> runHealthCheckQuery() {
// We use WRITE here to make sure UP is returned for a server that supports
// all possible workloads
return Mono.using(() -> this.driver.rxSession(Neo4jHealthIndicator.DEFAULT_SESSION_CONFIG), (session) -> {
RxResult result = session.run(Neo4jHealthIndicator.CYPHER);
return Mono.from(result.records()).map((record) -> record.get("edition").asString())
- .zipWhen((edition) -> Mono.from(result.consume()), (e, r) -> new ResultSummaryWithEdition(r, e));
+ .zipWhen((edition) -> Mono.from(result.consume()));
}, RxSession::close);
}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/ResultSummaryWithEdition.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/ResultSummaryWithEdition.java
deleted file mode 100644
index 9220a5d525..0000000000
--- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/ResultSummaryWithEdition.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.neo4j;
-
-import org.neo4j.driver.summary.ResultSummary;
-
-/**
- * A holder for a {@link ResultSummary result summary} and database edition.
- *
- * @author Michael J. Simons
- */
-final class ResultSummaryWithEdition {
-
- final ResultSummary resultSummary;
-
- final String edition;
-
- ResultSummaryWithEdition(ResultSummary resultSummary, String edition) {
- this.resultSummary = resultSummary;
- this.edition = edition;
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetricsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetricsTests.java
deleted file mode 100644
index 7b85151d65..0000000000
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetricsTests.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.neo4j;
-
-import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import org.junit.jupiter.api.Test;
-
-import java.util.Collections;
-
-import org.neo4j.driver.Driver;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.boot.actuate.neo4j.Neo4jDriverMetrics.PREFIX;
-import static org.springframework.boot.actuate.neo4j.Neo4jDriverMocks.mockDriverWithMetrics;
-import static org.springframework.boot.actuate.neo4j.Neo4jDriverMocks.mockDriverWithoutMetrics;
-
-/**
- * @author Michael J. Simons
- */
-class Neo4jDriverMetricsTests {
-
- @Test
- void shouldDetectEnabledMetrics() {
-
- Driver driver = mockDriverWithMetrics();
- assertThat(Neo4jDriverMetrics.metricsAreEnabled(driver)).isTrue();
- }
-
- @Test
- void shouldDetectDisabledMetrics() {
-
- Driver driver = mockDriverWithoutMetrics();
- assertThat(Neo4jDriverMetrics.metricsAreEnabled(driver)).isFalse();
- }
-
- @Test
- void shouldRegisterCorrectMeters() {
-
- SimpleMeterRegistry registry = new SimpleMeterRegistry();
- Neo4jDriverMetrics metrics = new Neo4jDriverMetrics("driver", mockDriverWithMetrics(), Collections.emptyList());
- metrics.bindTo(registry);
-
- assertThat(registry.get(PREFIX + ".acquired").functionCounter()).isNotNull();
- assertThat(registry.get(PREFIX + ".closed").functionCounter()).isNotNull();
- assertThat(registry.get(PREFIX + ".created").functionCounter()).isNotNull();
- assertThat(registry.get(PREFIX + ".failedToCreate").functionCounter()).isNotNull();
- assertThat(registry.get(PREFIX + ".idle").gauge()).isNotNull();
- assertThat(registry.get(PREFIX + ".inUse").gauge()).isNotNull();
- assertThat(registry.get(PREFIX + ".timedOutToAcquire").functionCounter()).isNotNull();
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMocks.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMocks.java
deleted file mode 100644
index f6e44d5fdc..0000000000
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMocks.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012-2020 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.actuate.neo4j;
-
-import java.util.Collections;
-import java.util.concurrent.CompletableFuture;
-
-import org.neo4j.driver.ConnectionPoolMetrics;
-import org.neo4j.driver.Driver;
-import org.neo4j.driver.Metrics;
-import org.neo4j.driver.exceptions.ClientException;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * Some predefined mocks, only to be used internally for tests.
- *
- * @author Michael J. Simons
- */
-final class Neo4jDriverMocks {
-
- public static Driver mockDriverWithMetrics() {
- ConnectionPoolMetrics p1 = mock(ConnectionPoolMetrics.class);
- when(p1.id()).thenReturn("p1");
-
- Metrics metrics = mock(Metrics.class);
- when(metrics.connectionPoolMetrics()).thenReturn(Collections.singletonList(p1));
-
- Driver driver = mock(Driver.class);
- when(driver.metrics()).thenReturn(metrics);
-
- when(driver.verifyConnectivityAsync()).thenReturn(CompletableFuture.completedFuture(null));
-
- return driver;
- }
-
- public static Driver mockDriverWithoutMetrics() {
-
- Driver driver = mock(Driver.class);
- when(driver.metrics()).thenThrow(ClientException.class);
- return driver;
- }
-
- private Neo4jDriverMocks() {
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTestBase.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTestBase.java
deleted file mode 100644
index 036ce85254..0000000000
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTestBase.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2019-2020 "Neo4j,"
- * Neo4j Sweden AB [https://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
- *
- * 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.actuate.neo4j;
-
-import org.mockito.Mock;
-
-import org.neo4j.driver.Driver;
-import org.neo4j.driver.Record;
-import org.neo4j.driver.Values;
-import org.neo4j.driver.summary.DatabaseInfo;
-import org.neo4j.driver.summary.ResultSummary;
-import org.neo4j.driver.summary.ServerInfo;
-
-import static org.mockito.Mockito.when;
-
-/**
- * Contains some shared mocks for the health indicator tests.
- *
- * @author Michael J. Simons
- */
-abstract class Neo4jHealthIndicatorTestBase {
-
- @Mock
- protected Driver driver;
-
- @Mock
- protected ResultSummary resultSummary;
-
- @Mock
- protected ServerInfo serverInfo;
-
- @Mock
- protected DatabaseInfo databaseInfo;
-
- @Mock
- protected Record record;
-
- protected void prepareSharedMocks() {
-
- when(serverInfo.version()).thenReturn("4711");
- when(serverInfo.address()).thenReturn("Zu Hause");
- when(databaseInfo.name()).thenReturn("n/a");
- when(resultSummary.server()).thenReturn(serverInfo);
- when(resultSummary.database()).thenReturn(databaseInfo);
- when(record.get("edition")).thenReturn(Values.value("ultimate collectors edition"));
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java
index c81bd28923..a72f1fa8fd 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java
@@ -16,29 +16,29 @@
package org.springframework.boot.actuate.neo4j;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.boot.actuate.health.Health;
-import org.springframework.boot.actuate.health.Status;
-
import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Driver;
+import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.SessionConfig;
import org.neo4j.driver.Values;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.exceptions.SessionExpiredException;
+import org.neo4j.driver.summary.ResultSummary;
+
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.Status;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
/**
* Tests for {@link Neo4jHealthIndicator}.
@@ -47,126 +47,87 @@ import static org.mockito.Mockito.when;
* @author Stephane Nicoll
* @author Michael Simons
*/
-@ExtendWith(MockitoExtension.class)
-class Neo4jHealthIndicatorTests extends Neo4jHealthIndicatorTestBase {
-
- @Mock
- private Session session;
-
- @Mock
- private Result statementResult;
-
- @Test
- void shouldWorkWithoutDatabaseName() {
- when(this.serverInfo.version()).thenReturn("4711");
- when(this.serverInfo.address()).thenReturn("Zu Hause");
- when(this.resultSummary.server()).thenReturn(this.serverInfo);
- when(this.resultSummary.database()).thenReturn(this.databaseInfo);
-
- when(this.databaseInfo.name()).thenReturn(null);
-
- when(record.get("edition")).thenReturn(Values.value("some edition"));
- when(this.statementResult.single()).thenReturn(this.record);
- when(this.statementResult.consume()).thenReturn(this.resultSummary);
- when(this.session.run(anyString())).thenReturn(this.statementResult);
-
- when(this.driver.session(any(SessionConfig.class))).thenReturn(this.session);
-
- Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(this.driver);
- Health health = healthIndicator.health();
- assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
- assertThat(health.getDetails()).doesNotContainKey("database");
- assertThat(health.getDetails()).containsEntry("edition", "some edition");
- }
-
- @Test
- void shouldWorkWithEmptyDatabaseName() {
- when(this.serverInfo.version()).thenReturn("4711");
- when(this.serverInfo.address()).thenReturn("Zu Hause");
- when(this.resultSummary.server()).thenReturn(this.serverInfo);
- when(this.resultSummary.database()).thenReturn(this.databaseInfo);
-
- when(this.databaseInfo.name()).thenReturn("");
-
- when(record.get("edition")).thenReturn(Values.value("some edition"));
- when(this.statementResult.single()).thenReturn(this.record);
- when(this.statementResult.consume()).thenReturn(this.resultSummary);
- when(this.session.run(anyString())).thenReturn(this.statementResult);
-
- when(driver.session(any(SessionConfig.class))).thenReturn(this.session);
-
- Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(this.driver);
- Health health = healthIndicator.health();
- assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
- assertThat(health.getDetails()).doesNotContainKey("database");
- assertThat(health.getDetails()).containsEntry("edition", "some edition");
- }
+class Neo4jHealthIndicatorTests {
@Test
void neo4jIsUp() {
-
- prepareSharedMocks();
-
- when(this.statementResult.single()).thenReturn(this.record);
- when(this.statementResult.consume()).thenReturn(this.resultSummary);
- when(this.session.run(anyString())).thenReturn(this.statementResult);
-
- when(this.driver.session(any(SessionConfig.class))).thenReturn(this.session);
-
- Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(this.driver);
- Health health = healthIndicator.health();
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "test");
+ Driver driver = mockDriver(resultSummary, "ultimate collectors edition");
+ Health health = new Neo4jHealthIndicator(driver).health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
- assertThat(health.getDetails()).containsEntry("database", "n/a");
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
+ assertThat(health.getDetails()).containsEntry("database", "test");
assertThat(health.getDetails()).containsEntry("edition", "ultimate collectors edition");
-
- verify(session).close();
- verifyNoMoreInteractions(this.driver, this.session, this.statementResult, this.resultSummary, this.serverInfo,
- this.databaseInfo);
}
@Test
- void neo4jSessionIsExpiredOnce() {
+ void neo4jIsUpWithoutDatabaseName() {
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", null);
+ Driver driver = mockDriver(resultSummary, "some edition");
+ Health health = new Neo4jHealthIndicator(driver).health();
+ assertThat(health.getStatus()).isEqualTo(Status.UP);
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
+ assertThat(health.getDetails()).doesNotContainKey("database");
+ assertThat(health.getDetails()).containsEntry("edition", "some edition");
+ }
- AtomicInteger cnt = new AtomicInteger(0);
+ @Test
+ void neo4jIsUpWithEmptyDatabaseName() {
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
+ Driver driver = mockDriver(resultSummary, "some edition");
+ Health health = new Neo4jHealthIndicator(driver).health();
+ assertThat(health.getStatus()).isEqualTo(Status.UP);
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
+ assertThat(health.getDetails()).doesNotContainKey("database");
+ assertThat(health.getDetails()).containsEntry("edition", "some edition");
+ }
- prepareSharedMocks();
- when(this.statementResult.single()).thenReturn(this.record);
- when(this.statementResult.consume()).thenReturn(this.resultSummary);
- when(this.session.run(anyString())).thenAnswer(invocation -> {
- if (cnt.compareAndSet(0, 1)) {
+ @Test
+ void neo4jIsUpWithOneSessionExpiredException() {
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
+ Session session = mock(Session.class);
+ Result statementResult = mockStatementResult(resultSummary, "some edition");
+ AtomicInteger count = new AtomicInteger(0);
+ given(session.run(anyString())).will((invocation) -> {
+ if (count.compareAndSet(0, 1)) {
throw new SessionExpiredException("Session expired");
}
- return Neo4jHealthIndicatorTests.this.statementResult;
+ return statementResult;
});
- when(driver.session(any(SessionConfig.class))).thenReturn(this.session);
-
- Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(this.driver);
+ Driver driver = mock(Driver.class);
+ given(driver.session(any(SessionConfig.class))).willReturn(session);
+ Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(driver);
Health health = healthIndicator.health();
-
assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
-
- verify(this.session, times(2)).close();
- verifyNoMoreInteractions(this.driver, this.session, this.statementResult, this.resultSummary, this.serverInfo,
- this.databaseInfo);
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
+ verify(session, times(2)).close();
}
@Test
- void neo4jSessionIsDown() {
-
- when(driver.session(any(SessionConfig.class))).thenThrow(ServiceUnavailableException.class);
-
- Neo4jHealthIndicator healthIndicator = new Neo4jHealthIndicator(driver);
- Health health = healthIndicator.health();
-
+ void neo4jIsDown() {
+ Driver driver = mock(Driver.class);
+ given(driver.session(any(SessionConfig.class))).willThrow(ServiceUnavailableException.class);
+ Health health = new Neo4jHealthIndicator(driver).health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsKeys("error");
+ }
- verifyNoMoreInteractions(this.driver, this.session, this.statementResult, this.resultSummary, this.serverInfo,
- this.databaseInfo);
+ private Result mockStatementResult(ResultSummary resultSummary, String edition) {
+ Record record = mock(Record.class);
+ given(record.get("edition")).willReturn(Values.value(edition));
+ Result statementResult = mock(Result.class);
+ given(statementResult.single()).willReturn(record);
+ given(statementResult.consume()).willReturn(resultSummary);
+ return statementResult;
+ }
+
+ private Driver mockDriver(ResultSummary resultSummary, String edition) {
+ Result statementResult = mockStatementResult(resultSummary, edition);
+ Session session = mock(Session.class);
+ given(session.run(anyString())).willReturn(statementResult);
+ Driver driver = mock(Driver.class);
+ given(driver.session(any(SessionConfig.class))).willReturn(session);
+ return driver;
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java
index 4963b35f95..9379d92d0f 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java
@@ -16,102 +16,101 @@
package org.springframework.boot.actuate.neo4j;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.boot.actuate.health.Status;
-import reactor.core.publisher.Mono;
-import reactor.test.StepVerifier;
-
import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Driver;
+import org.neo4j.driver.Record;
import org.neo4j.driver.SessionConfig;
+import org.neo4j.driver.Values;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.exceptions.SessionExpiredException;
import org.neo4j.driver.reactive.RxResult;
import org.neo4j.driver.reactive.RxSession;
+import org.neo4j.driver.summary.ResultSummary;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import org.springframework.boot.actuate.health.Status;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
/**
+ * Tests for {@link Neo4jReactiveHealthIndicator}.
+ *
* @author Michael J. Simons
+ * @author Stephane Nicoll
*/
-@ExtendWith(MockitoExtension.class)
-class Neo4jReactiveHealthIndicatorTest extends Neo4jHealthIndicatorTestBase {
-
- @Mock
- private RxSession session;
-
- @Mock
- private RxResult statementResult;
+class Neo4jReactiveHealthIndicatorTest {
@Test
void neo4jIsUp() {
-
- prepareSharedMocks();
- when(statementResult.records()).thenReturn(Mono.just(record));
- when(statementResult.consume()).thenReturn(Mono.just(resultSummary));
- when(session.run(anyString())).thenReturn(statementResult);
-
- when(driver.rxSession(any(SessionConfig.class))).thenReturn(session);
-
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "test");
+ Driver driver = mockDriver(resultSummary, "ultimate collectors edition");
Neo4jReactiveHealthIndicator healthIndicator = new Neo4jReactiveHealthIndicator(driver);
- healthIndicator.health().as(StepVerifier::create).consumeNextWith(health -> {
+ healthIndicator.health().as(StepVerifier::create).consumeNextWith((health) -> {
assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
assertThat(health.getDetails()).containsEntry("edition", "ultimate collectors edition");
}).verifyComplete();
-
- verify(session).close();
- verifyNoMoreInteractions(driver, session, statementResult, resultSummary, serverInfo, databaseInfo);
}
@Test
- void neo4jSessionIsExpiredOnce() {
-
- AtomicInteger cnt = new AtomicInteger(0);
-
- prepareSharedMocks();
- when(statementResult.records()).thenReturn(Mono.just(record));
- when(statementResult.consume()).thenReturn(Mono.just(resultSummary));
- when(session.run(anyString())).thenAnswer(invocation -> {
- if (cnt.compareAndSet(0, 1)) {
+ void neo4jIsUpWithOneSessionExpiredException() {
+ ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
+ RxSession session = mock(RxSession.class);
+ RxResult statementResult = mockStatementResult(resultSummary, "some edition");
+ AtomicInteger count = new AtomicInteger(0);
+ given(session.run(anyString())).will((invocation) -> {
+ if (count.compareAndSet(0, 1)) {
throw new SessionExpiredException("Session expired");
}
return statementResult;
});
- when(driver.rxSession(any(SessionConfig.class))).thenReturn(session);
-
+ Driver driver = mock(Driver.class);
+ given(driver.rxSession(any(SessionConfig.class))).willReturn(session);
Neo4jReactiveHealthIndicator healthIndicator = new Neo4jReactiveHealthIndicator(driver);
- healthIndicator.health().as(StepVerifier::create).consumeNextWith(health -> {
+ healthIndicator.health().as(StepVerifier::create).consumeNextWith((health) -> {
assertThat(health.getStatus()).isEqualTo(Status.UP);
- assertThat(health.getDetails()).containsEntry("server", "4711@Zu Hause");
- assertThat(health.getDetails()).containsEntry("edition", "ultimate collectors edition");
+ assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
+ assertThat(health.getDetails()).containsEntry("edition", "some edition");
}).verifyComplete();
-
verify(session, times(2)).close();
- verifyNoMoreInteractions(driver, session, statementResult, resultSummary, serverInfo, databaseInfo);
}
@Test
- void neo4jSessionIsDown() {
-
- when(driver.rxSession(any(SessionConfig.class))).thenThrow(ServiceUnavailableException.class);
-
+ void neo4jIsDown() {
+ Driver driver = mock(Driver.class);
+ given(driver.rxSession(any(SessionConfig.class))).willThrow(ServiceUnavailableException.class);
Neo4jReactiveHealthIndicator healthIndicator = new Neo4jReactiveHealthIndicator(driver);
- healthIndicator.health().as(StepVerifier::create).consumeNextWith(health -> {
+ healthIndicator.health().as(StepVerifier::create).consumeNextWith((health) -> {
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsKeys("error");
}).verifyComplete();
+ }
- verifyNoMoreInteractions(driver, session, statementResult, resultSummary, serverInfo, databaseInfo);
+ private RxResult mockStatementResult(ResultSummary resultSummary, String edition) {
+ Record record = mock(Record.class);
+ given(record.get("edition")).willReturn(Values.value(edition));
+ RxResult statementResult = mock(RxResult.class);
+ given(statementResult.records()).willReturn(Mono.just(record));
+ given(statementResult.consume()).willReturn(Mono.just(resultSummary));
+ return statementResult;
+ }
+
+ private Driver mockDriver(ResultSummary resultSummary, String edition) {
+ RxResult statementResult = mockStatementResult(resultSummary, edition);
+ RxSession session = mock(RxSession.class);
+ given(session.run(anyString())).willReturn(statementResult);
+ Driver driver = mock(Driver.class);
+ given(driver.rxSession(any(SessionConfig.class))).willReturn(session);
+ return driver;
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/ResultSummaryMock.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/ResultSummaryMock.java
new file mode 100644
index 0000000000..4bbd715a0e
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/ResultSummaryMock.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012-2020 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.actuate.neo4j;
+
+import org.neo4j.driver.summary.DatabaseInfo;
+import org.neo4j.driver.summary.ResultSummary;
+import org.neo4j.driver.summary.ServerInfo;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test utility to mock {@link ResultSummary}.
+ *
+ * @author Stephane Nicoll
+ */
+final class ResultSummaryMock {
+
+ private ResultSummaryMock() {
+ }
+
+ static ResultSummary createResultSummary(String serverVersion, String serverAddress, String databaseName) {
+ ServerInfo serverInfo = mock(ServerInfo.class);
+ given(serverInfo.version()).willReturn(serverVersion);
+ given(serverInfo.address()).willReturn(serverAddress);
+ DatabaseInfo databaseInfo = mock(DatabaseInfo.class);
+ given(databaseInfo.name()).willReturn(databaseName);
+ ResultSummary resultSummary = mock(ResultSummary.class);
+ given(resultSummary.server()).willReturn(serverInfo);
+ given(resultSummary.database()).willReturn(databaseInfo);
+ return resultSummary;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc
index 5109c93106..b872595e9b 100644
--- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc
+++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc
@@ -856,6 +856,9 @@ The following `ReactiveHealthIndicators` are auto-configured by Spring Boot when
| {spring-boot-actuator-module-code}/mongo/MongoReactiveHealthIndicator.java[`MongoReactiveHealthIndicator`]
| Checks that a Mongo database is up.
+| {spring-boot-actuator-module-code}/neo4j/Neo4jReactiveHealthIndicator.java[`Neo4jReactiveHealthIndicator`]
+| Checks that a Neo4j database is up.
+
| {spring-boot-actuator-module-code}/redis/RedisReactiveHealthIndicator.java[`RedisReactiveHealthIndicator`]
| Checks that a Redis server is up.
|===