diff --git a/spring-geode-actuator/src/main/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicator.java b/spring-geode-actuator/src/main/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicator.java index d858cd72..211cde3b 100644 --- a/spring-geode-actuator/src/main/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicator.java +++ b/spring-geode-actuator/src/main/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicator.java @@ -26,9 +26,12 @@ import java.util.stream.Collectors; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.internal.cache.LocalDataSet; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator; +import org.springframework.geode.boot.actuate.health.support.RegionStatisticsResolver; +import org.springframework.util.StringUtils; /** * The {@link GeodeRegionsHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about @@ -48,7 +51,7 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { private final BiConsumer, Health.Builder> gemfireRegionHealthIndicatorConsumers = withRegionDetails() .andThen(withPartitionRegionDetails()) .andThen(withRegionEvictionPolicyDetails()) - .andThen(withRegionExpirationDetails()) + .andThen(withRegionExpirationPolicyDetails()) .andThen(withRegionStatisticsDetails()); /** @@ -125,13 +128,16 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { Optional.ofNullable(region.getAttributes()) .ifPresent(regionAttributes -> builder .withDetail(cacheRegionKey(regionName, "cloning-enabled"), toYesNoString(regionAttributes.getCloningEnabled())) - .withDetail(cacheRegionKey(regionName, "data-policy"), regionAttributes.getDataPolicy()) + .withDetail(cacheRegionKey(regionName, "data-policy"), String.valueOf(regionAttributes.getDataPolicy())) .withDetail(cacheRegionKey(regionName, "initial-capacity"), regionAttributes.getInitialCapacity()) .withDetail(cacheRegionKey(regionName, "load-factor"), regionAttributes.getLoadFactor()) .withDetail(cacheRegionKey(regionName, "key-constraint"), nullSafeClassName(regionAttributes.getKeyConstraint())) .withDetail(cacheRegionKey(regionName, "off-heap"), toYesNoString(regionAttributes.getOffHeap())) - .withDetail(cacheRegionKey(regionName, "pool-name"), regionAttributes.getPoolName()) - .withDetail(cacheRegionKey(regionName, "scope"), regionAttributes.getScope()) + .withDetail(cacheRegionKey(regionName, "pool-name"), Optional.ofNullable(regionAttributes.getPoolName()) + .filter(StringUtils::hasText) + .orElse("")) + .withDetail(cacheRegionKey(regionName, "scope"), String.valueOf(regionAttributes.getScope())) + .withDetail(cacheRegionKey(regionName, "statistics-enabled"), toYesNoString(regionAttributes.getStatisticsEnabled())) .withDetail(cacheRegionKey(regionName, "value-constraint"), nullSafeClassName(regionAttributes.getValueConstraint()))); }; @@ -144,11 +150,13 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { String regionName = region.getName(); Optional.of(region) - .filter(it -> it.getAttributes() != null) + .filter(this::isRegionAttributesPresent) .map(Region::getAttributes) .map(RegionAttributes::getPartitionAttributes) .ifPresent(partitionAttributes -> builder - .withDetail(cachePartitionRegionKey(regionName, "collocated-with"), partitionAttributes.getColocatedWith()) + .withDetail(cachePartitionRegionKey(regionName, "collocated-with"), Optional.ofNullable(partitionAttributes.getColocatedWith()) + .filter(StringUtils::hasText) + .orElse("")) .withDetail(cachePartitionRegionKey(regionName, "local-max-memory"), partitionAttributes.getLocalMaxMemory()) .withDetail(cachePartitionRegionKey(regionName, "redundant-copies"), partitionAttributes.getRedundantCopies()) .withDetail(cachePartitionRegionKey(regionName, "total-max-memory"), partitionAttributes.getTotalMaxMemory()) @@ -163,15 +171,15 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { String regionName = region.getName(); Optional.of(region) - .filter(it -> it.getAttributes() != null) + .filter(this::isRegionAttributesPresent) .map(Region::getAttributes) .map(RegionAttributes::getEvictionAttributes) .ifPresent(evictionAttributes -> { - builder.withDetail(cacheRegionEvictionKey(regionName, "action"), evictionAttributes.getAction()) - .withDetail(cacheRegionEvictionKey(regionName, "algorithm"), evictionAttributes.getAlgorithm()); + builder.withDetail(cacheRegionEvictionKey(regionName, "action"), String.valueOf(evictionAttributes.getAction())) + .withDetail(cacheRegionEvictionKey(regionName, "algorithm"), String.valueOf(evictionAttributes.getAlgorithm())); - // NOTE: Careful! Eviction Maximum does not apply when Algorithm is Heap LRU. + // NOTE: Careful! Eviction Maximum does not apply when Eviction Algorithm is Heap LRU. Optional.ofNullable(evictionAttributes.getAlgorithm()) .filter(it -> !it.isLRUHeap()) .ifPresent(it -> builder @@ -180,26 +188,26 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { }; } - private BiConsumer, Health.Builder> withRegionExpirationDetails() { + private BiConsumer, Health.Builder> withRegionExpirationPolicyDetails() { return (region, builder) -> { String regionName = region.getName(); Optional.of(region) - .filter(it -> it.getAttributes() != null) + .filter(this::isRegionAttributesPresent) .map(Region::getAttributes) .map(RegionAttributes::getEntryTimeToLive) .ifPresent(expirationAttributes -> builder - .withDetail(cacheRegionExpirationKey(regionName, "entry.ttl.action"), expirationAttributes.getAction()) + .withDetail(cacheRegionExpirationKey(regionName, "entry.ttl.action"), String.valueOf(expirationAttributes.getAction())) .withDetail(cacheRegionExpirationKey(regionName, "entry.ttl.timeout"), expirationAttributes.getTimeout())); Optional.of(region) - .filter(it -> it.getAttributes() != null) + .filter(this::isRegionAttributesPresent) .map(Region::getAttributes) .map(RegionAttributes::getEntryIdleTimeout) .ifPresent(expirationAttributes -> builder - .withDetail(cacheRegionExpirationKey(regionName, "entry.tti.action"), expirationAttributes.getAction()) + .withDetail(cacheRegionExpirationKey(regionName, "entry.tti.action"), String.valueOf(expirationAttributes.getAction())) .withDetail(cacheRegionExpirationKey(regionName, "entry.tti.timeout"), expirationAttributes.getTimeout())); }; } @@ -211,10 +219,11 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { String regionName = region.getName(); Optional.of(region) - .filter(it -> it.getAttributes() != null) - .filter(it -> it.getAttributes().getStatisticsEnabled()) - .map(Region::getStatistics) + .filter(this::isNotLocalDataSet) + .filter(this::isStatisticsEnabled) + .map(RegionStatisticsResolver::resolve) .ifPresent(cacheStatistics -> builder + .withDetail(cacheRegionStatisticsKey(regionName, "cache-statistics-type"), nullSafeClassName(cacheStatistics.getClass())) .withDetail(cacheRegionStatisticsKey(regionName, "hit-count"), cacheStatistics.getHitCount()) .withDetail(cacheRegionStatisticsKey(regionName, "hit-ratio"), cacheStatistics.getHitRatio()) .withDetail(cacheRegionStatisticsKey(regionName, "last-accessed-time"), cacheStatistics.getLastAccessedTime()) @@ -223,6 +232,29 @@ public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator { }; } + private boolean isLocalDataSet(Region region) { + return region instanceof LocalDataSet; + } + + private boolean isNotLocalDataSet(Region region) { + return !isLocalDataSet(region); + } + + private boolean isRegionAttributesPresent(Region region) { + + return Optional.ofNullable(region) + .map(Region::getAttributes) + .isPresent(); + } + + private boolean isStatisticsEnabled(Region region) { + + return Optional.ofNullable(region) + .map(Region::getAttributes) + .filter(RegionAttributes::getStatisticsEnabled) + .isPresent(); + } + private String cachePartitionRegionKey(String regionName, String suffix) { return cacheRegionKey(regionName, String.format("partition.%s", suffix)); } diff --git a/spring-geode-actuator/src/test/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicatorUnitTests.java b/spring-geode-actuator/src/test/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicatorUnitTests.java index 766ad41a..3de3ca12 100644 --- a/spring-geode-actuator/src/test/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicatorUnitTests.java +++ b/spring-geode-actuator/src/test/java/org/springframework/geode/boot/actuate/GeodeRegionsHealthIndicatorUnitTests.java @@ -56,6 +56,7 @@ import org.springframework.data.gemfire.tests.mock.CacheMockObjects; * @see org.mockito.Mock * @see org.mockito.Mockito * @see org.mockito.junit.MockitoJUnitRunner + * @see org.apache.geode.cache.CacheStatistics * @see org.apache.geode.cache.GemFireCache * @see org.apache.geode.cache.Region * @see org.springframework.boot.actuate.health.Health @@ -90,6 +91,7 @@ public class GeodeRegionsHealthIndicatorUnitTests { when(mockRegionOne.getAttributes().getOffHeap()).thenReturn(true); when(mockRegionOne.getAttributes().getPoolName()).thenReturn(""); when(mockRegionOne.getAttributes().getScope()).thenReturn(Scope.DISTRIBUTED_ACK); + when(mockRegionOne.getAttributes().getStatisticsEnabled()).thenReturn(false); when(mockRegionOne.getAttributes().getValueConstraint()).thenReturn((Class) Currency.class); PartitionAttributes mockPartitionAttributes = mock(PartitionAttributes.class); @@ -163,13 +165,13 @@ public class GeodeRegionsHealthIndicatorUnitTests { assertThat(healthDetails).containsEntry("geode.cache.regions", Arrays.asList("/MockRegionOne", "/MockRegionTwo")); assertThat(healthDetails).containsEntry("geode.cache.regions.count", (long) mockRegions.size()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.cloning-enabled", "Yes"); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.data-policy", DataPolicy.PARTITION); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.data-policy", DataPolicy.PARTITION.toString()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.initial-capacity", 101); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.load-factor", 0.75f); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.key-constraint", Long.class.getName()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.off-heap", "Yes"); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.action", EvictionAction.LOCAL_DESTROY); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.algorithm", EvictionAlgorithm.LRU_ENTRY); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.action", EvictionAction.LOCAL_DESTROY.toString()); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.algorithm", EvictionAlgorithm.LRU_ENTRY.toString()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.maximum", 10000); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.collocated-with", "CollocatedRegion"); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.local-max-memory", 10240); @@ -177,19 +179,21 @@ public class GeodeRegionsHealthIndicatorUnitTests { assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.total-max-memory", 4096000L); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.total-number-of-buckets", 226); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.pool-name", ""); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.scope", Scope.DISTRIBUTED_ACK); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.scope", Scope.DISTRIBUTED_ACK.toString()); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.statistics-enabled", "No"); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.value-constraint", Currency.class.getName()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.cloning-enabled", "No"); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.data-policy", DataPolicy.EMPTY); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.data-policy", DataPolicy.EMPTY.toString()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.initial-capacity", 0); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.load-factor", 0.0f); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.key-constraint", Integer.class.getName()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.off-heap", "No"); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.pool-name", "TestPool"); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.scope", Scope.DISTRIBUTED_NO_ACK); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.tti.action", ExpirationAction.INVALIDATE); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.scope", Scope.DISTRIBUTED_NO_ACK.toString()); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics-enabled", "Yes"); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.tti.action", ExpirationAction.INVALIDATE.toString()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.tti.timeout", 600); - assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.ttl.action", ExpirationAction.DESTROY); + assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.ttl.action", ExpirationAction.DESTROY.toString()); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.ttl.timeout", 900); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.hit-count", 202408L); assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.hit-ratio", 0.82f);