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>