diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfiguration.java new file mode 100644 index 0000000000..097b7f3bdf --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2019 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.hazelcast; + +import java.util.Map; + +import com.hazelcast.core.HazelcastInstance; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; +import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.hazelcast.HazelcastHealthIndicator; +import org.springframework.boot.actuate.health.HealthIndicator; +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.hazelcast.HazelcastAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for + * {@link HazelcastHealthIndicator}. + * + * @author Dmytro Nosan + * @since 2.2.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(HazelcastInstance.class) +@ConditionalOnBean(HazelcastInstance.class) +@ConditionalOnEnabledHealthIndicator("hazelcast") +@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) +@AutoConfigureAfter(HazelcastAutoConfiguration.class) +public class HazelcastHealthIndicatorAutoConfiguration + extends CompositeHealthIndicatorConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "hazelcastHealthIndicator") + public HealthIndicator hazelcastHealthIndicator(Map hazelcastInstances) { + return createHealthIndicator(hazelcastInstances); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/package-info.java new file mode 100644 index 0000000000..cf59117c4e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/hazelcast/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2019 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. + */ + +/** + * Auto-configuration for actuator Hazelcast concerns. + */ +package org.springframework.boot.actuate.autoconfigure.hazelcast; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationIntegrationTests.java new file mode 100644 index 0000000000..da8cb533b3 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationIntegrationTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2019 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.hazelcast; + +import com.hazelcast.core.HazelcastInstance; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.hazelcast.HazelcastHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link HazelcastHealthIndicatorAutoConfiguration}. + * + * @author Dmytro Nosan + */ +class HazelcastHealthIndicatorAutoConfigurationIntegrationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(HazelcastHealthIndicatorAutoConfiguration.class, + HazelcastAutoConfiguration.class, HealthIndicatorAutoConfiguration.class)); + + @Test + void hazelcastUp() { + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(HazelcastInstance.class).hasSingleBean(HazelcastHealthIndicator.class); + HazelcastInstance hazelcast = context.getBean(HazelcastInstance.class); + Health health = context.getBean(HazelcastHealthIndicator.class).health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()).containsOnlyKeys("name", "uuid").containsEntry("name", hazelcast.getName()) + .containsEntry("uuid", hazelcast.getLocalEndpoint().getUuid()); + }); + } + + @Test + void hazelcastDown() { + this.contextRunner.run((context) -> { + context.getBean(HazelcastInstance.class).shutdown(); + assertThat(context).hasSingleBean(HazelcastHealthIndicator.class); + Health health = context.getBean(HazelcastHealthIndicator.class).health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + }); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationTests.java new file mode 100644 index 0000000000..3e9e980776 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/hazelcast/HazelcastHealthIndicatorAutoConfigurationTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2019 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.hazelcast; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.hazelcast.HazelcastHealthIndicator; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HazelcastHealthIndicatorAutoConfiguration}. + * + * @author Dmytro Nosan + */ +class HazelcastHealthIndicatorAutoConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(HazelcastAutoConfiguration.class, + HazelcastHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class)); + + @Test + void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(HazelcastHealthIndicator.class) + .doesNotHaveBean(ApplicationHealthIndicator.class)); + } + + @Test + void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.hazelcast.enabled:false") + .run((context) -> assertThat(context).doesNotHaveBean(HazelcastHealthIndicator.class) + .hasSingleBean(ApplicationHealthIndicator.class)); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicator.java new file mode 100644 index 0000000000..d2c23f3a73 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicator.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2019 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.hazelcast; + +import com.hazelcast.core.HazelcastInstance; + +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.Assert; + +/** + * {@link HealthIndicator} for Hazelcast. + * + * @author Dmytro Nosan + * @author Stephane Nicoll + * @since 2.2.0 + */ +public class HazelcastHealthIndicator extends AbstractHealthIndicator { + + private final HazelcastInstance hazelcast; + + public HazelcastHealthIndicator(HazelcastInstance hazelcast) { + super("Hazelcast health check failed"); + Assert.notNull(hazelcast, "HazelcastInstance must not be null"); + this.hazelcast = hazelcast; + } + + @Override + protected void doHealthCheck(Health.Builder builder) { + this.hazelcast.executeTransaction((context) -> { + builder.up().withDetail("name", this.hazelcast.getName()).withDetail("uuid", + this.hazelcast.getLocalEndpoint().getUuid()); + return null; + }); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/package-info.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/package-info.java new file mode 100644 index 0000000000..44e785578d --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2019 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. + */ + +/** + * Actuator support for Hazelcast. + */ +package org.springframework.boot.actuate.hazelcast; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicatorTests.java new file mode 100644 index 0000000000..1340e6058f --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicatorTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2019 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.hazelcast; + +import com.hazelcast.core.Endpoint; +import com.hazelcast.core.HazelcastException; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.transaction.TransactionalTask; +import org.junit.jupiter.api.Test; + +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.BDDMockito.when; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link HazelcastHealthIndicator}. + * + * @author Dmytro Nosan + * @author Stephane Nicoll + */ +class HazelcastHealthIndicatorTests { + + private final HazelcastInstance hazelcast = mock(HazelcastInstance.class); + + @Test + void hazelcastUp() { + Endpoint endpoint = mock(Endpoint.class); + when(this.hazelcast.getName()).thenReturn("hz0-instance"); + when(this.hazelcast.getLocalEndpoint()).thenReturn(endpoint); + when(endpoint.getUuid()).thenReturn("7581bb2f-879f-413f-b574-0071d7519eb0"); + when(this.hazelcast.executeTransaction(any())).thenAnswer((invocation) -> { + TransactionalTask task = invocation.getArgument(0); + return task.execute(null); + }); + Health health = new HazelcastHealthIndicator(this.hazelcast).health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()).containsOnlyKeys("name", "uuid").containsEntry("name", "hz0-instance") + .containsEntry("uuid", "7581bb2f-879f-413f-b574-0071d7519eb0"); + } + + @Test + void hazelcastDown() { + when(this.hazelcast.executeTransaction(any())).thenThrow(new HazelcastException()); + Health health = new HazelcastHealthIndicator(this.hazelcast).health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 209e94f805..24c1576c42 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -764,6 +764,9 @@ The following `HealthIndicators` are auto-configured by Spring Boot when appropr |{sc-spring-boot-actuator}/elasticsearch/ElasticsearchHealthIndicator.{sc-ext}[`ElasticsearchHealthIndicator`] |Checks that an Elasticsearch cluster is up. +|{sc-spring-boot-actuator}/hazelcast/HazelcastHealthIndicator.{sc-ext}[`HazelcastHealthIndicator`] +|Checks that an Hazelcast server is up. + |{sc-spring-boot-actuator}/influx/InfluxDbHealthIndicator.{sc-ext}[`InfluxDbHealthIndicator`] |Checks that an InfluxDB server is up.