Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
c6fde1e4
Commit
c6fde1e4
authored
Jul 28, 2020
by
Stephane Nicoll
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Polish "Update Neo4j health check to use the Neo4j Driver"
See gh-22302
parent
c5a7815e
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
357 additions
and
804 deletions
+357
-804
build.gradle
...t-project/spring-boot-actuator-autoconfigure/build.gradle
+1
-1
Neo4jHealthContributorAutoConfiguration.java
...figure/neo4j/Neo4jHealthContributorAutoConfiguration.java
+11
-52
Neo4jHealthContributorConfigurations.java
...configure/neo4j/Neo4jHealthContributorConfigurations.java
+67
-0
SpringApplicationHierarchyTests.java
...ctuate/autoconfigure/SpringApplicationHierarchyTests.java
+3
-2
Neo4jDriverMetricsAutoConfigurationTest.java
...figure/neo4j/Neo4jDriverMetricsAutoConfigurationTest.java
+0
-141
Neo4jDriverMocks.java
...rk/boot/actuate/autoconfigure/neo4j/Neo4jDriverMocks.java
+0
-62
Neo4jHealthContributorAutoConfigurationTests.java
...e/neo4j/Neo4jHealthContributorAutoConfigurationTests.java
+37
-89
build.gradle
spring-boot-project/spring-boot-actuator/build.gradle
+1
-1
Neo4jDriverMetrics.java
...pringframework/boot/actuate/neo4j/Neo4jDriverMetrics.java
+0
-124
Neo4jHealthDetailsHandler.java
...amework/boot/actuate/neo4j/Neo4jHealthDetailsHandler.java
+49
-0
Neo4jHealthIndicator.java
...ingframework/boot/actuate/neo4j/Neo4jHealthIndicator.java
+10
-42
Neo4jReactiveHealthIndicator.java
...work/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java
+12
-7
Neo4jDriverMetricsTests.java
...framework/boot/actuate/neo4j/Neo4jDriverMetricsTests.java
+0
-66
Neo4jDriverMocks.java
.../springframework/boot/actuate/neo4j/Neo4jDriverMocks.java
+0
-62
Neo4jHealthIndicatorTests.java
...amework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java
+63
-102
Neo4jReactiveHealthIndicatorTest.java
.../boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java
+52
-53
ResultSummaryMock.java
...springframework/boot/actuate/neo4j/ResultSummaryMock.java
+48
-0
production-ready-features.adoc
...oot-docs/src/docs/asciidoc/production-ready-features.adoc
+3
-0
No files found.
spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
View file @
c6fde1e4
...
...
@@ -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"
)
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfiguration.java
View file @
c6fde1e4
...
...
@@ -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"
)
@AutoConfigure
Before
(
HealthContributor
AutoConfiguration
.
class
)
@
AutoConfigureAfter
({
Neo4jDriverAutoConfiguration
.
class
,
Neo4jDataAuto
Configuration
.
class
})
@AutoConfigure
After
(
Neo4j
AutoConfiguration
.
class
)
@
Import
({
Neo4jReactiveConfiguration
.
class
,
Neo4j
Configuration
.
class
})
public
class
Neo4jHealthContributorAutoConfiguration
{
@Configuration
(
proxyBeanMethods
=
false
)
@Order
(-
20
)
static
class
Neo4jHealthIndicatorConfiguration
extends
CompositeHealthContributorConfiguration
<
Neo4jHealthIndicator
,
Driver
>
{
@Bean
// If Neo4jReactiveHealthIndicatorConfiguration kicked in, don't add the
// imperative version as well
@ConditionalOnMissingBean
(
name
=
"neo4jHealthContributor"
)
public
HealthContributor
neo4jHealthContributor
(
Map
<
String
,
Driver
>
drivers
)
{
return
createContributor
(
drivers
);
}
}
@Configuration
(
proxyBeanMethods
=
false
)
@ConditionalOnClass
({
Flux
.
class
})
@Order
(-
30
)
static
class
Neo4jReactiveHealthIndicatorConfiguration
extends
CompositeReactiveHealthContributorConfiguration
<
Neo4jReactiveHealthIndicator
,
Driver
>
{
@Bean
@ConditionalOnMissingBean
(
name
=
"neo4jHealthContributor"
)
public
ReactiveHealthContributor
neo4jHealthContributor
(
Map
<
String
,
Driver
>
drivers
)
{
return
createComposite
(
drivers
);
}
}
}
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4j
DriverMetricsAutoConfiguration
.java
→
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4j
HealthContributorConfigurations
.java
View file @
c6fde1e4
...
...
@@ -16,63 +16,52 @@
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
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.
neo4j.Neo4jDriverAutoConfiguratio
n
;
import
org.springframework.boot.autoconfigure.
condition.ConditionalOnMissingBea
n
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics on all available
* {@link Driver drivers}.
* <p>
* 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.
* Health contributor options for Neo4j.
*
* @author Michael J. Simons
* @
since 2.4.0
* @
author Stephane Nicoll
*/
@Configuration
(
proxyBeanMethods
=
false
)
@AutoConfigureAfter
({
MetricsAutoConfiguration
.
class
,
Neo4jDriverAutoConfiguration
.
class
,
SimpleMetricsExportAutoConfiguration
.
class
})
@ConditionalOnClass
({
Driver
.
class
,
MeterRegistry
.
class
})
@ConditionalOnBean
({
Driver
.
class
,
MeterRegistry
.
class
})
public
class
Neo4jDriverMetricsAutoConfiguration
{
class
Neo4jHealthContributorConfigurations
{
@Configuration
(
proxyBeanMethods
=
false
)
static
class
Neo4jConfiguration
extends
CompositeHealthContributorConfiguration
<
Neo4jHealthIndicator
,
Driver
>
{
@Bean
@ConditionalOnMissingBean
(
name
=
{
"neo4jHealthIndicator"
,
"neo4jHealthContributor"
})
HealthContributor
neo4jHealthContributor
(
Map
<
String
,
Driver
>
drivers
)
{
return
createContributor
(
drivers
);
}
}
private
static
final
Log
logger
=
LogFactory
.
getLog
(
Neo4jDriverMetricsAutoConfiguration
.
class
);
@Configuration
(
proxyBeanMethods
=
false
)
@ConditionalOnClass
(
Flux
.
class
)
static
class
Neo4jReactiveConfiguration
extends
CompositeReactiveHealthContributorConfiguration
<
Neo4jReactiveHealthIndicator
,
Driver
>
{
@Autowired
public
void
bindDataSourcesToRegistry
(
Map
<
String
,
Driver
>
drivers
,
MeterRegistry
registry
)
{
@Bean
@ConditionalOnMissingBean
(
name
=
{
"neo4jHealthIndicator"
,
"neo4jHealthContributor"
})
ReactiveHealthContributor
neo4jHealthContributor
(
Map
<
String
,
Driver
>
drivers
)
{
return
createContributor
(
drivers
);
}
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
;
});
});
}
}
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
View file @
c6fde1e4
...
...
@@ -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
,
Neo4j
AutoConfiguration
.
class
,
Neo4j
DataAutoConfiguration
.
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
,
Neo4j
AutoConfiguration
.
class
,
Neo4j
DataAutoConfiguration
.
class
,
Neo4jRepositoriesAutoConfiguration
.
class
,
RedisAutoConfiguration
.
class
,
RedisRepositoriesAutoConfiguration
.
class
,
FlywayAutoConfiguration
.
class
,
MetricsAutoConfiguration
.
class
})
static
class
Child
{
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMetricsAutoConfigurationTest.java
deleted
100644 → 0
View file @
c5a7815e
/*
* 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
<
AssertableApplicationContext
>
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
;
}
}
}
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jDriverMocks.java
deleted
100644 → 0
View file @
c5a7815e
/*
* 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
()
{
}
}
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/neo4j/Neo4jHealthContributorAutoConfigurationTests.java
View file @
c6fde1e4
/*
* 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
));
});
}
@Test
void
runShouldCreateHealthIndicator
()
{
this
.
contextRunner
.
withUserConfiguration
(
Neo4jConfiguration
.
class
).
run
((
context
)
->
assertThat
(
context
)
.
hasSingleBean
(
Neo4jReactiveHealthIndicator
.
class
).
doesNotHaveBean
(
Neo4jHealthIndicator
.
class
));
}
@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
runWithoutReactorShouldCreateHealthIndicator
()
{
this
.
contextRunner
.
withUserConfiguration
(
Neo4jConfiguration
.
class
)
.
withClassLoader
(
new
FilteredClassLoader
(
Flux
.
class
)).
run
((
context
)
->
assertThat
(
context
)
.
hasSingleBean
(
Neo4jHealthIndicator
.
class
).
doesNotHaveBean
(
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
reactiveHealthCheckShouldHavePrecedence
()
{
contextRunner
.
withUserConfiguration
(
WithDriver
.
class
).
run
(
ctx
->
{
assertThat
(
ctx
).
doesNotHaveBean
(
Neo4jHealthIndicator
.
class
).
hasBean
(
"neo4jHealthContributor"
);
assertThat
(
ctx
).
getBean
(
"neo4jHealthContributor"
).
isInstanceOf
(
CompositeReactiveHealthContributor
.
class
)
.
satisfies
(
o
->
{
CompositeReactiveHealthContributor
chc
=
(
CompositeReactiveHealthContributor
)
o
;
assertThat
(
chc
.
getContributor
(
"driver"
)).
isInstanceOf
(
Neo4jReactiveHealthIndicator
.
class
);
});
}
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
mock
DriverWithoutMetrics
(
);
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
);
}
};
...
...
spring-boot-project/spring-boot-actuator/build.gradle
View file @
c6fde1e4
...
...
@@ -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"
)
...
...
spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetrics.java
deleted
100644 → 0
View file @
c5a7815e
/*
* 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
<
Tag
>
tags
;
public
Neo4jDriverMetrics
(
String
name
,
Driver
driver
,
Iterable
<
Tag
>
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
<
ConnectionPoolMetrics
>
getPoolMetricsBinder
(
MeterRegistry
meterRegistry
)
{
return
(
poolMetrics
)
->
{
Iterable
<
Tag
>
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
;
}
}
}
spring-boot-project/spring-boot-actuator/src/
test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTestBase
.java
→
spring-boot-project/spring-boot-actuator/src/
main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthDetailsHandler
.java
View file @
c6fde1e4
/*
* 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,
...
...
@@ -16,49 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
actuate
.
neo4j
;
import
org.mockito.Mock
;
package
org
.
springframework
.
boot
.
actuate
.
neo4j
;
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
;
import
org.springframework.boot.actuate.health.Health.Builder
;
import
org.springframework.util.StringUtils
;
/**
*
Contains some shared mocks for the health indicator tests
.
*
Handle health check details for a Neo4j server
.
*
* @author
Michael J. Simons
* @author
Stephane Nicoll
*/
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"
));
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
());
}
}
}
spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java
View file @
c6fde1e4
...
...
@@ -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
{
r
esultSummaryWithEdition
=
runHealthCheckQuery
(
);
r
unHealthCheckQuery
(
builder
);
}
catch
(
SessionExpiredException
sessionExpiredException
)
{
catch
(
SessionExpiredException
ex
)
{
// Retry one time when the session has been expired
logger
.
warn
(
MESSAGE_SESSION_EXPIRED
);
r
esultSummaryWithEdition
=
runHealthCheckQuery
(
);
r
unHealthCheckQuery
(
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
);
}
}
...
...
spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java
View file @
c6fde1e4
...
...
@@ -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
<
ResultSummaryWithEdition
>
runHealthCheckQuery
()
{
Mono
<
Tuple2
<
String
,
ResultSummary
>
>
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
);
}
...
...
spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMetricsTests.java
deleted
100644 → 0
View file @
c5a7815e
/*
* 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
();
}
}
spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jDriverMocks.java
deleted
100644 → 0
View file @
c5a7815e
/*
* 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
()
{
}
}
spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicatorTests.java
View file @
c6fde1e4
...
...
@@ -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
;
class
Neo4jHealthIndicatorTests
{
@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
();
void
neo4jIsUp
()
{
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 Haus
e"
);
assertThat
(
health
.
getDetails
()).
doesNotContainKey
(
"database
"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"
some
edition"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"server"
,
"4711@
My Hom
e"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"database"
,
"test
"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"
ultimate collectors
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
();
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@
Zu Haus
e"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"server"
,
"4711@
My Hom
e"
);
assertThat
(
health
.
getDetails
()).
doesNotContainKey
(
"database"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"some edition"
);
}
@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
();
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@Zu Hause"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"database"
,
"n/a"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"ultimate collectors edition"
);
verify
(
session
).
close
();
verifyNoMoreInteractions
(
this
.
driver
,
this
.
session
,
this
.
statementResult
,
this
.
resultSummary
,
this
.
serverInfo
,
this
.
databaseInfo
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"server"
,
"4711@My Home"
);
assertThat
(
health
.
getDetails
()).
doesNotContainKey
(
"database"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"some edition"
);
}
@Test
void
neo4jSessionIsExpiredOnce
()
{
AtomicInteger
cnt
=
new
AtomicInteger
(
0
);
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
))
{
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"
);
}
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
;
}
verifyNoMoreInteractions
(
this
.
driver
,
this
.
session
,
this
.
statementResult
,
this
.
resultSummary
,
this
.
serverInfo
,
this
.
databaseInfo
);
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
;
}
}
spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicatorTest.java
View file @
c6fde1e4
...
...
@@ -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 Haus
e"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"server"
,
"4711@
My Hom
e"
);
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 Haus
e"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"edition"
,
"
ultimate collectors
edition"
);
assertThat
(
health
.
getDetails
()).
containsEntry
(
"server"
,
"4711@
My Hom
e"
);
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
();
}
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
;
}
verifyNoMoreInteractions
(
driver
,
session
,
statementResult
,
resultSummary
,
serverInfo
,
databaseInfo
);
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
;
}
}
spring-boot-project/spring-boot-actuator/src/
main/java/org/springframework/boot/actuate/neo4j/ResultSummaryWithEdition
.java
→
spring-boot-project/spring-boot-actuator/src/
test/java/org/springframework/boot/actuate/neo4j/ResultSummaryMock
.java
View file @
c6fde1e4
...
...
@@ -16,22 +16,33 @@
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
;
/**
*
A holder for a {@link ResultSummary result summary} and database edition
.
*
Test utility to mock {@link ResultSummary}
.
*
* @author
Michael J. Simons
* @author
Stephane Nicoll
*/
final
class
ResultSummaryWithEdition
{
final
ResultSummary
resultSummary
;
final
class
ResultSummaryMock
{
final
String
edition
;
private
ResultSummaryMock
()
{
}
ResultSummaryWithEdition
(
ResultSummary
resultSummary
,
String
edition
)
{
this
.
resultSummary
=
resultSummary
;
this
.
edition
=
edition
;
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
;
}
}
spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc
View file @
c6fde1e4
...
...
@@ -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.
|===
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment