diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapter.java index 94d8c399b522..fcd4f8e0ea4b 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -31,23 +31,29 @@ * @param the value type * @param the contributor type * @author Phillip Webb + * @author Guirong Hu * @see CompositeHealthContributorMapAdapter * @see CompositeReactiveHealthContributorMapAdapter */ abstract class NamedContributorsMapAdapter implements NamedContributors { - private final Map map; - - private final Function valueAdapter; + private final Map namedContributorsMap; NamedContributorsMapAdapter(Map map, Function valueAdapter) { Assert.notNull(map, "Map must not be null"); Assert.notNull(valueAdapter, "ValueAdapter must not be null"); - map.keySet().forEach(this::validateKey); - map.values().stream().map(valueAdapter) - .forEach((value) -> Assert.notNull(value, "Map must not contain null values")); - this.map = Collections.unmodifiableMap(new LinkedHashMap<>(map)); - this.valueAdapter = valueAdapter; + this.namedContributorsMap = getContributorsMap(map, valueAdapter); + } + + private Map getContributorsMap(Map map, Function valueAdapter) { + Map contributorsMap = new LinkedHashMap<>(map.size()); + map.forEach((name, value) -> { + this.validateKey(name); + C contributor = adapt(value, valueAdapter); + Assert.notNull(contributor, "Map must not contain null values"); + contributorsMap.put(name, contributor); + }); + return Collections.unmodifiableMap(contributorsMap); } private void validateKey(String value) { @@ -58,7 +64,7 @@ private void validateKey(String value) { @Override public Iterator> iterator() { - Iterator> iterator = this.map.entrySet().iterator(); + Iterator> iterator = this.namedContributorsMap.entrySet().iterator(); return new Iterator>() { @Override @@ -68,8 +74,8 @@ public boolean hasNext() { @Override public NamedContributor next() { - Entry entry = iterator.next(); - return NamedContributor.of(entry.getKey(), adapt(entry.getValue())); + Entry entry = iterator.next(); + return NamedContributor.of(entry.getKey(), entry.getValue()); } }; @@ -77,11 +83,11 @@ public NamedContributor next() { @Override public C getContributor(String name) { - return adapt(this.map.get(name)); + return this.namedContributorsMap.get(name); } - private C adapt(V value) { - return (value != null) ? this.valueAdapter.apply(value) : null; + private C adapt(V value, Function valueAdapter) { + return (value != null) ? valueAdapter.apply(value) : null; } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapterTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapterTests.java index 636b3b7f63b7..9e6a23bdb6a0 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapterTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -92,6 +93,22 @@ void getContributorReturnsAdaptedEntry() { assertThat(adapter.getContributor("two")).isEqualTo("owt"); } + @Test + void eachValueAdapterShouldBeCalledOnlyOnce() { + Map map = new LinkedHashMap<>(); + map.put("one", "one"); + map.put("two", "two"); + int callCount = map.size(); + + AtomicInteger counter = new AtomicInteger(0); + TestNamedContributorsMapAdapter adapter = new TestNamedContributorsMapAdapter<>(map, + (name) -> count(name, counter)); + assertThat(adapter.getContributor("one")).isEqualTo("eno"); + assertThat(counter.get()).isEqualTo(callCount); + assertThat(adapter.getContributor("two")).isEqualTo("owt"); + assertThat(counter.get()).isEqualTo(callCount); + } + @Test void getContributorWhenNotInMapReturnsNull() { TestNamedContributorsMapAdapter adapter = createAdapter(); @@ -106,6 +123,11 @@ private TestNamedContributorsMapAdapter createAdapter() { return adapter; } + private String count(CharSequence charSequence, AtomicInteger counter) { + counter.incrementAndGet(); + return reverse(charSequence); + } + private String reverse(CharSequence charSequence) { return new StringBuilder(charSequence).reverse().toString(); }