You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2021/03/11 23:40:39 UTC

[cxf] branch master updated: CXF-8424: Use template URL instead of actual path values for JAX-RS micrometer metrics (#758)

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

reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/master by this push:
     new e36d407  CXF-8424: Use template URL instead of actual path values for JAX-RS micrometer metrics (#758)
e36d407 is described below

commit e36d407354defa65587a5e291f808bd65f4f43df
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Thu Mar 11 18:38:55 2021 -0500

    CXF-8424: Use template URL instead of actual path values for JAX-RS micrometer metrics (#758)
---
 .../java/org/apache/cxf/message/MessageUtils.java  |  1 +
 .../metrics/micrometer/provider/StandardTags.java  | 27 ++++--
 .../java/org/apache/cxf/jaxrs/JAXRSInvoker.java    |  1 +
 .../cxf/jaxrs/interceptor/JAXRSInInterceptor.java  |  2 +
 .../org/apache/cxf/jaxrs/model/URITemplate.java    |  1 +
 .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java     | 98 ++++++++++++++++++++++
 .../LibraryApplication.java}                       | 39 ++++-----
 .../resources/{LibraryApi.java => Catalog.java}    | 22 ++---
 .../cxf/systest/jaxrs/resources/Library.java       |  5 ++
 .../cxf/systest/jaxrs/resources/LibraryApi.java    |  3 +
 ...rsTest.java => SpringJaxrsApplicationTest.java} | 84 +++++++++++++------
 .../systest/jaxrs/spring/boot/SpringJaxrsTest.java |  4 +-
 12 files changed, 220 insertions(+), 67 deletions(-)

diff --git a/core/src/main/java/org/apache/cxf/message/MessageUtils.java b/core/src/main/java/org/apache/cxf/message/MessageUtils.java
index 043c6af..b103bdd 100644
--- a/core/src/main/java/org/apache/cxf/message/MessageUtils.java
+++ b/core/src/main/java/org/apache/cxf/message/MessageUtils.java
@@ -202,6 +202,7 @@ public final class MessageUtils {
             MethodDispatcher md = (MethodDispatcher) m.getExchange().getService().get(MethodDispatcher.class.getName());
             method = md.getMethod(bop);
         } else {
+            // See please JAXRSInInterceptor#RESOURCE_METHOD for the reference
             method = (Method) m.get("org.apache.cxf.resource.method");
         }
         return Optional.ofNullable(method);
diff --git a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/StandardTags.java b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/StandardTags.java
index f62d0ce..d6ec7ae 100644
--- a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/StandardTags.java
+++ b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/micrometer/provider/StandardTags.java
@@ -73,12 +73,7 @@ public class StandardTags {
     }
 
     public Tag uri(Message request) {
-        return ofNullable(request)
-                .map(e -> e.get(Message.REQUEST_URI))
-                .filter(e -> e instanceof String)
-                .map(e -> stripQueryString((String) e))
-                .map(e -> Tag.of("uri", e))
-                .orElse(endpoint(request));
+        return template(request).orElse(requestUri(request));
     }
 
     public Tag exception(Class<?> exceptionClass) {
@@ -110,6 +105,14 @@ public class StandardTags {
         return OUTCOME_SERVER_ERROR;
     }
     
+    private Tag requestUri(Message request) {
+        return ofNullable(request)
+                .map(e -> e.get(Message.REQUEST_URI))
+                .filter(e -> e instanceof String)
+                .map(e -> stripQueryString((String) e))
+                .map(e -> Tag.of("uri", e))
+                .orElse(endpoint(request));
+    }
     /**
      * The Request URI endpoint fallback in case of JAX-WS client invocations
      */
@@ -123,6 +126,18 @@ public class StandardTags {
     }
     
     /**
+     * The Request URI template in case of JAX-RS invocation
+     */
+    private Optional<Tag> template(Message request) {
+        return ofNullable(request)
+                // See please URITemplate#URI_TEMPLATE for the reference
+                .map(e -> e.get("jaxrs.template.uri"))
+                .filter(e -> e instanceof String)
+                .map(e -> stripQueryString((String) e))
+                .map(e -> Tag.of("uri", e));
+    }
+    
+    /**
      * Strips the query string from the request URI
      */
     private String stripQueryString(String uri) {
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSInvoker.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
index f90f2e0..98252ba 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
@@ -268,6 +268,7 @@ public class JAXRSInvoker extends AbstractInvoker {
                                                          acceptContentType);
                 exchange.put(OperationResourceInfo.class, subOri);
                 inMessage.put(URITemplate.TEMPLATE_PARAMETERS, values);
+                inMessage.put(URITemplate.URI_TEMPLATE, JAXRSUtils.getUriTemplate(inMessage, subCri, ori, subOri));
 
                 if (!subOri.isSubResourceLocator()
                     && JAXRSUtils.runContainerRequestFilters(providerFactory,
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
index 948b9f2..98af3f2 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
@@ -64,6 +64,7 @@ public class JAXRSInInterceptor extends AbstractPhaseInterceptor<Message> {
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSInInterceptor.class);
     private static final String RESOURCE_METHOD = "org.apache.cxf.resource.method";
     private static final String RESOURCE_OPERATION_NAME = "org.apache.cxf.resource.operation.name";
+
     public JAXRSInInterceptor() {
         super(Phase.UNMARSHAL);
     }
@@ -238,6 +239,7 @@ public class JAXRSInInterceptor extends AbstractPhaseInterceptor<Message> {
         exchange.put(JAXRSUtils.ROOT_RESOURCE_CLASS, cri);
         message.put(RESOURCE_METHOD, ori.getMethodToInvoke());
         message.put(URITemplate.TEMPLATE_PARAMETERS, values);
+        message.put(URITemplate.URI_TEMPLATE, JAXRSUtils.getUriTemplate(message, cri, ori));
 
         String plainOperationName = ori.getMethodToInvoke().getName();
         if (numberOfResources > 1) {
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
index d646407..b0c0af2 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
@@ -40,6 +40,7 @@ import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 public final class URITemplate {
 
     public static final String TEMPLATE_PARAMETERS = "jaxrs.template.parameters";
+    public static final String URI_TEMPLATE = "jaxrs.template.uri";
     public static final String LIMITED_REGEX_SUFFIX = "(/.*)?";
     public static final String FINAL_MATCH_GROUP = "FINAL_MATCH_GROUP";
     private static final String DEFAULT_PATH_VARIABLE_REGEX = "([^/]+?)";
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index 0518d69..585711d 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -1895,6 +1895,104 @@ public final class JAXRSUtils {
         LOG.severe(errorMessage);
         return errorMessage;
     }
+    
+    /**
+     * Get path URI template, combining base path, class & method & subresource templates 
+     * @param message message instance
+     * @param cri class resource info
+     * @param ori operation resource info
+     * @param subOri operation subresource info
+     * @return the URI template for the method in question
+     */
+    public static String getUriTemplate(Message message, ClassResourceInfo cri, OperationResourceInfo ori, 
+            OperationResourceInfo subOri) {
+        final String template = getUriTemplate(message, cri, ori);
+        final String methodPathTemplate = getUriTemplate(subOri);
+        return combineUriTemplates(template, methodPathTemplate);
+    }
+
+    /**
+     * Get path URI template, combining base path, class & method templates 
+     * @param message message instance
+     * @param cri class resource info
+     * @param ori operation resource info
+     * @return the URI template for the method in question
+     */
+    public static String getUriTemplate(Message message, ClassResourceInfo cri, OperationResourceInfo ori) {
+        final String basePath = (String)message.get(Message.BASE_PATH);
+        final String classPathTemplate = getUriTemplate(cri);
+        final String methodPathTemplate = getUriTemplate(ori);
+
+        // The application path (@ApplicationPath) is incorporated into Message.BASE_PATH,
+        // since it is part of the address.
+        String template = basePath;
+        if (StringUtils.isEmpty(template)) {
+            template = "/";
+        } else if (!template.startsWith("/")) {
+            template = "/" + template;
+        }
+        
+        template = combineUriTemplates(template, classPathTemplate);
+        return combineUriTemplates(template, methodPathTemplate);
+    }
+    
+    /**
+     * Gets the URI template of the operation from its resource info
+     * to assemble final URI template 
+     * @param ori operation resource info
+     * @return URI template
+     */
+    private static String getUriTemplate(OperationResourceInfo ori) {
+        final URITemplate template = ori.getURITemplate();
+        if (template != null) {
+            return template.getValue();
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Goes over sub-resource class resource templates (through parent chain) if necessary
+     * to assemble final URI template 
+     * @param cri root or subresource class resource info
+     * @return URI template chain
+     */
+    private static String getUriTemplate(ClassResourceInfo cri) {
+        final URITemplate template = cri.getURITemplate();
+        if (template != null) {
+            return template.getValue();
+        } else if (cri.getParent() != null) { /* probably subresource */
+            return getUriTemplate(cri.getParent());
+        } else {
+            return null; /* should not happen */
+        }
+    }
+    
+    /**
+     * Combines two URI templates together
+     * @param parent parent URI template
+     * @param child child URI template
+     * @return the URI template combined from the parent and child
+     */
+    private static String combineUriTemplates(final String parent, final String child) {
+        if (StringUtils.isEmpty(child)) {
+            return parent;
+        }
+
+        // The way URI templates are normalized in org.apache.cxf.jaxrs.model.URITemplate:
+        //  - empty or null become "/"
+        //  - "/" is added at the start if not present 
+        if ("/".equals(parent)) {
+            return child;
+        } else if ("/".equals(child)) {
+            return parent;
+        } else if (parent.endsWith("/")) {
+            // Remove only last slash
+            return parent.replaceAll("/$", "") + child;
+        } else {
+            return parent + child;
+        }
+    }
 
     // copy the input stream so that it is not inadvertently closed
     private static InputStream copyAndGetEntityStream(Message m) {
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/applications/LibraryApplication.java
similarity index 55%
copy from systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
copy to systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/applications/LibraryApplication.java
index b02a6e0..5a5d079 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/applications/LibraryApplication.java
@@ -17,28 +17,25 @@
  * under the License.
  */
 
-package org.apache.cxf.systest.jaxrs.resources;
+package org.apache.cxf.systest.jaxrs.applications;
 
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
-public interface LibraryApi {
-    @Produces({ MediaType.APPLICATION_JSON })
-    @GET
-    Response getBooks(@QueryParam("page") @DefaultValue("1") int page);
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
 
-    @Produces({ MediaType.APPLICATION_JSON })
-    @Path("{id}")
-    @GET
-    Response getBook(@PathParam("id") String id);
-    
-    @DELETE
-    void deleteBooks();
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import org.apache.cxf.systest.jaxrs.resources.Library;
+
+@ApplicationPath("app")
+public class LibraryApplication extends Application {
+    @Override
+    public Set<Object> getSingletons() {
+        return new HashSet<>(Arrays.asList(
+            new Library(), 
+            new JacksonJsonProvider()));
+    }
 }
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Catalog.java
similarity index 72%
copy from systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
copy to systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Catalog.java
index b02a6e0..5703a0a 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Catalog.java
@@ -19,26 +19,20 @@
 
 package org.apache.cxf.systest.jaxrs.resources;
 
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
+import java.util.Collection;
+import java.util.Collections;
+
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-public interface LibraryApi {
-    @Produces({ MediaType.APPLICATION_JSON })
-    @GET
-    Response getBooks(@QueryParam("page") @DefaultValue("1") int page);
 
+public class Catalog {
     @Produces({ MediaType.APPLICATION_JSON })
-    @Path("{id}")
+    @Path("{catalog}")
     @GET
-    Response getBook(@PathParam("id") String id);
-    
-    @DELETE
-    void deleteBooks();
+    public Collection<Book> getCatalog(@PathParam("catalog") String catalog) {
+        return Collections.singletonList(new Book(catalog, "John Smith"));
+    }
 }
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java
index 841599b..53fe0bd 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java
@@ -58,4 +58,9 @@ public class Library implements LibraryApi {
     public void deleteBooks() {
         throw new UnsupportedOperationException("Operation is not supported by the server");
     }
+    
+    @Override
+    public Catalog catalog() {
+        return new Catalog();
+    }
 }
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
index b02a6e0..911214d 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java
@@ -41,4 +41,7 @@ public interface LibraryApi {
     
     @DELETE
     void deleteBooks();
+    
+    @Path("/catalog")
+    Catalog catalog(); 
 }
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java
similarity index 86%
copy from systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java
copy to systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java
index 67aa150..8c39555 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java
@@ -27,6 +27,7 @@ import javax.ws.rs.NotFoundException;
 import javax.ws.rs.ProcessingException;
 import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
 import javax.ws.rs.core.Response;
 
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@@ -36,8 +37,8 @@ import org.apache.cxf.feature.Feature;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.metrics.MetricsFeature;
 import org.apache.cxf.metrics.MetricsProvider;
+import org.apache.cxf.systest.jaxrs.applications.LibraryApplication;
 import org.apache.cxf.systest.jaxrs.resources.Book;
-import org.apache.cxf.systest.jaxrs.resources.Library;
 import org.apache.cxf.systest.jaxrs.resources.LibraryApi;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -46,7 +47,6 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
 import org.springframework.boot.test.system.OutputCaptureRule;
 import org.springframework.boot.web.server.LocalServerPort;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -67,9 +67,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.entry;
 
 @RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SpringJaxrsTest.TestConfig.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SpringJaxrsApplicationTest.TestConfig.class)
 @ActiveProfiles("jaxrs")
-public class SpringJaxrsTest {
+public class SpringJaxrsApplicationTest {
     @Rule
     public OutputCaptureRule output = new OutputCaptureRule();
 
@@ -84,7 +84,6 @@ public class SpringJaxrsTest {
 
     @Configuration
     @EnableAutoConfiguration
-    @ComponentScan(basePackageClasses = Library.class)
     static class TestConfig {
         @Bean
         public SpringBus cxf() {
@@ -95,13 +94,13 @@ public class SpringJaxrsTest {
         }
         
         @Bean
-        public Feature metricsFeature(MetricsProvider metricsProvider) {
-            return new MetricsFeature(metricsProvider);
+        public Application application() {
+            return new LibraryApplication();
         }
         
         @Bean
-        public JacksonJsonProvider jacksonJsonProvider() {
-            return new JacksonJsonProvider();
+        public Feature metricsFeature(MetricsProvider metricsProvider) {
+            return new MetricsFeature(metricsProvider);
         }
     }
     
@@ -128,7 +127,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBooks"),
-                entry("uri", "/api/library"),
+                entry("uri", "/api/app/library"),
                 entry("outcome", "SUCCESS"),
                 entry("status", "200"));
         
@@ -142,7 +141,44 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library"),
+                entry("uri", "http://localhost:" + port + "/api/app/library"),
+                entry("outcome", "SUCCESS"),
+                entry("status", "200"));
+    }
+    
+    @Test
+    public void testJaxrsSubresourceSuccessMetric() {
+        final WebTarget target = createWebTarget().path("catalog").path("cxf");
+        
+        try (Response r = target.request().get()) {
+            assertThat(r.getStatus()).isEqualTo(200);
+        }
+        
+        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("method", "GET"),
+                entry("operation", "catalog"),
+                entry("uri", "/api/app/library/catalog/{catalog}"),
+                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("method", "GET"),
+                entry("operation", "UNKNOWN"),
+                entry("uri", "http://localhost:" + port + "/api/app/library/catalog/cxf"),
                 entry("outcome", "SUCCESS"),
                 entry("status", "200"));
     }
@@ -165,7 +201,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBook"),
-                entry("uri", "/api/library/100"),
+                entry("uri", "/api/app/library/{id}"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));
         
@@ -179,7 +215,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library/100"),
+                entry("uri", "http://localhost:" + port + "/api/app/library/100"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));
     }
@@ -202,7 +238,7 @@ public class SpringJaxrsTest {
                 entry("exception", "UnsupportedOperationException"),
                 entry("method", "DELETE"),
                 entry("operation", "deleteBooks"),
-                entry("uri", "/api/library"),
+                entry("uri", "/api/app/library"),
                 entry("outcome", "SERVER_ERROR"),
                 entry("status", "500"));
         
@@ -216,7 +252,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "DELETE"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library"),
+                entry("uri", "http://localhost:" + port + "/api/app/library"),
                 entry("outcome", "SERVER_ERROR"),
                 entry("status", "500"));
     }
@@ -271,7 +307,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBooks"),
-                entry("uri", "/api/library"),
+                entry("uri", "/api/app/library"),
                 entry("outcome", "SUCCESS"),
                 entry("status", "200"));
         
@@ -285,7 +321,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library"),
+                entry("uri", "http://localhost:" + port + "/api/app/library"),
                 entry("outcome", "SUCCESS"),
                 entry("status", "200"));
     }
@@ -308,7 +344,7 @@ public class SpringJaxrsTest {
                 entry("exception", "UnsupportedOperationException"),
                 entry("method", "DELETE"),
                 entry("operation", "deleteBooks"),
-                entry("uri", "/api/library"),
+                entry("uri", "/api/app/library"),
                 entry("outcome", "SERVER_ERROR"),
                 entry("status", "500"));
         
@@ -322,7 +358,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "DELETE"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library"),
+                entry("uri", "http://localhost:" + port + "/api/app/library"),
                 entry("outcome", "SERVER_ERROR"),
                 entry("status", "500"));
     }
@@ -345,7 +381,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBook"),
-                entry("uri", "/api/library/100"),
+                entry("uri", "/api/app/library/{id}"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));
         
@@ -359,7 +395,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + port + "/api/library/100"),
+                entry("uri", "http://localhost:" + port + "/api/app/library/100"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));
     }
