This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit a1531cfe652275d85f061fc05ac9a9d62aaa3919
Author: Andriy Redko <[email protected]>
AuthorDate: Sun Aug 10 09:04:34 2025 -0400

    CXF-9152: Adding @Timed annotation to JaxWS WebService class breaks 
Micrometer integration (#2539)
    
    (cherry picked from commit d66e4b60fbc0fe153a7a1dec2d67e2b21b6478e6)
    (cherry picked from commit 073a3a0feb96f16a42a01800aeb151a8cbceeac3)
---
 .../SpringBasedTimedAnnotationProvider.java        |  11 +
 .../micrometer/MicrometerMetricsContext.java       |  25 +-
 .../provider/DefaultTimedAnnotationProvider.java   |  11 +
 .../provider/TimedAnnotationProvider.java          |   5 +
 .../MicrometerClientMetricsContextTest.java        |   2 +-
 .../MicrometerServerMetricsContextTest.java        |  70 +++++-
 .../resources/HelloServiceEmptyTimedImpl.java      |  19 +-
 .../jaxws/resources/HelloServiceTimedImpl.java     |  19 +-
 .../jaxws/spring/boot/SpringJaxwsTimedTest.java    | 271 +++++++++++++++++++++
 9 files changed, 413 insertions(+), 20 deletions(-)

diff --git 
a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/micrometer/provider/SpringBasedTimedAnnotationProvider.java
 
b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/micrometer/provider/SpringBasedTimedAnnotationProvider.java
index 3533e23bc1..5c763f8446 100644
--- 
a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/micrometer/provider/SpringBasedTimedAnnotationProvider.java
+++ 
b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/micrometer/provider/SpringBasedTimedAnnotationProvider.java
@@ -22,6 +22,7 @@ package 
org.apache.cxf.spring.boot.autoconfigure.micrometer.provider;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Method;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -66,6 +67,16 @@ public class SpringBasedTimedAnnotationProvider implements 
TimedAnnotationProvid
         });
     }
 
