From 025c6575e6e8e0e3e1e1a03834f63f82e97972da Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 4 Dec 2024 08:43:01 -0600 Subject: [PATCH] Fix possible NPE if code creates a REST client 'early'; also tidy up a few other things (#9554) --- .../RestClientMetricsCdiExtension.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/microprofile/rest-client-metrics/src/main/java/io/helidon/microprofile/restclientmetrics/RestClientMetricsCdiExtension.java b/microprofile/rest-client-metrics/src/main/java/io/helidon/microprofile/restclientmetrics/RestClientMetricsCdiExtension.java index 12025f410fd..bdcab01f713 100644 --- a/microprofile/rest-client-metrics/src/main/java/io/helidon/microprofile/restclientmetrics/RestClientMetricsCdiExtension.java +++ b/microprofile/rest-client-metrics/src/main/java/io/helidon/microprofile/restclientmetrics/RestClientMetricsCdiExtension.java @@ -20,6 +20,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -27,6 +28,7 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -39,7 +41,6 @@ import jakarta.enterprise.inject.spi.AfterBeanDiscovery; import jakarta.enterprise.inject.spi.AnnotatedMethod; import jakarta.enterprise.inject.spi.AnnotatedType; -import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; import jakarta.enterprise.inject.spi.ProcessAnnotatedType; import jakarta.enterprise.inject.spi.WithAnnotations; @@ -84,6 +85,10 @@ public class RestClientMetricsCdiExtension implements Extension { private final Map, Map>> registrations = new HashMap<>(); + // Some code might create REST clients before CDI announces that app-scoped beans are initialized. + // We record those "early" REST clients and set up metrics for them only when we are ready. + private final Collection> deferredRestClients = new ConcurrentLinkedQueue<>(); + private MetricRegistry metricRegistry; /** @@ -92,10 +97,6 @@ public class RestClientMetricsCdiExtension implements Extension { public RestClientMetricsCdiExtension() { } - void checkForMpMetrics(@Observes BeforeBeanDiscovery bbd) { - - } - void recordRestClientTypes(@Observes @WithAnnotations({OPTIONS.class, HEAD.class, GET.class, @@ -218,20 +219,25 @@ void prepareMetricRegistrations(@Observes AfterBeanDiscovery abd) { void ready(@Observes @Initialized(ApplicationScoped.class) Object event, MetricRegistry metricRegistry) { this.metricRegistry = metricRegistry; + deferredRestClients.forEach(this::registerMetricsForRestClient); + deferredRestClients.clear(); } void registerMetricsForRestClient(Class restClient) { + if (metricRegistry == null) { + deferredRestClients.add(restClient); + return; + } registrations.get(restClient).forEach((method, regs) -> { List metricsRegisteredForRestClient = LOGGER.isLoggable(DEBUG) ? new ArrayList<>() : null; regs.forEach(registration -> { Metric metric = registration.registrationOp.apply(metricRegistry); - if (LOGGER.isLoggable(DEBUG)) { + if (metricsRegisteredForRestClient != null) { metricsRegisteredForRestClient.add(metric); LOGGER.log(DEBUG, String.format("For REST client method %s#%s registering metric using %s", restClient.getCanonicalName(), method.getName(), - registration, - metric)); + registration)); } metricsUpdateWorkByMethod.computeIfAbsent(method, k -> new ArrayList<>()) .add(MetricsUpdateWork.create(metric));