Skip to content

Commit c530f12

Browse files
terminuxphilwebb
authored andcommitted
Call the value adapter during NamedContributorsMapAdapter construction
Update `NamedContributorsMapAdapter` so that the adapter function is called only once per entry. Prior to this commit, the adapter was called dynamically which made `CompositeHealthContributor` behave differently from a regular `HealthContributor`. See gh-31676
1 parent 5243cb8 commit c530f12

File tree

2 files changed

+44
-16
lines changed

2 files changed

+44
-16
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapter.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,23 +31,29 @@
3131
* @param <V> the value type
3232
* @param <C> the contributor type
3333
* @author Phillip Webb
34+
* @author Guirong Hu
3435
* @see CompositeHealthContributorMapAdapter
3536
* @see CompositeReactiveHealthContributorMapAdapter
3637
*/
3738
abstract class NamedContributorsMapAdapter<V, C> implements NamedContributors<C> {
3839

39-
private final Map<String, V> map;
40-
41-
private final Function<V, ? extends C> valueAdapter;
40+
private final Map<String, C> namedContributorsMap;
4241

4342
NamedContributorsMapAdapter(Map<String, V> map, Function<V, ? extends C> valueAdapter) {
4443
Assert.notNull(map, "Map must not be null");
4544
Assert.notNull(valueAdapter, "ValueAdapter must not be null");
46-
map.keySet().forEach(this::validateKey);
47-
map.values().stream().map(valueAdapter)
48-
.forEach((value) -> Assert.notNull(value, "Map must not contain null values"));
49-
this.map = Collections.unmodifiableMap(new LinkedHashMap<>(map));
50-
this.valueAdapter = valueAdapter;
45+
this.namedContributorsMap = getContributorsMap(map, valueAdapter);
46+
}
47+
48+
private Map<String, C> getContributorsMap(Map<String, V> map, Function<V, ? extends C> valueAdapter) {
49+
Map<String, C> contributorsMap = new LinkedHashMap<>(map.size());
50+
map.forEach((name, value) -> {
51+
this.validateKey(name);
52+
C contributor = adapt(value, valueAdapter);
53+
Assert.notNull(contributor, "Map must not contain null values");
54+
contributorsMap.put(name, contributor);
55+
});
56+
return Collections.unmodifiableMap(contributorsMap);
5157
}
5258

5359
private void validateKey(String value) {
@@ -58,7 +64,7 @@ private void validateKey(String value) {
5864

5965
@Override
6066
public Iterator<NamedContributor<C>> iterator() {
61-
Iterator<Entry<String, V>> iterator = this.map.entrySet().iterator();
67+
Iterator<Entry<String, C>> iterator = this.namedContributorsMap.entrySet().iterator();
6268
return new Iterator<NamedContributor<C>>() {
6369

6470
@Override
@@ -68,20 +74,20 @@ public boolean hasNext() {
6874

6975
@Override
7076
public NamedContributor<C> next() {
71-
Entry<String, V> entry = iterator.next();
72-
return NamedContributor.of(entry.getKey(), adapt(entry.getValue()));
77+
Entry<String, C> entry = iterator.next();
78+
return NamedContributor.of(entry.getKey(), entry.getValue());
7379
}
7480

7581
};
7682
}
7783

7884
@Override
7985
public C getContributor(String name) {
80-
return adapt(this.map.get(name));
86+
return this.namedContributorsMap.get(name);
8187
}
8288

83-
private C adapt(V value) {
84-
return (value != null) ? this.valueAdapter.apply(value) : null;
89+
private C adapt(V value, Function<V, ? extends C> valueAdapter) {
90+
return (value != null) ? valueAdapter.apply(value) : null;
8591
}
8692

8793
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/NamedContributorsMapAdapterTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import java.util.Iterator;
2121
import java.util.LinkedHashMap;
2222
import java.util.Map;
23+
import java.util.concurrent.atomic.AtomicInteger;
2324
import java.util.function.Function;
2425

2526
import org.junit.jupiter.api.Test;
@@ -92,6 +93,22 @@ void getContributorReturnsAdaptedEntry() {
9293
assertThat(adapter.getContributor("two")).isEqualTo("owt");
9394
}
9495

96+
@Test
97+
void eachValueAdapterShouldBeCalledOnlyOnce() {
98+
Map<String, String> map = new LinkedHashMap<>();
99+
map.put("one", "one");
100+
map.put("two", "two");
101+
int callCount = map.size();
102+
103+
AtomicInteger counter = new AtomicInteger(0);
104+
TestNamedContributorsMapAdapter<String> adapter = new TestNamedContributorsMapAdapter<>(map,
105+
(name) -> count(name, counter));
106+
assertThat(adapter.getContributor("one")).isEqualTo("eno");
107+
assertThat(counter.get()).isEqualTo(callCount);
108+
assertThat(adapter.getContributor("two")).isEqualTo("owt");
109+
assertThat(counter.get()).isEqualTo(callCount);
110+
}
111+
95112
@Test
96113
void getContributorWhenNotInMapReturnsNull() {
97114
TestNamedContributorsMapAdapter<String> adapter = createAdapter();
@@ -106,6 +123,11 @@ private TestNamedContributorsMapAdapter<String> createAdapter() {
106123
return adapter;
107124
}
108125

126+
private String count(CharSequence charSequence, AtomicInteger counter) {
127+
counter.incrementAndGet();
128+
return reverse(charSequence);
129+
}
130+
109131
private String reverse(CharSequence charSequence) {
110132
return new StringBuilder(charSequence).reverse().toString();
111133
}

0 commit comments

Comments
 (0)