+    @Override
+    public Optional<String> getDefaultMetricName(Exchange ex, boolean client) {
+        final HandlerMethod handlerMethod = HandlerMethod.create(ex, client);
+        if (handlerMethod == null) {
+            return Optional.empty();
+        } else {
+            return Optional.of(handlerMethod.method.getName());
+        }
+    }
+
     Set<Timed> findTimedAnnotations(AnnotatedElement element) {
         return MergedAnnotations.from(element).stream(Timed.class)
                 .collect(MergedAnnotationCollectors.toAnnotationSet());
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/MicrometerMetricsContext.java
 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/MicrometerMetricsContext.java
index 955b016377..aaa10c5913 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/MicrometerMetricsContext.java
+++ 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/MicrometerMetricsContext.java
@@ -20,6 +20,7 @@
 package org.apache.cxf.metrics.micrometer;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Supplier;
 import java.util.logging.Logger;
@@ -99,15 +100,27 @@ abstract class MicrometerMetricsContext implements 
MetricsContext {
         Timer.Sample timerSample = timingContext.getTimerSample();
         Supplier<Iterable<Tag>> tags = () -> getAllTags(ex);
 
-        if (annotations.isEmpty()) {
-            if (this.autoTimeRequests) {
-                stop(timerSample, tags, Timer.builder(this.metricName));
-            }
-        } else {
+        boolean defaultMetricReported = false;
+        if (!annotations.isEmpty()) {
+            final Optional<String> defaultMetricNameOpt = 
timedAnnotationProvider
+                .getDefaultMetricName(ex, client);
+
             for (Timed annotation : annotations) {
-                stop(timerSample, tags, Timer.builder(annotation, 
this.metricName));
+                // Edge case, the @Timed without value would be reported as a 
default one.
+                // We should not report the same metric more than once.
+                if (annotation.value().isEmpty() && defaultMetricReported) {
+                    continue;
+                }
+                
+                defaultMetricReported |= defaultMetricNameOpt.isEmpty() && 
annotation.value().isEmpty();
+                stop(timerSample, tags, Timer.builder(annotation, 
+                        defaultMetricNameOpt.orElse(this.metricName)));        
        
             }
         }
+        
+        if (this.autoTimeRequests && !defaultMetricReported) {
+            stop(timerSample, tags, Timer.builder(this.metricName));
+        }
     }
 
     private void startAndAttachTimingContext(Message request) {
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/DefaultTimedAnnotationProvider.java
 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/DefaultTimedAnnotationProvider.java
index 03afd292be..2d5b63b8cb 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/DefaultTimedAnnotationProvider.java
+++ 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/DefaultTimedAnnotationProvider.java
@@ -25,6 +25,7 @@ import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
@@ -65,6 +66,16 @@ public class DefaultTimedAnnotationProvider implements 
TimedAnnotationProvider {
             return timed;
         });
     }
+    
+    @Override
+    public Optional<String> getDefaultMetricName(Exchange ex, boolean client) {
+        final HandlerMethod handlerMethod = HandlerMethod.create(ex, client);
+        if (handlerMethod == null) {
+            return Optional.empty();
+        } else {
+            return Optional.of(handlerMethod.method.getName());
+        }
+    }
 
     Set<Timed> findTimedAnnotations(AnnotatedElement element) {
         Set<Annotation> foundAnnotations = new HashSet<>();
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
index d8d0fe2f0f..8aec51085d 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
+++ 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
@@ -19,6 +19,7 @@
 
 package org.apache.cxf.metrics.micrometer.provider;
 
+import java.util.Optional;
 import java.util.Set;
 
 import org.apache.cxf.message.Exchange;
@@ -28,4 +29,8 @@ import io.micrometer.core.annotation.Timed;
 public interface TimedAnnotationProvider {
 
     Set<Timed> getTimedAnnotations(Exchange ex, boolean client);
+    
+    default Optional<String> getDefaultMetricName(Exchange ex, boolean client) 
{
+        return Optional.empty();
+    }
 }
diff --git 
a/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerClientMetricsContextTest.java
 
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerClientMetricsContextTest.java
index b53097e211..e6c522317f 100644
--- 
a/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerClientMetricsContextTest.java
+++ 
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerClientMetricsContextTest.java
@@ -183,7 +183,7 @@ public class MicrometerClientMetricsContextTest {
         underTest.stop(DUMMY_LONG, DUMMY_LONG, DUMMY_LONG, exchange);
 
         // then
-        verify(sample, times(2)).stop(timerArgumentCaptor.capture());
+        verify(sample, times(3)).stop(timerArgumentCaptor.capture());
 
         List<Meter.Id> timers = 
timerArgumentCaptor.getAllValues().stream().map(Meter::getId)
                 .collect(Collectors.toList());
diff --git 
a/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerServerMetricsContextTest.java
 
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerServerMetricsContextTest.java
index 11fb9754d3..df1e99006e 100644
--- 
a/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerServerMetricsContextTest.java
+++ 
b/rt/features/metrics/src/test/java/org/apache/cxf/metrics/micrometer/MicrometerServerMetricsContextTest.java
@@ -185,7 +185,7 @@ public class MicrometerServerMetricsContextTest {
         underTest.stop(DUMMY_LONG, DUMMY_LONG, DUMMY_LONG, exchange);
 
         // then
-        verify(sample, times(2)).stop(timerArgumentCaptor.capture());
+        verify(sample, times(3)).stop(timerArgumentCaptor.capture());
 
         List<Meter.Id> timers = 
timerArgumentCaptor.getAllValues().stream().map(Meter::getId)
                 .collect(Collectors.toList());
@@ -203,6 +203,74 @@ public class MicrometerServerMetricsContextTest {
                         Meter.Type.OTHER)));
     }
 
+    @Test
+    public void 
testStopShoulCallStopOnTimedAnnotationAndDoNotEmitDefaultMetric() {
+        // given
+        doReturn(new HashSet<>(asList(firstTimedAnnotation)))
+                .when(timedAnnotationProvider).getTimedAnnotations(exchange, 
false);
+
+        doReturn("").when(firstTimedAnnotation).value();
+        doReturn("").when(firstTimedAnnotation).description();
+        doReturn(new double[]{}).when(firstTimedAnnotation).percentiles();
+
+        
+        TimingContext timingContext = new TimingContext(sample);
+
+        doReturn(timingContext).when(request).getContent(TimingContext.class);
+
+        // when
+        underTest.stop(DUMMY_LONG, DUMMY_LONG, DUMMY_LONG, exchange);
+
+        // then
+        verify(sample, times(1)).stop(timerArgumentCaptor.capture());
+
+        List<Meter.Id> timers = 
timerArgumentCaptor.getAllValues().stream().map(Meter::getId)
+                .collect(Collectors.toList());
+
+        assertThat(timers, hasItems(
+                new Meter.Id(DUMMY_METRIC,
+                        Tags.of(DEFAULT_DUMMY_TAG, FIRST_ADDITIONAL_DUMMY_TAG, 
SECOND_ADDITIONAL_DUMMY_TAG),
+                        null,
+                        null,
+                        Meter.Type.OTHER)));
+    }
+
+    @Test
+    public void 
testStopShoulCallStopOnAllTimedAnnotationsAndDoNotEmitDefaultMetric() {
+        // given
+        doReturn(new HashSet<>(asList(firstTimedAnnotation, 
secondTimedAnnotation)))
+                .when(timedAnnotationProvider).getTimedAnnotations(exchange, 
false);
+
+        doReturn("").when(firstTimedAnnotation).value();
+        doReturn("").when(firstTimedAnnotation).description();
+        doReturn(new double[]{}).when(firstTimedAnnotation).percentiles();
+
+        doReturn("").when(secondTimedAnnotation).value();
+        doReturn("").when(secondTimedAnnotation).description();
+        doReturn(new double[]{}).when(secondTimedAnnotation).percentiles();
+
+        
+        TimingContext timingContext = new TimingContext(sample);
+
+        doReturn(timingContext).when(request).getContent(TimingContext.class);
+
+        // when
+        underTest.stop(DUMMY_LONG, DUMMY_LONG, DUMMY_LONG, exchange);
+
+        // then
+        verify(sample, times(1)).stop(timerArgumentCaptor.capture());
+
+        List<Meter.Id> timers = 
timerArgumentCaptor.getAllValues().stream().map(Meter::getId)
+                .collect(Collectors.toList());
+
+        assertThat(timers, hasItems(
+                new Meter.Id(DUMMY_METRIC,
+                        Tags.of(DEFAULT_DUMMY_TAG, FIRST_ADDITIONAL_DUMMY_TAG, 
SECOND_ADDITIONAL_DUMMY_TAG),
+                        null,
+                        null,
+                        Meter.Type.OTHER)));
+    }
+
     private Object getTimer(ArgumentCaptor<TimingContext> content) throws 
NoSuchFieldException {
         Timer.Sample timerSample = content.getValue().getTimerSample();
 
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceEmptyTimedImpl.java
similarity index 62%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
copy to 
systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceEmptyTimedImpl.java
index d8d0fe2f0f..0b0c05f490 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
+++ 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceEmptyTimedImpl.java
@@ -16,16 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.cxf.systest.jaxws.resources;
 
-package org.apache.cxf.metrics.micrometer.provider;
+import javax.jws.WebService;
 
-import java.util.Set;
+import io.micrometer.core.annotation.Timed;
 
-import org.apache.cxf.message.Exchange;
+import static java.util.Objects.requireNonNull;
 
-import io.micrometer.core.annotation.Timed;
+@WebService(serviceName = "HelloService", portName = "HelloPort",
+        targetNamespace = "http://service.ws.sample/";,
+        endpointInterface = 
"org.apache.cxf.systest.jaxws.resources.HelloService")
+public class HelloServiceEmptyTimedImpl implements HelloService {
 
-public interface TimedAnnotationProvider {
+    @Timed
+    public String sayHello(String name) {
+        requireNonNull(name);
+        return "Hello, " + name;
+    }
 
-    Set<Timed> getTimedAnnotations(Exchange ex, boolean client);
 }
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceTimedImpl.java
similarity index 61%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
copy to 
systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceTimedImpl.java
index d8d0fe2f0f..866d93ce47 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/TimedAnnotationProvider.java
+++ 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/resources/HelloServiceTimedImpl.java
@@ -16,16 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.cxf.systest.jaxws.resources;
 
-package org.apache.cxf.metrics.micrometer.provider;
+import javax.jws.WebService;
 
-import java.util.Set;
+import io.micrometer.core.annotation.Timed;
 
-import org.apache.cxf.message.Exchange;
+import static java.util.Objects.requireNonNull;
 
-import io.micrometer.core.annotation.Timed;
+@WebService(serviceName = "HelloService", portName = "HelloPort",
+        targetNamespace = "http://service.ws.sample/";,
+        endpointInterface = 
"org.apache.cxf.systest.jaxws.resources.HelloService")
+public class HelloServiceTimedImpl implements HelloService {
 
-public interface TimedAnnotationProvider {
+    @Timed(value = "sayHello.requests")
+    public String sayHello(String name) {
+        requireNonNull(name);
+        return "Hello, " + name;
+    }
 
-    Set<Timed> getTimedAnnotations(Exchange ex, boolean client);
 }
diff --git 
a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/spring/boot/SpringJaxwsTimedTest.java
 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/spring/boot/SpringJaxwsTimedTest.java
new file mode 100644
index 0000000000..f75e6ed4db
--- /dev/null
+++ 
b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxws/spring/boot/SpringJaxwsTimedTest.java
@@ -0,0 +1,271 @@
+/**
+ * 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.systest.jaxws.spring.boot;
+
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Duration;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.ws.Dispatch;
+import javax.xml.ws.Endpoint;
+import javax.xml.ws.Service;
+import javax.xml.ws.Service.Mode;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.jaxws.EndpointImpl;
+import org.apache.cxf.metrics.MetricsFeature;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.systest.jaxws.resources.HelloServiceEmptyTimedImpl;
+import org.apache.cxf.systest.jaxws.resources.HelloServiceTimedImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.test.context.ActiveProfiles;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.search.MeterNotFoundException;
+import io.micrometer.core.instrument.search.RequiredSearch;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static java.util.stream.Collectors.toMap;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.empty;
+
+@SpringBootApplication
+@SpringBootTest(
+        webEnvironment = WebEnvironment.RANDOM_PORT,
+        classes = {
+            SpringJaxwsTimedTest.TestConfig.class,
+      
+        },
+        properties = {
+            "cxf.metrics.server.max-uri-tags=2"
+        })
+@ImportResource("classpath:spring/jaxws-client.xml") 
+@ActiveProfiles("jaxws")
+
+public class SpringJaxwsTimedTest {
+
+    private static final String DUMMY_REQUEST_BODY = "<q0:sayHello 
xmlns:q0=\"http://service.ws.sample/\";>"
+            + "<name>Elan</name>"
+            + "</q0:sayHello>";
+    private static final String HELLO_SERVICE_NAME_V1 = "HelloV1";
+    private static final String HELLO_SERVICE_NAME_V2 = "HelloV2";
+
+    @Autowired
+    private MeterRegistry registry;
+    
+    @Autowired
+    private MetricsProvider metricsProvider;
+
+    @LocalServerPort
+    private int port;
+
+    @EnableAutoConfiguration
+    static class TestConfig {
+        @Autowired
+        private Bus bus;
+
+        @Autowired
+        private MetricsProvider metricsProvider;
+
+        @Bean
+        public Endpoint helloEndpoint() {
+            EndpointImpl endpoint = new EndpointImpl(bus, new 
HelloServiceTimedImpl(), null, null, new MetricsFeature[]{
+                new MetricsFeature(metricsProvider)
+            });
+            endpoint.publish("/" + HELLO_SERVICE_NAME_V1);
+            return endpoint;
+        }
+        
+        @Bean
+        public Endpoint secondHelloEndpoint() {
+            EndpointImpl endpoint = new EndpointImpl(bus, new 
HelloServiceEmptyTimedImpl(), null, null, 
+                new MetricsFeature[] {
+                    new MetricsFeature(metricsProvider)
+                }
+            );
+            endpoint.publish("/" + HELLO_SERVICE_NAME_V2);
+            return endpoint;
+        }
+
+    }
+
+    @AfterEach
+    public void clear() {
+        registry.clear();
+    }
+
+    @Test
+    public void testJaxwsTimedSuccessMetric() throws MalformedURLException {
+        // given in setUp
+
+        // when
+        String actual = sendSoapRequest(DUMMY_REQUEST_BODY, 
HELLO_SERVICE_NAME_V1);
+
+        // then
+        assertThat(actual)
+                .isEqualTo("<ns2:sayHelloResponse 
xmlns:ns2=\"http://service.ws.sample/\";>"
+                        + "<return>Hello, Elan</return>"
+                        + "</ns2:sayHelloResponse>");
+
+        await()
+            .atMost(Duration.ofSeconds(1))
+            .ignoreException(MeterNotFoundException.class)
+            .until(() -> registry.get("cxf.server.requests").timers(), 
not(empty()));
+        RequiredSearch serverRequestMetrics = 
registry.get("cxf.server.requests");
+
+        Map<Object, Object> serverTags = 
serverRequestMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(serverTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "sayHello"),
+                entry("uri", "/Service/" + HELLO_SERVICE_NAME_V1),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+        
+        RequiredSearch timedMetrics = registry.get("sayHello.requests");
+
+        Map<Object, Object> timedTags = 
timedMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(timedTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "sayHello"),
+                entry("uri", "/Service/" + HELLO_SERVICE_NAME_V1),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+            
+        RequiredSearch clientRequestMetrics = 
registry.get("cxf.client.requests");
+
+        Map<Object, Object> clientTags = 
clientRequestMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(clientTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "Invoke"),
+                entry("uri", "http://localhost:"; + port + "/Service/" + 
HELLO_SERVICE_NAME_V1),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+    }
+    
+    @Test
+    public void testJaxwsEmptyTimedSuccessMetric() throws 
MalformedURLException {
+        // given in setUp
+
+        // when
+        String actual = sendSoapRequest(DUMMY_REQUEST_BODY, 
HELLO_SERVICE_NAME_V2);
+
+        // then
+        assertThat(actual)
+                .isEqualTo("<ns2:sayHelloResponse 
xmlns:ns2=\"http://service.ws.sample/\";>"
+                        + "<return>Hello, Elan</return>"
+                        + "</ns2:sayHelloResponse>");
+
+        await()
+            .atMost(Duration.ofSeconds(1))
+            .ignoreException(MeterNotFoundException.class)
+            .until(() -> registry.get("cxf.server.requests").timers(), 
not(empty()));
+        RequiredSearch serverRequestMetrics = 
registry.get("cxf.server.requests");
+
+        Map<Object, Object> serverTags = 
serverRequestMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(serverTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "sayHello"),
+                entry("uri", "/Service/" + HELLO_SERVICE_NAME_V2),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+        
+        RequiredSearch timedMetrics = registry.get("sayHello");
+
+        Map<Object, Object> timedTags = 
timedMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(timedTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "sayHello"),
+                entry("uri", "/Service/" + HELLO_SERVICE_NAME_V2),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+            
+        RequiredSearch clientRequestMetrics = 
registry.get("cxf.client.requests");
+
+        Map<Object, Object> clientTags = 
clientRequestMetrics.timer().getId().getTags().stream()
+                .collect(toMap(Tag::getKey, Tag::getValue));
+
+        assertThat(clientTags)
+            .containsOnly(
+                entry("exception", "None"),
+                entry("faultCode", "None"),
+                entry("method", "POST"),
+                entry("operation", "Invoke"),
+                entry("uri", "http://localhost:"; + port + "/Service/" + 
HELLO_SERVICE_NAME_V2),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+    }
+        
+    private String sendSoapRequest(String requestBody, final String 
serviceName) throws MalformedURLException {
+        String address = "http://localhost:"; + port + "/Service/" + 
serviceName;
+
+        StreamSource source = new StreamSource(new StringReader(requestBody));
+        Service service = Service.create(new URL(address + "?wsdl"),
+                new QName("http://service.ws.sample/";, "HelloService"), new 
MetricsFeature(metricsProvider));
+        Dispatch<Source> dispatch = service.createDispatch(new 
QName("http://service.ws.sample/";, "HelloPort"),
+            Source.class, Mode.PAYLOAD);
+
+        Source result = dispatch.invoke(source);
+        return StaxUtils.toString(result);
+    }
+}

Reply via email to