Add support for ReactiveHealthIndicatorRegistry
This commit updates the initial proposal to add support for reactive use cases as well. A reactive application can use ReactiveHealthIndicatorRegistry as an alternative to HealthIndicatorRegistry. Closes gh-4965
This commit is contained in:
@@ -25,8 +25,6 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ReactiveHealthIndicator} that returns health indications from all registered
|
||||
* delegates. Provides an alternative {@link Health} for a delegate that reaches a
|
||||
@@ -37,7 +35,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator {
|
||||
|
||||
private final Map<String, ReactiveHealthIndicator> indicators;
|
||||
private final ReactiveHealthIndicatorRegistry registry;
|
||||
|
||||
private final HealthAggregator healthAggregator;
|
||||
|
||||
@@ -47,15 +45,42 @@ public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator
|
||||
|
||||
private final Function<Mono<Health>, Mono<Health>> timeoutCompose;
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeReactiveHealthIndicator}.
|
||||
* @param healthAggregator the health aggregator
|
||||
* @deprecated since 2.1.0 in favour of
|
||||
* {@link #CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
|
||||
*/
|
||||
@Deprecated
|
||||
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator) {
|
||||
this(healthAggregator, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeReactiveHealthIndicator} from the specified
|
||||
* indicators.
|
||||
* @param healthAggregator the health aggregator
|
||||
* @param indicators a map of {@link ReactiveHealthIndicator HealthIndicators} with
|
||||
* the key being used as an indicator name.
|
||||
* @deprecated since 2.1.0 in favour of
|
||||
* {@link #CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
|
||||
*/
|
||||
@Deprecated
|
||||
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
|
||||
Map<String, ReactiveHealthIndicator> indicators) {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(indicators, "Indicators must not be null");
|
||||
this.indicators = new LinkedHashMap<>(indicators);
|
||||
this(healthAggregator, new DefaultReactiveHealthIndicatorRegistry(indicators));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeReactiveHealthIndicator} from the indicators in the
|
||||
* given {@code registry}.
|
||||
* @param healthAggregator the health aggregator
|
||||
* @param registry the registry of {@link ReactiveHealthIndicator HealthIndicators}.
|
||||
*/
|
||||
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
|
||||
ReactiveHealthIndicatorRegistry registry) {
|
||||
this.registry = registry;
|
||||
this.healthAggregator = healthAggregator;
|
||||
this.timeoutCompose = (mono) -> (this.timeout != null ? mono.timeout(
|
||||
Duration.ofMillis(this.timeout), Mono.just(this.timeoutHealth)) : mono);
|
||||
@@ -66,10 +91,15 @@ public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator
|
||||
* @param name the name of the health indicator
|
||||
* @param indicator the health indicator to add
|
||||
* @return this instance
|
||||
* @throws IllegalStateException if an indicator with the given {@code name}
|
||||
* is already registered.
|
||||
* @deprecated since 2.1.0 in favour of
|
||||
* {@link ReactiveHealthIndicatorRegistry#register(String, ReactiveHealthIndicator)}
|
||||
*/
|
||||
@Deprecated
|
||||
public CompositeReactiveHealthIndicator addHealthIndicator(String name,
|
||||
ReactiveHealthIndicator indicator) {
|
||||
this.indicators.put(name, indicator);
|
||||
this.registry.register(name, indicator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -92,7 +122,7 @@ public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator
|
||||
|
||||
@Override
|
||||
public Mono<Health> health() {
|
||||
return Flux.fromIterable(this.indicators.entrySet())
|
||||
return Flux.fromIterable(this.registry.getAll().entrySet())
|
||||
.flatMap((entry) -> Mono.zip(Mono.just(entry.getKey()),
|
||||
entry.getValue().health().compose(this.timeoutCompose)))
|
||||
.collectMap(Tuple2::getT1, Tuple2::getT2)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -16,19 +16,20 @@
|
||||
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link CompositeReactiveHealthIndicator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.1.0 in favor of
|
||||
* {@link CompositeReactiveHealthIndicator#CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeReactiveHealthIndicatorFactory {
|
||||
|
||||
private final Function<String, String> healthIndicatorNameFactory;
|
||||
@@ -62,30 +63,11 @@ public class CompositeReactiveHealthIndicatorFactory {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(reactiveHealthIndicators,
|
||||
"ReactiveHealthIndicators must not be null");
|
||||
CompositeReactiveHealthIndicator healthIndicator = new CompositeReactiveHealthIndicator(
|
||||
healthAggregator);
|
||||
merge(reactiveHealthIndicators, healthIndicators)
|
||||
.forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
healthIndicator.addHealthIndicator(name, indicator);
|
||||
});
|
||||
return healthIndicator;
|
||||
}
|
||||
|
||||
private Map<String, ReactiveHealthIndicator> merge(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
if (ObjectUtils.isEmpty(healthIndicators)) {
|
||||
return reactiveHealthIndicators;
|
||||
}
|
||||
Map<String, ReactiveHealthIndicator> allIndicators = new LinkedHashMap<>(
|
||||
reactiveHealthIndicators);
|
||||
healthIndicators.forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
allIndicators.computeIfAbsent(name,
|
||||
(n) -> new HealthIndicatorReactiveAdapter(indicator));
|
||||
});
|
||||
return allIndicators;
|
||||
ReactiveHealthIndicatorRegistryFactory factory = new ReactiveHealthIndicatorRegistryFactory(
|
||||
this.healthIndicatorNameFactory);
|
||||
return new CompositeReactiveHealthIndicator(healthAggregator,
|
||||
factory.createReactiveHealthIndicatorRegistry(reactiveHealthIndicators,
|
||||
healthIndicators));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveHealthIndicatorRegistry}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class DefaultReactiveHealthIndicatorRegistry
|
||||
implements ReactiveHealthIndicatorRegistry {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final Map<String, ReactiveHealthIndicator> healthIndicators;
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultReactiveHealthIndicatorRegistry}.
|
||||
*/
|
||||
public DefaultReactiveHealthIndicatorRegistry() {
|
||||
this(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultReactiveHealthIndicatorRegistry} from the specified
|
||||
* indicators.
|
||||
* @param healthIndicators a map of {@link HealthIndicator}s with the key
|
||||
* being used as an indicator name.
|
||||
*/
|
||||
public DefaultReactiveHealthIndicatorRegistry(
|
||||
Map<String, ReactiveHealthIndicator> healthIndicators) {
|
||||
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
|
||||
this.healthIndicators = new LinkedHashMap<>(healthIndicators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, ReactiveHealthIndicator healthIndicator) {
|
||||
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
synchronized (this.monitor) {
|
||||
ReactiveHealthIndicator existing = this.healthIndicators.putIfAbsent(name,
|
||||
healthIndicator);
|
||||
if (existing != null) {
|
||||
throw new IllegalStateException(
|
||||
"HealthIndicator with name '" + name + "' already registered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHealthIndicator unregister(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
synchronized (this.monitor) {
|
||||
return this.healthIndicators.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHealthIndicator get(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
synchronized (this.monitor) {
|
||||
return this.healthIndicators.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ReactiveHealthIndicator> getAll() {
|
||||
synchronized (this.monitor) {
|
||||
return Collections
|
||||
.unmodifiableMap(new LinkedHashMap<>(this.healthIndicators));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import java.util.Map;
|
||||
public interface HealthIndicatorRegistry {
|
||||
|
||||
/**
|
||||
* Registers the given {@code healthIndicator}, associating it with the
|
||||
* Registers the given {@link HealthIndicator}, associating it with the
|
||||
* given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @param healthIndicator the indicator
|
||||
@@ -41,7 +41,7 @@ public interface HealthIndicatorRegistry {
|
||||
void register(String name, HealthIndicator healthIndicator);
|
||||
|
||||
/**
|
||||
* Unregisters the {@code HealthIndicator} previously registered with the
|
||||
* Unregisters the {@link HealthIndicator} previously registered with the
|
||||
* given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the unregistered indicator, or {@code null} if no indicator was
|
||||
@@ -50,7 +50,7 @@ public interface HealthIndicatorRegistry {
|
||||
HealthIndicator unregister(String name);
|
||||
|
||||
/**
|
||||
* Returns the health indicator registered with the given {@code name}.
|
||||
* Returns the {@link HealthIndicator} registered with the given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the health indicator, or {@code null} if no indicator was
|
||||
* registered with the given {@code name}.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.Map;
|
||||
|
||||
/**
|
||||
* A registry of {@link ReactiveHealthIndicator ReactiveHealthIndicators}.
|
||||
* <p>
|
||||
* Implementations <strong>must</strong> be thread-safe.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public interface ReactiveHealthIndicatorRegistry {
|
||||
|
||||
/**
|
||||
* Registers the given {@link ReactiveHealthIndicator}, associating it with the
|
||||
* given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @param healthIndicator the indicator
|
||||
* @throws IllegalStateException if an indicator with the given {@code name}
|
||||
* is already registered.
|
||||
*/
|
||||
void register(String name, ReactiveHealthIndicator healthIndicator);
|
||||
|
||||
/**
|
||||
* Unregisters the {@link ReactiveHealthIndicator} previously registered with the
|
||||
* given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the unregistered indicator, or {@code null} if no indicator was
|
||||
* found in the registry for the given {@code name}.
|
||||
*/
|
||||
ReactiveHealthIndicator unregister(String name);
|
||||
|
||||
/**
|
||||
* Returns the {@link ReactiveHealthIndicator} registered with the given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the health indicator, or {@code null} if no indicator was
|
||||
* registered with the given {@code name}.
|
||||
*/
|
||||
ReactiveHealthIndicator get(String name);
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the registered health indicators and their names.
|
||||
* The contents of the map do not reflect subsequent changes to the
|
||||
* registry.
|
||||
* @return the snapshot of registered health indicators
|
||||
*/
|
||||
Map<String, ReactiveHealthIndicator> getAll();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link HealthIndicatorRegistry}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class ReactiveHealthIndicatorRegistryFactory {
|
||||
|
||||
private final Function<String, String> healthIndicatorNameFactory;
|
||||
|
||||
public ReactiveHealthIndicatorRegistryFactory(
|
||||
Function<String, String> healthIndicatorNameFactory) {
|
||||
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
|
||||
}
|
||||
|
||||
public ReactiveHealthIndicatorRegistryFactory() {
|
||||
this(new HealthIndicatorNameFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ReactiveHealthIndicatorRegistry} based on the specified health
|
||||
* indicators. Each {@link HealthIndicator} are wrapped to a
|
||||
* {@link HealthIndicatorReactiveAdapter}. If two instances share the same name, the
|
||||
* reactive variant takes precedence.
|
||||
* @param reactiveHealthIndicators the {@link ReactiveHealthIndicator} instances
|
||||
* mapped by name
|
||||
* @param healthIndicators the {@link HealthIndicator} instances mapped by name if
|
||||
* any.
|
||||
* @return a {@link ReactiveHealthIndicator} that delegates to the specified
|
||||
* {@code reactiveHealthIndicators}.
|
||||
*/
|
||||
public ReactiveHealthIndicatorRegistry createReactiveHealthIndicatorRegistry(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
Assert.notNull(reactiveHealthIndicators,
|
||||
"ReactiveHealthIndicators must not be null");
|
||||
return initialize(new DefaultReactiveHealthIndicatorRegistry(),
|
||||
reactiveHealthIndicators, healthIndicators);
|
||||
}
|
||||
|
||||
protected <T extends ReactiveHealthIndicatorRegistry> T initialize(T registry,
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
merge(reactiveHealthIndicators, healthIndicators)
|
||||
.forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
registry.register(name, indicator);
|
||||
});
|
||||
return registry;
|
||||
}
|
||||
|
||||
private Map<String, ReactiveHealthIndicator> merge(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
if (ObjectUtils.isEmpty(healthIndicators)) {
|
||||
return reactiveHealthIndicators;
|
||||
}
|
||||
Map<String, ReactiveHealthIndicator> allIndicators = new LinkedHashMap<>(
|
||||
reactiveHealthIndicators);
|
||||
healthIndicators.forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
allIndicators.computeIfAbsent(name,
|
||||
(n) -> new HealthIndicatorReactiveAdapter(indicator));
|
||||
});
|
||||
return allIndicators;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeReactiveHealthIndicatorFactoryTests {
|
||||
|
||||
private static final Health UP = new Health.Builder().status(Status.UP).build();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -17,6 +17,9 @@
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -38,13 +41,12 @@ public class CompositeReactiveHealthIndicatorTests {
|
||||
|
||||
private OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
|
||||
|
||||
private CompositeReactiveHealthIndicator indicator = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator);
|
||||
|
||||
@Test
|
||||
public void singleIndicator() {
|
||||
this.indicator.addHealthIndicator("test", () -> Mono.just(HEALTHY));
|
||||
StepVerifier.create(this.indicator.health()).consumeNextWith((h) -> {
|
||||
CompositeReactiveHealthIndicator indicator = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator, new DefaultReactiveHealthIndicatorRegistry(
|
||||
Collections.singletonMap("test", () -> Mono.just(HEALTHY))));
|
||||
StepVerifier.create(indicator.health()).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("test");
|
||||
assertThat(h.getDetails().get("test")).isEqualTo(HEALTHY);
|
||||
@@ -53,24 +55,31 @@ public class CompositeReactiveHealthIndicatorTests {
|
||||
|
||||
@Test
|
||||
public void longHealth() {
|
||||
Map<String, ReactiveHealthIndicator> indicators = new HashMap<>();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
this.indicator.addHealthIndicator("test" + i,
|
||||
new TimeoutHealth(10000, Status.UP));
|
||||
indicators.put("test" + i, new TimeoutHealth(10000, Status.UP));
|
||||
}
|
||||
StepVerifier.withVirtualTime(this.indicator::health).expectSubscription()
|
||||
CompositeReactiveHealthIndicator indicator = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator,
|
||||
new DefaultReactiveHealthIndicatorRegistry(indicators));
|
||||
StepVerifier.withVirtualTime(indicator::health).expectSubscription()
|
||||
.thenAwait(Duration.ofMillis(10000)).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).hasSize(50);
|
||||
}).verifyComplete();
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).hasSize(50);
|
||||
}).verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeoutReachedUsesFallback() {
|
||||
this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
|
||||
.addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
|
||||
Map<String, ReactiveHealthIndicator> indicators = new HashMap<>();
|
||||
indicators.put("slow", new TimeoutHealth(10000, Status.UP));
|
||||
indicators.put("fast", new TimeoutHealth(10, Status.UP));
|
||||
CompositeReactiveHealthIndicator indicator = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator,
|
||||
new DefaultReactiveHealthIndicatorRegistry(indicators))
|
||||
.timeoutStrategy(100, UNKNOWN_HEALTH);
|
||||
StepVerifier.create(this.indicator.health()).consumeNextWith((h) -> {
|
||||
StepVerifier.create(indicator.health()).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
|
||||
assertThat(h.getDetails().get("slow")).isEqualTo(UNKNOWN_HEALTH);
|
||||
@@ -80,16 +89,20 @@ public class CompositeReactiveHealthIndicatorTests {
|
||||
|
||||
@Test
|
||||
public void timeoutNotReached() {
|
||||
this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
|
||||
.addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
|
||||
Map<String, ReactiveHealthIndicator> indicators = new HashMap<>();
|
||||
indicators.put("slow", new TimeoutHealth(10000, Status.UP));
|
||||
indicators.put("fast", new TimeoutHealth(10, Status.UP));
|
||||
CompositeReactiveHealthIndicator indicator = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator,
|
||||
new DefaultReactiveHealthIndicatorRegistry(indicators))
|
||||
.timeoutStrategy(20000, null);
|
||||
StepVerifier.withVirtualTime(this.indicator::health).expectSubscription()
|
||||
StepVerifier.withVirtualTime(indicator::health).expectSubscription()
|
||||
.thenAwait(Duration.ofMillis(10000)).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
|
||||
assertThat(h.getDetails().get("slow")).isEqualTo(HEALTHY);
|
||||
assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
|
||||
}).verifyComplete();
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
|
||||
assertThat(h.getDetails().get("slow")).isEqualTo(HEALTHY);
|
||||
assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
static class TimeoutHealth implements ReactiveHealthIndicator {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultReactiveHealthIndicatorRegistry}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DefaultReactiveHealthIndicatorRegistryTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ReactiveHealthIndicator one = mock(ReactiveHealthIndicator.class);
|
||||
|
||||
private ReactiveHealthIndicator two = mock(ReactiveHealthIndicator.class);
|
||||
|
||||
private DefaultReactiveHealthIndicatorRegistry registry;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
given(this.one.health()).willReturn(Mono.just(
|
||||
new Health.Builder().unknown().withDetail("1", "1").build()));
|
||||
given(this.two.health()).willReturn(Mono.just(
|
||||
new Health.Builder().unknown().withDetail("2", "2").build()));
|
||||
this.registry = new DefaultReactiveHealthIndicatorRegistry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register() {
|
||||
this.registry.register("one", this.one);
|
||||
this.registry.register("two", this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(2);
|
||||
assertThat(this.registry.get("one")).isSameAs(this.one);
|
||||
assertThat(this.registry.get("two")).isSameAs(this.two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerAlreadyUsedName() {
|
||||
this.registry.register("one", this.one);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("HealthIndicator with name 'one' already registered");
|
||||
this.registry.register("one", this.two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister() {
|
||||
this.registry.register("one", this.one);
|
||||
this.registry.register("two", this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(2);
|
||||
ReactiveHealthIndicator two = this.registry.unregister("two");
|
||||
assertThat(two).isSameAs(this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterUnknown() {
|
||||
this.registry.register("one", this.one);
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
ReactiveHealthIndicator two = this.registry.unregister("two");
|
||||
assertThat(two).isNull();
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllIsASnapshot() {
|
||||
this.registry.register("one", this.one);
|
||||
Map<String, ReactiveHealthIndicator> snapshot = this.registry.getAll();
|
||||
assertThat(snapshot).containsOnlyKeys("one");
|
||||
this.registry.register("two", this.two);
|
||||
assertThat(snapshot).containsOnlyKeys("one");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllIsImmutable() {
|
||||
this.registry.register("one", this.one);
|
||||
Map<String, ReactiveHealthIndicator> snapshot = this.registry.getAll();
|
||||
|
||||
this.thrown.expect(UnsupportedOperationException.class);
|
||||
snapshot.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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 org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveHealthIndicatorRegistryFactory}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ReactiveHealthIndicatorRegistryFactoryTests {
|
||||
|
||||
private static final Health UP = new Health.Builder().status(Status.UP).build();
|
||||
|
||||
private static final Health DOWN = new Health.Builder().status(Status.DOWN).build();
|
||||
|
||||
private final ReactiveHealthIndicatorRegistryFactory factory = new ReactiveHealthIndicatorRegistryFactory();
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorNameFactory() {
|
||||
ReactiveHealthIndicatorRegistry registry = this.factory.createReactiveHealthIndicatorRegistry(
|
||||
Collections.singletonMap("myHealthIndicator", () -> Mono.just(UP)), null);
|
||||
assertThat(registry.getAll()).containsOnlyKeys("my");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void healthIndicatorIsAdapted() {
|
||||
ReactiveHealthIndicatorRegistry registry = this.factory.createReactiveHealthIndicatorRegistry(
|
||||
Collections.singletonMap("test", () -> Mono.just(UP)),
|
||||
Collections.singletonMap("regular", () -> DOWN));
|
||||
assertThat(registry.getAll()).containsOnlyKeys("test", "regular");
|
||||
StepVerifier.create(registry.get("regular").health()).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.DOWN);
|
||||
assertThat(h.getDetails()).isEmpty();
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user