This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new d538346d757 CXF-8803: Support Dropwizard Metrics 5 (#2819)
d538346d757 is described below
commit d538346d757bcfc7d9e4a3cd44e06f2060acfd59
Author: Andriy Redko <[email protected]>
AuthorDate: Wed Jan 14 21:31:04 2026 -0500
CXF-8803: Support Dropwizard Metrics 5 (#2819)
---
distribution/javadoc/pom.xml | 8 ++
parent/pom.xml | 11 ++
rt/features/metrics/pom.xml | 12 ++
.../dropwizard/DropwizardMetricsContext.java | 159 +++++++++++++++++++++
.../dropwizard/DropwizardMetricsProvider.java | 134 +++++++++++++++++
.../dropwizard/DropwizardMetricsProviderTest.java | 142 ++++++++++++++++++
6 files changed, 466 insertions(+)
diff --git a/distribution/javadoc/pom.xml b/distribution/javadoc/pom.xml
index ad9534813ca..8ee895a5581 100644
--- a/distribution/javadoc/pom.xml
+++ b/distribution/javadoc/pom.xml
@@ -382,6 +382,14 @@
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-jmx</artifactId>
+ </dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
diff --git a/parent/pom.xml b/parent/pom.xml
index ef2dc4c1ce9..2c6fc7a7528 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -111,6 +111,7 @@
<cxf.dom4j.version>2.2.0</cxf.dom4j.version>
<cxf.dropwizard3.version>3.2.6</cxf.dropwizard3.version>
<cxf.dropwizard4.version>4.2.37</cxf.dropwizard4.version>
+ <cxf.dropwizard5.version>5.0.5</cxf.dropwizard5.version>
<cxf.ehcache3.version>3.11.1</cxf.ehcache3.version>
<cxf.el-api.version>6.0.1</cxf.el-api.version>
<cxf.jvnet.jaxb.version>4.0.12</cxf.jvnet.jaxb.version>
@@ -1895,6 +1896,16 @@
<artifactId>metrics-jmx</artifactId>
<version>${cxf.dropwizard4.version}</version>
</dependency>
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>${cxf.dropwizard5.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-jmx</artifactId>
+ <version>${cxf.dropwizard5.version}</version>
+ </dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
diff --git a/rt/features/metrics/pom.xml b/rt/features/metrics/pom.xml
index 3354629508b..5c1271bd9c6 100644
--- a/rt/features/metrics/pom.xml
+++ b/rt/features/metrics/pom.xml
@@ -26,6 +26,18 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-core</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>io.dropwizard.metrics5</groupId>
+ <artifactId>metrics-jmx</artifactId>
+ <optional>true</optional>
+ </dependency>
+
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
diff --git
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsContext.java
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsContext.java
new file mode 100644
index 00000000000..f9170ff0307
--- /dev/null
+++
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsContext.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.metrics.dropwizard;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.FaultMode;
+import org.apache.cxf.metrics.MetricsContext;
+
+import io.dropwizard.metrics5.Counter;
+import io.dropwizard.metrics5.Meter;
+import io.dropwizard.metrics5.MetricName;
+import io.dropwizard.metrics5.MetricRegistry;
+import io.dropwizard.metrics5.Timer;
+
+/**
+ * Dropwizard 5 Metrics Context
+ */
+public class DropwizardMetricsContext implements MetricsContext, Closeable {
+ protected Counter inFlight;
+ protected Timer totals;
+ protected Timer uncheckedApplicationFaults;
+ protected Timer checkedApplicationFaults;
+ protected Timer runtimeFaults;
+ protected Timer logicalRuntimeFaults;
+ protected Meter incomingData;
+ protected Meter outgoingData;
+
+ protected final String baseName;
+ protected final MetricRegistry registry;
+
+ public DropwizardMetricsContext(String prefix, MetricRegistry registry) {
+ baseName = prefix;
+ this.registry = registry;
+ totals = registry.timer(baseName + "Attribute=Totals");
+ uncheckedApplicationFaults = registry.timer(baseName
+ + "Attribute=Unchecked
Application Faults");
+ checkedApplicationFaults = registry.timer(baseName +
"Attribute=Checked Application Faults");
+ runtimeFaults = registry.timer(baseName + "Attribute=Runtime Faults");
+ logicalRuntimeFaults = registry.timer(baseName + "Attribute=Logical
Runtime Faults");
+ inFlight = registry.counter(baseName + "Attribute=In Flight");
+ incomingData = registry.meter(baseName + "Attribute=Data Read");
+ outgoingData = registry.meter(baseName + "Attribute=Data Written");
+ }
+
+ @Override
+ public void close() throws IOException {
+ registry.remove(MetricName.build(baseName, "Attribute=Totals"));
+ registry.remove(MetricName.build(baseName, "Attribute=Unchecked
Application Faults"));
+ registry.remove(MetricName.build(baseName, baseName +
"Attribute=Checked Application Faults"));
+ registry.remove(MetricName.build(baseName, baseName +
"Attribute=Runtime Faults"));
+ registry.remove(MetricName.build(baseName, baseName +
"Attribute=Logical Runtime Faults"));
+ registry.remove(MetricName.build(baseName, baseName + "Attribute=In
Flight"));
+ registry.remove(MetricName.build(baseName, baseName + "Attribute=Data
Read"));
+ registry.remove(MetricName.build(baseName, baseName + "Attribute=Data
Written"));
+ }
+
+
+ public void start(Exchange ex) {
+ inFlight.inc();
+ }
+
+ public void stop(long timeInNS, long inSize, long outSize, Exchange ex) {
+ totals.update(timeInNS, TimeUnit.NANOSECONDS);
+
+ if (inSize != -1) {
+ incomingData.mark(inSize);
+ }
+ if (outSize != -1) {
+ outgoingData.mark(outSize);
+ }
+ FaultMode fm = ex.get(FaultMode.class);
+ if (fm == null && ex.getOutFaultMessage() != null) {
+ fm = ex.getOutFaultMessage().get(FaultMode.class);
+ }
+ if (fm == null && ex.getInMessage() != null) {
+ fm = ex.getInMessage().get(FaultMode.class);
+ }
+ if (fm != null) {
+ switch (fm) {
+ case CHECKED_APPLICATION_FAULT:
+ checkedApplicationFaults.update(timeInNS,
TimeUnit.NANOSECONDS);
+ break;
+ case UNCHECKED_APPLICATION_FAULT:
+ uncheckedApplicationFaults.update(timeInNS,
TimeUnit.NANOSECONDS);
+ break;
+ case RUNTIME_FAULT:
+ runtimeFaults.update(timeInNS, TimeUnit.NANOSECONDS);
+ break;
+ case LOGICAL_RUNTIME_FAULT:
+ logicalRuntimeFaults.update(timeInNS, TimeUnit.NANOSECONDS);
+ break;
+ default:
+ }
+ }
+ inFlight.dec();
+ }
+
+ public Counter getInFlight() {
+ return inFlight;
+ }
+
+ public Timer getTotals() {
+ return totals;
+ }
+
+ public Timer getUncheckedApplicationFaults() {
+ return uncheckedApplicationFaults;
+ }
+
+ public Timer getCheckedApplicationFaults() {
+ return checkedApplicationFaults;
+ }
+
+ public Timer getRuntimeFaults() {
+ return runtimeFaults;
+ }
+
+ public Timer getLogicalRuntimeFaults() {
+ return logicalRuntimeFaults;
+ }
+
+ public Meter getIncomingData() {
+ return incomingData;
+ }
+
+ public Meter getOutgoingData() {
+ return outgoingData;
+ }
+
+ public String getBaseName() {
+ return baseName;
+ }
+
+ public MetricRegistry getRegistry() {
+ return registry;
+ }
+
+}
diff --git
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProvider.java
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProvider.java
new file mode 100644
index 00000000000..d656e24649e
--- /dev/null
+++
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProvider.java
@@ -0,0 +1,134 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.metrics.dropwizard;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.management.InstrumentationManager;
+import org.apache.cxf.management.ManagementConstants;
+import org.apache.cxf.metrics.MetricsContext;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.service.model.BindingOperationInfo;
+
+import io.dropwizard.metrics5.MetricName;
+import io.dropwizard.metrics5.MetricRegistry;
+import io.dropwizard.metrics5.jmx.JmxReporter;
+import io.dropwizard.metrics5.jmx.ObjectNameFactory;
+
+/**
+ * Dropwizard 5 Metrics Provider
+ */
+@NoJSR250Annotations
+public class DropwizardMetricsProvider implements MetricsProvider {
+ private static final String QUESTION_MARK = "?";
+ private static final String ESCAPED_QUESTION_MARK = "\\?";
+
+ protected Bus bus;
+ protected MetricRegistry registry;
+
+ public DropwizardMetricsProvider(Bus b) {
+ bus = b;
+ registry = b.getExtension(MetricRegistry.class);
+ if (registry == null) {
+ registry = new MetricRegistry();
+ setupJMXReporter(b, registry);
+ }
+
+ }
+
+ public static void setupJMXReporter(Bus b, MetricRegistry reg) {
+ InstrumentationManager im =
b.getExtension(InstrumentationManager.class);
+ if (im != null) {
+ JmxReporter reporter =
JmxReporter.forRegistry(reg).registerWith(im.getMBeanServer())
+ .inDomain("org.apache.cxf")
+ .createsObjectNamesWith(new ObjectNameFactory() {
+ public ObjectName createName(String type, String domain,
MetricName name) {
+ try {
+ return new ObjectName(name.getKey());
+ } catch (MalformedObjectNameException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ })
+ .build();
+ reporter.start();
+ }
+ }
+
+ protected String escapePatternChars(String value) {
+ // This can be replaced if really needed with pattern-based matching
+ if (value.lastIndexOf(QUESTION_MARK) != -1) {
+ value = value.replace(QUESTION_MARK, ESCAPED_QUESTION_MARK);
+ }
+ return value;
+ }
+
+ StringBuilder getBaseServiceName(Endpoint endpoint, boolean isClient,
String clientId) {
+ StringBuilder buffer = new StringBuilder();
+ if (endpoint.get("org.apache.cxf.management.service.counter.name") !=
null) {
+
buffer.append(endpoint.get("org.apache.cxf.management.service.counter.name"));
+ } else {
+ buffer.append(ManagementConstants.DEFAULT_DOMAIN_NAME).append(':');
+
buffer.append(ManagementConstants.BUS_ID_PROP).append('=').append(bus.getId()).append(',');
+ buffer.append(ManagementConstants.TYPE_PROP).append("=Metrics");
+ buffer.append(isClient ? ".Client," : ".Server,");
+ buffer.append(ManagementConstants.SERVICE_NAME_PROP)
+
.append("=\"").append(escapePatternChars(endpoint.getService().getName().toString())).append("\",");
+ buffer.append(ManagementConstants.PORT_NAME_PROP)
+
.append("=\"").append(endpoint.getEndpointInfo().getName().getLocalPart()).append("\",");
+ if (clientId != null) {
+ buffer.append("Client=").append(clientId).append(',');
+ }
+ }
+ return buffer;
+ }
+
+
+ /** {@inheritDoc}*/
+ @Override
+ public MetricsContext createEndpointContext(final Endpoint endpoint,
boolean isClient, String clientId) {
+ StringBuilder buffer = getBaseServiceName(endpoint, isClient,
clientId);
+ final String baseName = buffer.toString();
+ return new DropwizardMetricsContext(baseName, registry);
+ }
+
+ /** {@inheritDoc}*/
+ @Override
+ public MetricsContext createOperationContext(Endpoint endpoint,
BindingOperationInfo boi,
+ boolean asClient, String
clientId) {
+ StringBuilder buffer = getBaseServiceName(endpoint, asClient,
clientId);
+
buffer.append("Operation=").append(boi.getName().getLocalPart()).append(',');
+ return new DropwizardMetricsContext(buffer.toString(), registry);
+ }
+
+ /** {@inheritDoc}*/
+ @Override
+ public MetricsContext createResourceContext(Endpoint endpoint, String
resourceName,
+ boolean asClient, String
clientId) {
+ StringBuilder buffer = getBaseServiceName(endpoint, asClient,
clientId);
+ buffer.append("Operation=").append(resourceName).append(',');
+ return new DropwizardMetricsContext(buffer.toString(), registry);
+ }
+
+}
diff --git
a/rt/features/metrics/src/test/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProviderTest.java
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProviderTest.java
new file mode 100644
index 00000000000..de4a2981423
--- /dev/null
+++
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/dropwizard/DropwizardMetricsProviderTest.java
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.metrics.dropwizard;
+
+import java.io.IOException;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.extension.ExtensionManagerBus;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.endpoint.EndpointException;
+import org.apache.cxf.endpoint.EndpointImpl;
+import org.apache.cxf.metrics.MetricsContext;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.ServiceImpl;
+import org.apache.cxf.service.model.BindingInfo;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.service.model.OperationInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+
+import io.dropwizard.metrics5.MetricRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class DropwizardMetricsProviderTest {
+
+ private DropwizardMetricsProvider provider;
+ private Bus bus;
+ private Endpoint endpoint;
+ private MetricRegistry registry;
+ private BindingOperationInfo boi;
+
+ @Before
+ public void setUp() throws EndpointException {
+ registry = new MetricRegistry();
+
+ bus = new ExtensionManagerBus();
+ bus.setExtension(registry, MetricRegistry.class);
+
+ provider = new DropwizardMetricsProvider(bus);
+
+ final ServiceInfo si = new ServiceInfo();
+ si.setName(new QName("http://www.apache.org", "ServiceName"));
+ final Service s = new ServiceImpl(si);
+ final EndpointInfo endpointInfo = new EndpointInfo();
+ endpointInfo.setName(si.getName());
+
+ final OperationInfo opinfo = new OperationInfo();
+ opinfo.setName(si.getName());
+
+ boi = new BindingOperationInfo(new BindingInfo(si, "test"), opinfo);
+ endpoint = new EndpointImpl(bus, s, endpointInfo);
+ }
+
+ @After
+ public void tearDown() {
+ bus.shutdown(true);
+ }
+
+ @Test
+ public void testCreateEndpointContext() throws IOException {
+ final MetricsContext actual = provider.createEndpointContext(endpoint,
true, "clientId");
+ assertThat(actual, instanceOf(DropwizardMetricsContext.class));
+
+ try (DropwizardMetricsContext context = (DropwizardMetricsContext)
actual) {
+ assertThat(context.getRegistry(), is(registry));
+ assertThat(context.getBaseName(),
containsString("Metrics.Client"));
+ }
+ }
+
+ @Test
+ public void testCreateServerOperationContext() throws IOException {
+ final MetricsContext actual =
provider.createOperationContext(endpoint, boi, false, "clientId");
+ assertThat(actual, instanceOf(DropwizardMetricsContext.class));
+
+ try (DropwizardMetricsContext context = (DropwizardMetricsContext)
actual) {
+ assertThat(context.getRegistry(), is(registry));
+ assertThat(context.getBaseName(),
containsString("Metrics.Server"));
+ }
+ }
+
+ @Test
+ public void testCreateClientOperationContext() throws IOException {
+ final MetricsContext actual =
provider.createOperationContext(endpoint, boi, true, "clientId");
+ assertThat(actual, instanceOf(DropwizardMetricsContext.class));
+
+ try (DropwizardMetricsContext context = (DropwizardMetricsContext)
actual) {
+ assertThat(context.getRegistry(), is(registry));
+ assertThat(context.getBaseName(),
containsString("Metrics.Client"));
+ }
+ }
+
+ @Test
+ public void testCreateServerResourceContext() throws IOException {
+ final MetricsContext actual = provider.createResourceContext(endpoint,
"resourceName", false, "clientId");
+ assertThat(actual, instanceOf(DropwizardMetricsContext.class));
+
+ try (DropwizardMetricsContext context = (DropwizardMetricsContext)
actual) {
+ assertThat(context.getRegistry(), is(registry));
+ assertThat(context.getBaseName(),
containsString("Metrics.Server"));
+ }
+ }
+
+ @Test
+ public void testCreateClientResourceContext() throws IOException {
+ final MetricsContext actual = provider.createResourceContext(endpoint,
"resourceName", true, "clientId");
+ assertThat(actual, instanceOf(DropwizardMetricsContext.class));
+
+ // then
+ // then
+ try (DropwizardMetricsContext context = (DropwizardMetricsContext)
actual) {
+ assertThat(context.getRegistry(), is(registry));
+ assertThat(context.getBaseName(),
containsString("Metrics.Client"));
+ }
+ }
+}