From a4d461aae08cf86fe9c349a39bc36fdfcefb8bdd Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 15 Dec 2014 12:17:03 -0700 Subject: [PATCH] Change DiscoveryHealthIndicator to an interface and create a DiscoveryCompositeHealthIndicator fixes gh-1 --- .../CommonsClientAutoConfiguration.java | 14 +++- .../client/discovery/DiscoveryClient.java | 2 + .../DiscoveryClientHealthIndicator.java | 61 ++++++++++++++++ .../DiscoveryCompositeHealthIndicator.java | 36 ++++++++++ .../discovery/DiscoveryHealthIndicator.java | 39 ++-------- ...iscoveryCompositeHealthIndicatorTests.java | 71 +++++++++++++++++++ 6 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/springframework/cloud/client/discovery/DiscoveryClientHealthIndicator.java create mode 100644 src/main/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicator.java create mode 100644 src/test/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicatorTests.java diff --git a/src/main/java/org/springframework/cloud/client/CommonsClientAutoConfiguration.java b/src/main/java/org/springframework/cloud/client/CommonsClientAutoConfiguration.java index e1d99711..0229782a 100644 --- a/src/main/java/org/springframework/cloud/client/CommonsClientAutoConfiguration.java +++ b/src/main/java/org/springframework/cloud/client/CommonsClientAutoConfiguration.java @@ -1,12 +1,17 @@ package org.springframework.cloud.client; +import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.client.discovery.DiscoveryClientHealthIndicator; +import org.springframework.cloud.client.discovery.DiscoveryCompositeHealthIndicator; import org.springframework.cloud.client.discovery.DiscoveryHealthIndicator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import java.util.List; + /** * @author Spencer Gibb */ @@ -15,8 +20,13 @@ import org.springframework.core.annotation.Order; @Order(0) public class CommonsClientAutoConfiguration { + @Bean + public DiscoveryClientHealthIndicator instancesHealthIndicator() { + return new DiscoveryClientHealthIndicator(); + } + @Bean - public DiscoveryHealthIndicator discoveryHealthIndicator() { - return new DiscoveryHealthIndicator(); + public DiscoveryCompositeHealthIndicator discoveryHealthIndicator(HealthAggregator aggregator, List indicators) { + return new DiscoveryCompositeHealthIndicator(aggregator, indicators); } } diff --git a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java index fa3822aa..2d4d0803 100644 --- a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java +++ b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java @@ -9,6 +9,8 @@ import java.util.List; */ //TODO: merge with LoadBalancerClient? public interface DiscoveryClient { + public String description(); + /** * @return ServiceInstance with information used to register the local service */ diff --git a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClientHealthIndicator.java b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClientHealthIndicator.java new file mode 100644 index 00000000..2ac96b14 --- /dev/null +++ b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClientHealthIndicator.java @@ -0,0 +1,61 @@ +package org.springframework.cloud.client.discovery; + +import java.util.List; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.BeansException; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.Ordered; + +/** + * @author Spencer Gibb + */ +@Slf4j +public class DiscoveryClientHealthIndicator implements ApplicationContextAware, DiscoveryHealthIndicator, Ordered { + + private ApplicationContext context; + + private int order = Ordered.HIGHEST_PRECEDENCE; + + @Override + public Health health() { + Health.Builder builder = new Health.Builder(); + try { + DiscoveryClient client = context.getBean(DiscoveryClient.class); + if (client == null) { + builder.unknown().withDetail("warning", "No DiscoveryClient found"); + return builder.build(); + } + List services = client.getServices(); + builder.status(new Status("UP", client.description())) + .withDetail("services", services); + } catch (Exception e) { + log.error("Error", e); + builder.down(e); + } + return builder.build(); + } + + @Override + public String getName() { + return "discoveryClient"; + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + @Override + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } +} diff --git a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicator.java b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicator.java new file mode 100644 index 00000000..8eb63ab3 --- /dev/null +++ b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicator.java @@ -0,0 +1,36 @@ +package org.springframework.cloud.client.discovery; + +import java.util.List; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.*; + +/** + * @author Spencer Gibb + */ +@Slf4j +public class DiscoveryCompositeHealthIndicator extends CompositeHealthIndicator { + + @Autowired + public DiscoveryCompositeHealthIndicator(HealthAggregator healthAggregator, List indicators) { + super(healthAggregator); + for (DiscoveryHealthIndicator indicator : indicators) { + addHealthIndicator(indicator.getName(), new Holder(indicator)); + } + } + + public static class Holder implements HealthIndicator { + DiscoveryHealthIndicator delegate; + + public Holder(DiscoveryHealthIndicator delegate) { + this.delegate = delegate; + } + + @Override + public Health health() { + return delegate.health(); + } + } +} diff --git a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryHealthIndicator.java b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryHealthIndicator.java index 50709af6..3db0bf40 100644 --- a/src/main/java/org/springframework/cloud/client/discovery/DiscoveryHealthIndicator.java +++ b/src/main/java/org/springframework/cloud/client/discovery/DiscoveryHealthIndicator.java @@ -1,42 +1,15 @@ package org.springframework.cloud.client.discovery; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -import org.springframework.beans.BeansException; -import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; /** * @author Spencer Gibb */ -@Slf4j -public class DiscoveryHealthIndicator extends AbstractHealthIndicator implements ApplicationContextAware { - - private ApplicationContext context; - - @Override - protected void doHealthCheck(Health.Builder builder) throws Exception { - try { - DiscoveryClient client = context.getBean(DiscoveryClient.class); - if (client == null) { - builder.unknown().withDetail("warning", "No DiscoveryClient found"); - return; - } - List instances = client.getAllInstances(); - builder.up().withDetail("instances", instances); - } catch (Exception e) { - log.error("Error", e); - builder.down(e); - } - } +public interface DiscoveryHealthIndicator { + public String getName(); - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } + /** + * @return an indication of health + */ + Health health(); } diff --git a/src/test/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicatorTests.java b/src/test/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicatorTests.java new file mode 100644 index 00000000..58a04ee7 --- /dev/null +++ b/src/test/java/org/springframework/cloud/client/discovery/DiscoveryCompositeHealthIndicatorTests.java @@ -0,0 +1,71 @@ +package org.springframework.cloud.client.discovery; + +import com.google.common.collect.Lists; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.client.CommonsClientAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +/** + * @author Spencer Gibb + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {DiscoveryCompositeHealthIndicatorTests.Config.class, CommonsClientAutoConfiguration.class}) +public class DiscoveryCompositeHealthIndicatorTests { + + @Autowired + DiscoveryCompositeHealthIndicator healthIndicator; + + @Configuration + public static class Config { + @Bean + public HealthAggregator healthAggregator() { + return new OrderedHealthAggregator(); + } + + @Bean + public DiscoveryClient discoveryClient() { + DiscoveryClient mock = mock(DiscoveryClient.class); + when(mock.description()).thenReturn("TestDiscoveryClient"); + when(mock.getServices()).thenReturn(Lists.newArrayList("TestService1")); + return mock; + } + + @Bean + public DiscoveryHealthIndicator discoveryHealthIndicator() { + return new DiscoveryHealthIndicator() { + @Override + public String getName() { + return "testDiscoveryHealthIndicator"; + } + + @Override + public Health health() { + return new Health.Builder().up().build(); + } + }; + } + } + + @Test + public void testHealthIndicator() { + assertNotNull("healthIndicator was null", healthIndicator); + Health health = healthIndicator.health(); + assertNotNull("health was null", health); + Status status = health.getStatus(); + assertNotNull("status was null", status); + assertEquals("status code was wrong", "UP", status.getCode()); + assertEquals("status desciption was wrong", "TestDiscoveryClient", status.getDescription()); + } +}