diff --git a/microprofile/metrics/pom.xml b/microprofile/metrics/pom.xml
index 35b8f731ad7..49ea791c52c 100644
--- a/microprofile/metrics/pom.xml
+++ b/microprofile/metrics/pom.xml
@@ -94,6 +94,8 @@
**/HelloWorldAsyncResponseTest.java
+ **/TestExtendedKPIMetrics.java
+ **/TestMetricsOnOwnSocket.java
@@ -106,6 +108,8 @@
**/HelloWorldAsyncResponseTest.java
+ **/TestExtendedKPIMetrics.java
+ **/TestMetricsOnOwnSocket.java
diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java
new file mode 100644
index 00000000000..e289f4b4894
--- /dev/null
+++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.metrics;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.Set;
+
+@ApplicationScoped
+@ApplicationPath("/")
+public class HelloWorldApp extends Application {
+
+ @Override
+ public Set> getClasses() {
+ return Set.of(HelloWorldResource.class);
+ }
+}
diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java
new file mode 100644
index 00000000000..b0ed7e6ba6d
--- /dev/null
+++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.metrics;
+
+import io.helidon.common.http.Http;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+
+import javax.inject.Inject;
+import javax.json.JsonObject;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import static io.helidon.metrics.KeyPerformanceIndicatorMetricsSettings.Builder.KEY_PERFORMANCE_INDICATORS_CONFIG_KEY;
+import static io.helidon.metrics.KeyPerformanceIndicatorMetricsSettings.Builder.KEY_PERFORMANCE_INDICATORS_EXTENDED_CONFIG_KEY;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+@HelidonTest
+@AddConfig(key =
+ "metrics."
+ + KEY_PERFORMANCE_INDICATORS_CONFIG_KEY
+ + "." + KEY_PERFORMANCE_INDICATORS_EXTENDED_CONFIG_KEY,
+ value = "true")
+@AddConfig(key = "server.executor-service.core-pool-size", value = "1")
+@AddConfig(key = "server.executor-service.max-pool-size", value = "1")
+@AddBean(HelloWorldApp.class)
+public class TestExtendedKPIMetrics {
+
+ @Inject
+ WebTarget webTarget;
+
+
+ @Test
+ void checkNonZeroDeferredTime() throws ExecutionException, InterruptedException {
+ // Run two requests concurrently, with the server configured for one thread, to force one request to be deferred.
+ Future response1Future = webTarget
+ .path("helloworld")
+ .request()
+ .accept(MediaType.TEXT_PLAIN)
+ .buildGet()
+ .submit();
+
+ Future response2Future = webTarget
+ .path("helloworld")
+ .request()
+ .accept(MediaType.TEXT_PLAIN)
+ .buildGet()
+ .submit();
+
+ // Now wait for both requests to finish.
+ try (Response response1 = response1Future.get(); Response response2 = response2Future.get()) {
+ assertThat("Access to GET response 1", response1.getStatus(), is(Http.Status.OK_200.code()));
+ assertThat("Access to GET response 2", response2.getStatus(), is(Http.Status.OK_200.code()));
+ }
+
+ try (Response metricsResponse = webTarget
+ .path("metrics/vendor")
+ .request()
+ .accept(MediaType.APPLICATION_JSON_TYPE)
+ .get()) {
+
+ assertThat("Metrics /metrics/vendor URL HTTP status", metricsResponse.getStatus(), is(Http.Status.OK_200.code()));
+
+ JsonObject vendorMetrics = metricsResponse.readEntity(JsonObject.class);
+
+ assertThat("Extended KPI metric requests.deferred present", vendorMetrics.containsKey("requests.deferred"),
+ is(true));
+
+ JsonObject requestsDeferred = vendorMetrics.getJsonObject("requests.deferred");
+ assertThat("requests.deferred", requestsDeferred, is(notNullValue()));
+
+ int deferredCount = requestsDeferred.getInt("count");
+ assertThat("Extended KPI metric requests.deferred->count value", deferredCount, is(greaterThan(0)));
+ }
+ }
+}
diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java
index 23a4eb490a7..1c5782f0d0b 100644
--- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java
+++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -103,6 +104,8 @@ public class ServerCdiExtension implements Extension {
private final Map, RoutingConfiguration> serviceBeans
= Collections.synchronizedMap(new IdentityHashMap<>());
+ private final Set routingsWithKPIMetrics = new HashSet<>();
+
private void buildTime(@Observes @BuildTimeStart Object event) {
// update the status of server, as we may have been started without a builder being used
// such as when cdi.Main or SeContainerInitializer are used
@@ -146,19 +149,16 @@ private void recordBeanServices(@Observes ProcessManagedBean extends Service>
private void registerKpiMetricsDeferrableRequestContextSetterHandler(JaxRsCdiExtension jaxRs,
JaxRsApplication applicationMeta) {
- Optional contextRoot = jaxRs.findContextRoot(config, applicationMeta);
Optional namedRouting = jaxRs.findNamedRouting(config, applicationMeta);
boolean routingNameRequired = jaxRs.isNamedRoutingRequired(config, applicationMeta);
Routing.Builder routing = routingBuilder(namedRouting, routingNameRequired, applicationMeta.appName());
- if (contextRoot.isPresent()) {
- String contextRootString = contextRoot.get();
- routing.any(contextRootString, KeyPerformanceIndicatorSupport.DeferrableRequestContext.CONTEXT_SETTING_HANDLER);
- } else {
- LOGGER.finer(() ->
- "JAX-RS application " + applicationMeta.appName() + " adding deferrable request KPI metrics context on '/'");
+ if (!routingsWithKPIMetrics.contains(routing)) {
+ routingsWithKPIMetrics.add(routing);
routing.any(KeyPerformanceIndicatorSupport.DeferrableRequestContext.CONTEXT_SETTING_HANDLER);
+ LOGGER.finer(() -> String.format("Adding deferrable request KPI metrics context for routing with name '%s'",
+ namedRouting.orElse("")));
}
}