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

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


The following commit(s) were added to refs/heads/3.3.x-fixes by this push:
     new 68a248a  CXF-8124: CXF metrics - MetricsContext#stop called twice or 
without Fault in certain error cases (#587)
68a248a is described below

commit 68a248a5ce0d8db6d991222c110a161764aedf9d
Author: Andriy Redko <drr...@gmail.com>
AuthorDate: Sun Oct 13 15:20:11 2019 -0400

    CXF-8124: CXF metrics - MetricsContext#stop called twice or without Fault 
in certain error cases (#587)
    
    (cherry picked from commit 424bc7b198315da58b4c445002b17113be894df0)
---
 .../MetricsMessageInPostInvokeInterceptor.java     |   3 +-
 systests/jaxrs/pom.xml                             |  25 +++
 .../org/apache/cxf/systest/jaxrs/metrics/Book.java |  35 +--
 .../jaxrs/metrics/JAXRSClientMetricsTest.java      | 219 +++++++++++++++++++
 .../jaxrs/metrics/JAXRSServerMetricsTest.java      | 237 +++++++++++++++++++++
 .../apache/cxf/systest/jaxrs/metrics/Library.java  |  29 ++-
 systests/jaxws/pom.xml                             |   7 +-
 .../cxf/systest/jaxws/metrics/BookWebService.java  |  25 ++-
 .../cxf/systest/jaxws/metrics/IBookWebService.java |  24 +--
 .../jaxws/metrics/JAXWSClientMetricsTest.java      | 143 +++++++++++++
 .../apache/cxf/systest/jaxws/metrics/context.xml   |   9 +
 11 files changed, 698 insertions(+), 58 deletions(-)

diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
index cb00860..eb51168 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
+++ 
b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
@@ -20,6 +20,7 @@ package org.apache.cxf.metrics.interceptors;
 
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.metrics.MetricsProvider;
 import org.apache.cxf.phase.Phase;
 
@@ -30,7 +31,7 @@ public class MetricsMessageInPostInvokeInterceptor extends 
AbstractMetricsInterc
     }
 
     public void handleMessage(Message message) throws Fault {
-        if (isRequestor(message)) {
+        if (isRequestor(message) && !MessageUtils.isOutbound(message)) {
             stop(message);
         }
     }
diff --git a/systests/jaxrs/pom.xml b/systests/jaxrs/pom.xml
index 00a5eee..bafda98 100644
--- a/systests/jaxrs/pom.xml
+++ b/systests/jaxrs/pom.xml
@@ -344,6 +344,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-features-metrics</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.annotation</groupId>
             <artifactId>jsr250-api</artifactId>
             <version>${cxf.jsr250.api.version}</version>
@@ -543,6 +549,25 @@
             <version>${cxf.ehcache3.version}</version>
             <scope>test</scope>
        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${cxf.mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.tomakehurst</groupId>
+            <artifactId>wiremock</artifactId>
+            <version>${cxf.wiremock.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.xmlunit</groupId>
+                    <artifactId>xmlunit-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        
     </dependencies>
     <build>
         <plugins>
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
 b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Book.java
similarity index 60%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
copy to 
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Book.java
index cb00860..c4445cd 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Book.java
@@ -16,22 +16,33 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.cxf.metrics.interceptors;
 
-import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.metrics.MetricsProvider;
-import org.apache.cxf.phase.Phase;
+package org.apache.cxf.systest.jaxrs.metrics;
 
-public class MetricsMessageInPostInvokeInterceptor extends 
AbstractMetricsInterceptor {
+public class Book {
+    private int id;
+    private String name;
+    
+    public Book() {
+    }
 
-    public MetricsMessageInPostInvokeInterceptor(MetricsProvider[] p) {
-        super(Phase.POST_INVOKE, p);
+    public Book(int id) {
+        this.id = id;
     }
 
-    public void handleMessage(Message message) throws Fault {
-        if (isRequestor(message)) {
-            stop(message);
-        }
+    public int getId() {
+        return id;
+    }
+    
+    public void setId(int id) {
+        this.id = id;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public void setName(String name) {
+        this.name = name;
     }
 }
\ No newline at end of file
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSClientMetricsTest.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSClientMetricsTest.java
new file mode 100644
index 0000000..6f635e5
--- /dev/null
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSClientMetricsTest.java
@@ -0,0 +1,219 @@
+/**
+ * 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.jaxrs.metrics;
+
+import java.util.Arrays;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.metrics.MetricsContext;
+import org.apache.cxf.metrics.MetricsFeature;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.springframework.util.SocketUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static 
com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.times;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JAXRSClientMetricsTest {
+    @Rule public WireMockRule wireMockRule = new 
WireMockRule(wireMockConfig().dynamicPort());
+    @Rule public ExpectedException expectedException = 
ExpectedException.none();
+    
+    private MetricsProvider provider;
+    private MetricsContext operationContext;
+    private MetricsContext resourceContext;
+    private MetricsContext endpointContext;
+    
+    @Before
+    public void setUp() {
+        endpointContext = Mockito.mock(MetricsContext.class);
+        operationContext = Mockito.mock(MetricsContext.class);
+        resourceContext = Mockito.mock(MetricsContext.class);
+
+        provider = new MetricsProvider() {
+            public MetricsContext createEndpointContext(Endpoint endpoint, 
boolean asClient, String cid) {
+                return endpointContext;
+            }
+
+            public MetricsContext createOperationContext(Endpoint endpoint, 
BindingOperationInfo boi, 
+                    boolean asClient, String cid) {
+                return operationContext;
+            }
+
+            public MetricsContext createResourceContext(Endpoint endpoint, 
String resourceName, 
+                    boolean asClient, String cid) {
+                return resourceContext;
+            }
+        };
+    }
+
+    @Test
+    public void usingClientProxyStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean();
+        factory.setResourceClass(Library.class);
+        factory.setAddress("http://localhost:"; + wireMockRule.port() + "/");
+        factory.setFeatures(Arrays.asList(new MetricsFeature(provider)));
+        factory.setProvider(JacksonJsonProvider.class);
+        
+        stubFor(get(urlEqualTo("/books/10"))
+            .willReturn(aResponse()
+                .withStatus(404)));
+
+        try {
+            final Library client = factory.create(Library.class);
+            expectedException.expect(NotFoundException.class);
+            client.getBook(10);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+   
+    @Test
+    public void usingClientStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final Client client = ClientBuilder
+                .newClient()
+                .register(new MetricsFeature(provider))
+                .register(JacksonJsonProvider.class);
+
+        stubFor(get(urlEqualTo("/books/10"))
+            .willReturn(aResponse()
+                .withStatus(404)));
+
+        try {
+            expectedException.expect(ProcessingException.class);
+            client
+                .target("http://localhost:"; + wireMockRule.port() + 
"/books/10")
+                .request(MediaType.APPLICATION_JSON).get()
+                .readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+
+    @Test
+    public void usingClientStopIsCalledWhenConnectionIsRefused() throws 
Exception {
+        final int port = SocketUtils.findAvailableTcpPort();
+        
+        final Client client = ClientBuilder
+            .newClient()
+            .register(new MetricsFeature(provider))
+            .register(JacksonJsonProvider.class);
+
+        try {
+            expectedException.expect(ProcessingException.class);
+            client
+                .target("http://localhost:"; + port + "/books/10")
+                .request(MediaType.APPLICATION_JSON)
+                .get()
+                .readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+
+    @Test
+    public void usingClientStopIsCalledWhenServerReturnSuccessfulResponse() 
throws Exception {
+        final Client client = ClientBuilder
+            .newClient()
+            .register(new MetricsFeature(provider))
+            .register(JacksonJsonProvider.class);
+
+        stubFor(get(urlEqualTo("/books/10"))
+            .withHeader("Accept", equalTo(MediaType.APPLICATION_JSON))
+            .willReturn(aResponse()
+                .withHeader("Content-Type", MediaType.APPLICATION_JSON)
+                .withBody("{}")
+                .withStatus(200)));
+
+        try {
+            client
+                .target("http://localhost:"; + wireMockRule.port() + 
"/books/10")
+                .request(MediaType.APPLICATION_JSON)
+                .get()
+                .readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+    
+    @Test
+    public void usingWebClientStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final WebClient client = WebClient.create("http://localhost:"; + 
wireMockRule.port() + "/books/10",
+            Arrays.asList(JacksonJsonProvider.class), Arrays.asList(new 
MetricsFeature(provider)), null);
+
+        stubFor(get(urlEqualTo("/books/10"))
+            .willReturn(aResponse()
+                .withStatus(404)));
+
+        try {
+            expectedException.expect(ProcessingException.class);
+            client.get().readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+}
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSServerMetricsTest.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSServerMetricsTest.java
new file mode 100644
index 0000000..53f999a
--- /dev/null
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/JAXRSServerMetricsTest.java
@@ -0,0 +1,237 @@
+/**
+ * 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.jaxrs.metrics;
+
+import java.util.Arrays;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.metrics.MetricsContext;
+import org.apache.cxf.metrics.MetricsFeature;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.times;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JAXRSServerMetricsTest extends AbstractBusClientServerTestBase {
+    public static final String PORT = 
allocatePort(JAXRSServerMetricsTest.class);
+
+    private static MetricsProvider provider;
+    private static MetricsContext operationContext;
+    private static MetricsContext resourceContext;
+    private static MetricsContext endpointContext;
+    
+    @Rule public ExpectedException expectedException = 
ExpectedException.none();
+    
+    public static class BookLibrary implements Library {
+        @Override
+        public Book getBook(int id) {
+            if (id == 10) {
+                throw new NotFoundException();
+            } else {
+                return new Book(id);
+            }
+        }
+    }
+    
+    @Ignore
+    public static class Server extends AbstractBusTestServerBase {
+        protected void run() {
+            final JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+            sf.setResourceClasses(BookLibrary.class);
+            sf.setResourceProvider(BookLibrary.class, new 
SingletonResourceProvider(new BookLibrary()));
+            sf.setFeatures(Arrays.asList(new MetricsFeature(provider)));
+            sf.setAddress("http://localhost:"; + PORT + "/");
+            sf.setProvider(new JacksonJsonProvider());
+            sf.create();
+        }
+
+        public static void main(String[] args) {
+            try {
+                Server s = new Server();
+                s.start();
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                System.exit(-1);
+            } finally {
+                System.out.println("done!");
+            }
+        }
+    }
+
+    @BeforeClass
+    public static void startServers() throws Exception {
+        endpointContext = Mockito.mock(MetricsContext.class);
+        operationContext = Mockito.mock(MetricsContext.class);
+        resourceContext = Mockito.mock(MetricsContext.class);
+
+        provider = new MetricsProvider() {
+            public MetricsContext createEndpointContext(Endpoint endpoint, 
boolean asClient, String cid) {
+                return endpointContext;
+            }
+
+            public MetricsContext createOperationContext(Endpoint endpoint, 
BindingOperationInfo boi, 
+                    boolean asClient, String cid) {
+                return operationContext;
+            }
+
+            public MetricsContext createResourceContext(Endpoint endpoint, 
String resourceName, 
+                    boolean asClient, String cid) {
+                return resourceContext;
+            }
+        };
+
+        AbstractResourceInfo.clearAllMaps();
+        //keep out of process due to stack traces testing failures
+        assertTrue("server did not launch correctly", 
launchServer(Server.class, true));
+        createStaticBus();
+    }
+
+    @Before
+    public void setUp() {
+        Mockito.reset(resourceContext);
+        Mockito.reset(operationContext);
+        Mockito.reset(endpointContext);
+    }
+
+    @Test
+    public void usingClientProxyStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean();
+        factory.setResourceClass(Library.class);
+        factory.setAddress("http://localhost:"; + PORT + "/");
+        factory.setProvider(JacksonJsonProvider.class);
+        
+        try {
+            final Library client = factory.create(Library.class);
+            expectedException.expect(NotFoundException.class);
+            client.getBook(10);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+   
+    @Test
+    public void usingClientStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final Client client = ClientBuilder
+            .newClient()
+            .register(JacksonJsonProvider.class);
+
+        try {
+            expectedException.expect(ProcessingException.class);
+            client
+                .target("http://localhost:"; + PORT + "/books/10")
+                .request(MediaType.APPLICATION_JSON).get()
+                .readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+
+    @Test
+    public void usingClientStopIsCalledWhenServerReturnSuccessfulResponse() 
throws Exception {
+        final Client client = ClientBuilder
+            .newClient()
+            .register(JacksonJsonProvider.class);
+
+        try {
+            client
+                .target("http://localhost:"; + PORT + "/books/11")
+                .request(MediaType.APPLICATION_JSON)
+                .get()
+                .readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+    
+    @Test
+    public void usingWebClientStopIsCalledWhenServerReturnsNotFound() throws 
Exception {
+        final WebClient client = WebClient.create("http://localhost:"; + PORT + 
"/books/10",
+            Arrays.asList(JacksonJsonProvider.class));
+        
+        try {
+            expectedException.expect(ProcessingException.class);
+            client.get().readEntity(Book.class);
+        } finally {
+            Mockito.verify(resourceContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(resourceContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+    
+    @Test
+    public void usingWebClientStopIsCalledWhenUrlIsNotServed() throws 
Exception {
+        final WebClient client = WebClient.create("http://localhost:"; + PORT + 
"/books",
+            Arrays.asList(JacksonJsonProvider.class));
+        
+        try {
+            expectedException.expect(ProcessingException.class);
+            client.get().readEntity(Book.class);
+        } finally {
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(resourceContext);
+            Mockito.verifyZeroInteractions(operationContext);
+        }
+    }
+}
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Library.java
similarity index 59%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
copy to 
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Library.java
index cb00860..385cfb8 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/metrics/Library.java
@@ -16,22 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.cxf.metrics.interceptors;
 
-import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.metrics.MetricsProvider;
-import org.apache.cxf.phase.Phase;
+package org.apache.cxf.systest.jaxrs.metrics;
 
-public class MetricsMessageInPostInvokeInterceptor extends 
AbstractMetricsInterceptor {
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
 
-    public MetricsMessageInPostInvokeInterceptor(MetricsProvider[] p) {
-        super(Phase.POST_INVOKE, p);
-    }
-
-    public void handleMessage(Message message) throws Fault {
-        if (isRequestor(message)) {
-            stop(message);
-        }
-    }
+@Path("/")
+public interface Library {
+    @GET
+    @Path("books/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    Book getBook(@PathParam("id") int id);
 }
\ No newline at end of file
diff --git a/systests/jaxws/pom.xml b/systests/jaxws/pom.xml
index fe2aa0f..bb91672 100644
--- a/systests/jaxws/pom.xml
+++ b/systests/jaxws/pom.xml
@@ -276,7 +276,12 @@
             <artifactId>jaxb-xjc</artifactId>
             <scope>test</scope>
         </dependency>
-
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${cxf.mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <profiles>
         <profile>
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/BookWebService.java
similarity index 60%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
copy to 
systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/BookWebService.java
index cb00860..d1639f5 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
+++ 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/BookWebService.java
@@ -16,22 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.cxf.metrics.interceptors;
 
-import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.metrics.MetricsProvider;
-import org.apache.cxf.phase.Phase;
+package org.apache.cxf.systest.jaxws.metrics;
 
-public class MetricsMessageInPostInvokeInterceptor extends 
AbstractMetricsInterceptor {
+import javax.jws.WebMethod;
+import javax.jws.WebService;
 
-    public MetricsMessageInPostInvokeInterceptor(MetricsProvider[] p) {
-        super(Phase.POST_INVOKE, p);
-    }
-
-    public void handleMessage(Message message) throws Fault {
-        if (isRequestor(message)) {
-            stop(message);
+@WebService(name = "Library", serviceName = "Book",
+endpointInterface = "org.apache.cxf.systest.jaxws.metrics.IBookWebService")
+public class BookWebService implements IBookWebService {
+    @WebMethod
+    public String getBook(int id) {
+        if (id == 11) {
+            throw new RuntimeException();
+        } else {
+            return "All your bases belong to us.";
         }
     }
 }
\ No newline at end of file
diff --git 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/IBookWebService.java
similarity index 59%
copy from 
rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
copy to 
systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/IBookWebService.java
index cb00860..4ad0da7 100644
--- 
a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/interceptors/MetricsMessageInPostInvokeInterceptor.java
+++ 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/IBookWebService.java
@@ -16,22 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.cxf.metrics.interceptors;
 
-import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.metrics.MetricsProvider;
-import org.apache.cxf.phase.Phase;
+package org.apache.cxf.systest.jaxws.metrics;
 
-public class MetricsMessageInPostInvokeInterceptor extends 
AbstractMetricsInterceptor {
+import javax.jws.WebMethod;
+import javax.jws.WebService;
 
-    public MetricsMessageInPostInvokeInterceptor(MetricsProvider[] p) {
-        super(Phase.POST_INVOKE, p);
-    }
-
-    public void handleMessage(Message message) throws Fault {
-        if (isRequestor(message)) {
-            stop(message);
-        }
-    }
-}
\ No newline at end of file
+@WebService
+public interface IBookWebService {
+    @WebMethod
+    String getBook(int id);
+}
diff --git 
a/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/JAXWSClientMetricsTest.java
 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/JAXWSClientMetricsTest.java
new file mode 100644
index 0000000..4e7bb64
--- /dev/null
+++ 
b/systests/jaxws/src/test/java/org/apache/cxf/systest/jaxws/metrics/JAXWSClientMetricsTest.java
@@ -0,0 +1,143 @@
+/**
+ * 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.metrics;
+
+import java.util.Arrays;
+
+import org.apache.cxf.binding.soap.SoapFault;
+import org.apache.cxf.common.i18n.UncheckedException;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.jaxws.JaxWsClientFactoryBean;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.metrics.MetricsContext;
+import org.apache.cxf.metrics.MetricsFeature;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.test.AbstractCXFSpringTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.times;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JAXWSClientMetricsTest extends AbstractCXFSpringTest {
+    @Rule public ExpectedException expectedException = 
ExpectedException.none();
+    
+    private MetricsProvider provider;
+    private MetricsContext operationContext;
+    private MetricsContext resourceContext;
+    private MetricsContext endpointContext;
+    
+    @Before
+    public void setUp() {
+        endpointContext = Mockito.mock(MetricsContext.class);
+        operationContext = Mockito.mock(MetricsContext.class);
+        resourceContext = Mockito.mock(MetricsContext.class);
+
+        provider = new MetricsProvider() {
+            public MetricsContext createEndpointContext(Endpoint endpoint, 
boolean asClient, String cid) {
+                return endpointContext;
+            }
+
+            public MetricsContext createOperationContext(Endpoint endpoint, 
BindingOperationInfo boi, 
+                    boolean asClient, String cid) {
+                return operationContext;
+            }
+
+            public MetricsContext createResourceContext(Endpoint endpoint, 
String resourceName, 
+                    boolean asClient, String cid) {
+                return resourceContext;
+            }
+        };
+    }
+
+    @Test
+    public void usingClientProxyStopIsCalledWhenServerReturnsResponse() throws 
Exception {
+        final JaxWsClientFactoryBean factory = new JaxWsClientFactoryBean();
+        factory.setAddress("local://services/Book");
+        factory.setServiceClass(IBookWebService.class);
+        factory.setFeatures(Arrays.asList(new MetricsFeature(provider)));
+        
+        try {
+            final Client client = factory.create();
+            String response = (String)client.invoke("getBook", 10)[0];
+            assertEquals("All your bases belong to us.", response);
+        } finally {
+            Mockito.verify(operationContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(operationContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(resourceContext);
+        }
+    }
+   
+    @Test
+    public void usingClientProxyStopIsCalledWhenServerReturnsFault() throws 
Exception {
+        final JaxWsClientFactoryBean factory = new JaxWsClientFactoryBean();
+        factory.setAddress("local://services/Book");
+        factory.setServiceClass(IBookWebService.class);
+        factory.setFeatures(Arrays.asList(new MetricsFeature(provider)));
+        
+        try {
+            final Client client = factory.create();
+            expectedException.expect(SoapFault.class);
+            client.invoke("getBook", 11);
+        } finally {
+            Mockito.verify(operationContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(operationContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verify(endpointContext, 
times(1)).start(any(Exchange.class));
+            Mockito.verify(endpointContext, times(1)).stop(anyLong(), 
anyLong(), anyLong(), any(Exchange.class));
+            Mockito.verifyZeroInteractions(resourceContext);
+        }
+    }
+
+    @Test
+    public void usingClientProxyStopIsCalledForUnsupportedOperation() throws 
Exception {
+        final JaxWsClientFactoryBean factory = new JaxWsClientFactoryBean();
+        factory.setAddress("local://services/Book");
+        factory.setServiceClass(IBookWebService.class);
+        factory.setFeatures(Arrays.asList(new MetricsFeature(provider)));
+        
+        try {
+            final Client client = factory.create();
+            expectedException.expect(UncheckedException.class);
+            client.invoke("getBooks");
+        } finally {
+            Mockito.verifyZeroInteractions(endpointContext);
+            Mockito.verifyZeroInteractions(operationContext);
+            Mockito.verifyZeroInteractions(resourceContext);
+        }
+    }
+
+    @Override
+    protected String[] getConfigLocations() {
+        return new String[] 
{"/org/apache/cxf/systest/jaxws/metrics/context.xml" };
+    }
+}
diff --git 
a/systests/jaxws/src/test/resources/org/apache/cxf/systest/jaxws/metrics/context.xml
 
b/systests/jaxws/src/test/resources/org/apache/cxf/systest/jaxws/metrics/context.xml
new file mode 100644
index 0000000..8e22fb2
--- /dev/null
+++ 
b/systests/jaxws/src/test/resources/org/apache/cxf/systest/jaxws/metrics/context.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:context="http://www.springframework.org/schema/context"; 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd         
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd";>
+    <import resource="classpath:META-INF/cxf/cxf.xml"/>
+    <!-- This test is not run with a servlet container. It tests non-servlet 
publication. -->
+    <bean id="servicebean" 
class="org.apache.cxf.systest.jaxws.metrics.BookWebService"/>
+    <bean id="postprocess" 
class="org.apache.cxf.jaxws.spring.JaxWsWebServicePublisherBeanPostProcessor">
+        <property name="urlPrefix" value="local://services/"/>
+    </bean>
+</beans>

Reply via email to