Add health indicator for Neo4j
See gh-9557
This commit is contained in:
committed by
Stephane Nicoll
parent
3b0cbea682
commit
4c97dcb53a
@@ -26,6 +26,7 @@ import javax.sql.DataSource;
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -42,6 +43,7 @@ import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
@@ -59,6 +61,7 @@ import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoCo
|
||||
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
@@ -91,6 +94,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
* @author Phillip Webb
|
||||
* @author Tommy Ludwig
|
||||
* @author Eddú Meléndez
|
||||
* @author Eric Spiegelberg
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -102,7 +106,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
LdapDataAutoConfiguration.class, MailSenderAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
RabbitAutoConfiguration.class, RedisAutoConfiguration.class,
|
||||
SolrAutoConfiguration.class })
|
||||
SolrAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
|
||||
@EnableConfigurationProperties({ HealthIndicatorProperties.class })
|
||||
@Import({
|
||||
ElasticsearchHealthIndicatorConfiguration.ElasticsearchClientHealthIndicatorConfiguration.class,
|
||||
@@ -257,6 +261,28 @@ public class HealthIndicatorAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(SessionFactory.class)
|
||||
@ConditionalOnBean(SessionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("neo4j")
|
||||
public static class Neo4jHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<Neo4jHealthIndicator, SessionFactory> {
|
||||
|
||||
private final Map<String, SessionFactory> sessionFactories;
|
||||
|
||||
public Neo4jHealthIndicatorConfiguration(
|
||||
Map<String, SessionFactory> sessionFactories) {
|
||||
this.sessionFactories = sessionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "neo4jHealthIndicator")
|
||||
public HealthIndicator neo4jHealthIndicator() {
|
||||
return createHealthIndicator(this.sessionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
@ConditionalOnEnabledHealthIndicator("mongo")
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.health;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.model.Result;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link HealthIndicator} that tests the status of a Neo4j by executing a Cypher
|
||||
* statement.
|
||||
*
|
||||
* @author Eric Spiegelberg
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "management.health.neo4j", ignoreUnknownFields = false)
|
||||
public class Neo4jHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
/**
|
||||
* The Cypher statement used to verify Neo4j is up.
|
||||
*/
|
||||
public static final String CYPHER = "match (n) return count(n) as nodes";
|
||||
|
||||
/**
|
||||
* Create a new {@link Neo4jHealthIndicator} using the specified
|
||||
* {@link SessionFactory}.
|
||||
* @param sessionFactory the SessionFactory
|
||||
*/
|
||||
public Neo4jHealthIndicator(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
Session session = this.sessionFactory.openSession();
|
||||
|
||||
Result result = session.query(CYPHER, Collections.emptyMap());
|
||||
Iterable<Map<String, Object>> results = result.queryResults();
|
||||
int nodes = (int) results.iterator().next().get("nodes");
|
||||
|
||||
builder.up().withDetail("nodes", nodes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,6 +181,12 @@
|
||||
"description": "Enable Mail health check.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "management.health.neo4j.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable Neo4j health check.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "management.info.build.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
|
||||
@@ -23,6 +23,7 @@ import javax.sql.DataSource;
|
||||
import io.searchbox.client.JestClient;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SolrHealthIndicator;
|
||||
@@ -76,6 +78,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Eric Spiegelberg
|
||||
*/
|
||||
public class HealthIndicatorAutoConfigurationTests {
|
||||
|
||||
@@ -578,6 +581,34 @@ public class HealthIndicatorAutoConfigurationTests {
|
||||
.isEqualTo(ApplicationHealthIndicator.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neo4jHealthIndicator() throws Exception {
|
||||
TestPropertyValues.of("management.health.diskspace.enabled:false")
|
||||
.applyTo(this.context);
|
||||
this.context.register(Neo4jConfiguration.class, ManagementServerProperties.class,
|
||||
HealthIndicatorAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
Map<String, HealthIndicator> beans = this.context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans.size()).isEqualTo(1);
|
||||
assertThat(beans.values().iterator().next().getClass())
|
||||
.isEqualTo(Neo4jHealthIndicator.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notNeo4jHealthIndicator() throws Exception {
|
||||
TestPropertyValues.of("management.health.diskspace.enabled:false",
|
||||
"management.health.neo4j.enabled:false").applyTo(this.context);
|
||||
this.context.register(Neo4jConfiguration.class, ManagementServerProperties.class,
|
||||
HealthIndicatorAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
Map<String, HealthIndicator> beans = this.context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans.size()).isEqualTo(1);
|
||||
assertThat(beans.values().iterator().next().getClass())
|
||||
.isEqualTo(ApplicationHealthIndicator.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
protected static class DataSourceConfig {
|
||||
@@ -659,4 +690,14 @@ public class HealthIndicatorAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Neo4jConfiguration {
|
||||
|
||||
@Bean
|
||||
public SessionFactory sessionFactory() {
|
||||
return mock(SessionFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.health;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.ogm.exception.CypherException;
|
||||
import org.neo4j.ogm.model.Result;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link Neo4jHealthIndicator}.
|
||||
*
|
||||
* @author Eric Spiegelberg
|
||||
*/
|
||||
public class Neo4jHealthIndicatorTests {
|
||||
|
||||
private Result result;
|
||||
private Session session;
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
private Neo4jHealthIndicator neo4jHealthIndicator;
|
||||
|
||||
private Map<String, Object> emptyParameters = new HashMap<>();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
this.result = mock(Result.class);
|
||||
this.session = mock(Session.class);
|
||||
this.sessionFactory = mock(SessionFactory.class);
|
||||
|
||||
given(this.sessionFactory.openSession()).willReturn(this.session);
|
||||
|
||||
this.neo4jHealthIndicator = new Neo4jHealthIndicator(this.sessionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neo4jUp() {
|
||||
given(this.session.query(Neo4jHealthIndicator.CYPHER, this.emptyParameters))
|
||||
.willReturn(this.result);
|
||||
|
||||
int nodeCount = 500;
|
||||
Map<String, Object> expectedCypherDetails = new HashMap<>();
|
||||
expectedCypherDetails.put("nodes", nodeCount);
|
||||
|
||||
List<Map<String, Object>> queryResults = new ArrayList<>();
|
||||
queryResults.add(expectedCypherDetails);
|
||||
|
||||
given(this.result.queryResults()).willReturn(queryResults);
|
||||
|
||||
Health health = this.neo4jHealthIndicator.health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
|
||||
Map<String, Object> details = health.getDetails();
|
||||
int nodeCountFromDetails = (int) details.get("nodes");
|
||||
|
||||
Assert.assertEquals(nodeCount, nodeCountFromDetails);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neo4jDown() {
|
||||
|
||||
CypherException cypherException = new CypherException("Error executing Cypher",
|
||||
"Neo.ClientError.Statement.SyntaxError",
|
||||
"Unable to execute invalid Cypher");
|
||||
|
||||
given(this.session.query(Neo4jHealthIndicator.CYPHER, this.emptyParameters))
|
||||
.willThrow(cypherException);
|
||||
|
||||
Health health = this.neo4jHealthIndicator.health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user