@@ -387,14 +423,14 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "DELETE"),
                 entry("operation", "UNKNOWN"),
-                entry("uri", "http://localhost:" + fakePort + "/api/library"),
+                entry("uri", "http://localhost:" + fakePort + "/api/app/library"),
                 entry("outcome", "UNKNOWN"),
                 entry("status", "UNKNOWN"));
     }
     
     private LibraryApi createApi(int portToUse) {
         final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean();
-        factory.setAddress("http://localhost:" + portToUse + "/api/library");
+        factory.setAddress("http://localhost:" + portToUse + "/api/app/library");
         factory.setFeatures(Arrays.asList(new MetricsFeature(metricsProvider)));
         factory.setResourceClass(LibraryApi.class);
         return factory.create(LibraryApi.class);
@@ -405,7 +441,7 @@ public class SpringJaxrsTest {
             .newClient()
             .register(JacksonJsonProvider.class)
             .register(new MetricsFeature(metricsProvider))
-            .target("http://localhost:" + port + "/api/library");
+            .target("http://localhost:" + port + "/api/app/library");
     }
 
 }
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java
index 67aa150..338ec83 100644
--- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java
@@ -165,7 +165,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBook"),
-                entry("uri", "/api/library/100"),
+                entry("uri", "/api/library/{id}"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));
         
@@ -345,7 +345,7 @@ public class SpringJaxrsTest {
                 entry("exception", "None"),
                 entry("method", "GET"),
                 entry("operation", "getBook"),
-                entry("uri", "/api/library/100"),
+                entry("uri", "/api/library/{id}"),
                 entry("outcome", "CLIENT_ERROR"),
                 entry("status", "404"));