Skip to content

Commit 7004a26

Browse files
vasiliy-sarzhynskyivsarzhynskyi
andauthored
GH-1531: fixed NPE in MicrometerHolder during app shutdown (#1532)
* fixed NPE issue in MicrometerHolder during app shutdown (#1531) * fixed build to be compatible with Java 8 * fixed checkstyle issues Co-authored-by: vsarzhynskyi <[email protected]>
1 parent d76781d commit 7004a26

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

spring-kafka/src/main/java/org/springframework/kafka/support/micrometer/MicrometerHolder.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
*/
3737
public final class MicrometerHolder {
3838

39+
private static final String NONE_EXCEPTION_METERS_KEY = "none";
40+
3941
private final Map<String, Timer> meters = new ConcurrentHashMap<>();
4042

4143
private final MeterRegistry registry;
@@ -69,7 +71,7 @@ public MicrometerHolder(@Nullable ApplicationContext context, String name,
6971
this.tags = tags;
7072
if (registries.size() == 1) {
7173
this.registry = registries.values().iterator().next();
72-
buildTimer("none");
74+
buildTimer(NONE_EXCEPTION_METERS_KEY);
7375
}
7476
else {
7577
throw new IllegalStateException("No micrometer registry present (or more than one)");
@@ -86,11 +88,14 @@ public Object start() {
8688

8789
/**
8890
* Record success.
89-
* @param sample ths sample.
91+
* @param sample the sample.
9092
* @see #start()
9193
*/
9294
public void success(Object sample) {
93-
((Sample) sample).stop(this.meters.get("none"));
95+
Timer timer = this.meters.get(NONE_EXCEPTION_METERS_KEY);
96+
if (timer != null) {
97+
((Sample) sample).stop(timer);
98+
}
9499
}
95100

96101
/**
@@ -111,10 +116,10 @@ private Timer buildTimer(String exception) {
111116
Builder builder = Timer.builder(this.timerName)
112117
.description(this.timerDesc)
113118
.tag("name", this.name)
114-
.tag("result", exception.equals("none") ? "success" : "failure")
119+
.tag("result", exception.equals(NONE_EXCEPTION_METERS_KEY) ? "success" : "failure")
115120
.tag("exception", exception);
116121
if (this.tags != null && !this.tags.isEmpty()) {
117-
this.tags.forEach((key, value) -> builder.tag(key, value));
122+
this.tags.forEach(builder::tag);
118123
}
119124
Timer registeredTimer = builder.register(this.registry);
120125
this.meters.put(exception, registeredTimer);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.kafka.support.micrometer;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.ArgumentMatchers.anyBoolean;
22+
import static org.mockito.BDDMockito.given;
23+
import static org.mockito.Mockito.mock;
24+
import static org.mockito.Mockito.times;
25+
import static org.mockito.Mockito.verify;
26+
import static org.mockito.Mockito.verifyNoMoreInteractions;
27+
28+
import java.util.Collections;
29+
import java.util.Map;
30+
31+
import org.junit.jupiter.api.Test;
32+
33+
import org.springframework.context.ApplicationContext;
34+
import org.springframework.test.util.ReflectionTestUtils;
35+
36+
import io.micrometer.core.instrument.MeterRegistry;
37+
import io.micrometer.core.instrument.Timer;
38+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
39+
40+
/**
41+
* @author Vasyl Sarzhynskyi
42+
*/
43+
public class MicrometerHolderTests {
44+
45+
@SuppressWarnings({ "unchecked" })
46+
@Test
47+
public void testMicrometerHolderRecordSuccessWorksGracefullyAfterDestroy() {
48+
MeterRegistry meterRegistry = new SimpleMeterRegistry();
49+
ApplicationContext ctx = mock(ApplicationContext.class);
50+
Timer.Sample sample = mock(Timer.Sample.class);
51+
given(ctx.getBeansOfType(any(), anyBoolean(), anyBoolean())).willReturn(Collections.singletonMap("registry", meterRegistry));
52+
53+
MicrometerHolder micrometerHolder = new MicrometerHolder(ctx, "holderName",
54+
"timerName", "timerDesc", Collections.emptyMap());
55+
Map<String, Timer> meters = (Map<String, Timer>) ReflectionTestUtils.getField(micrometerHolder, "meters");
56+
assertThat(meters).hasSize(1);
57+
58+
micrometerHolder.success(sample);
59+
micrometerHolder.destroy();
60+
61+
meters = (Map<String, Timer>) ReflectionTestUtils.getField(micrometerHolder, "meters");
62+
assertThat(meters).hasSize(0);
63+
64+
micrometerHolder.success(sample);
65+
66+
verify(ctx, times(1)).getBeansOfType(any(), anyBoolean(), anyBoolean());
67+
verify(sample, times(1)).stop(any());
68+
verifyNoMoreInteractions(ctx, sample);
69+
}
70+
71+
}

0 commit comments

Comments
 (0)