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"));
+        }
+    }
+}

Reply via email to