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 095de19b982..d1beb0c25c8 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 @@ -47,6 +47,7 @@ import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.xml.namespace.QName; @@ -206,6 +207,7 @@ public final class JAXRSUtils { "java.util.concurrent.CompletionStage" )); private static final LazyLoadedClass DATA_SOURCE_CLASS = new LazyLoadedClass("jakarta.activation.DataSource"); + private static final Pattern URI_SCHEME = Pattern.compile("^([a-zA-Z]{2,20}):\\/\\/", Pattern.CASE_INSENSITIVE); // Class to lazily call the ClassLoaderUtil.loadClass, but do it once // and cache the result. Then use the class to create instances as needed. @@ -2114,14 +2116,27 @@ public static String getUriTemplate(Message message, ClassResourceInfo cri, Oper String template = basePath; if (StringUtils.isEmpty(template)) { template = "/"; - } else if (!template.startsWith("/")) { + } else if (!template.startsWith("/") && !hasScheme(template)) { template = "/" + template; } template = combineUriTemplates(template, classPathTemplate); return combineUriTemplates(template, methodPathTemplate); } - + + /** + * Checks if the URI string is absolute with the scheme + * @param uriStr URI string + * @return "true" if the URI string is absolute with the scheme, "false" otherwise + */ + private static boolean hasScheme(String uriStr) { + if (uriStr == null) { + return false; + } else { + return URI_SCHEME.matcher(uriStr).find(); + } + } + /** * Gets the URI template of the operation from its resource info * to assemble final URI template diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java index d20ffa74a61..8b4a524deb9 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java @@ -88,6 +88,7 @@ import org.apache.cxf.jaxrs.model.OperationResourceInfo; import org.apache.cxf.jaxrs.model.Parameter; import org.apache.cxf.jaxrs.model.ParameterType; +import org.apache.cxf.jaxrs.model.URITemplate; import org.apache.cxf.jaxrs.utils.AnnotationUtils; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.InjectionUtils; @@ -921,6 +922,7 @@ protected Object doChainedInvocation(URI uri, outMessage.put(PROXY_METHOD_PARAM_BODY_INDEX, bodyIndex); } outMessage.getInterceptorChain().add(bodyWriter); + outMessage.put(URITemplate.URI_TEMPLATE, JAXRSUtils.getUriTemplate(outMessage, cri, ori)); Map reqContext = getRequestContext(outMessage); reqContext.put(OperationResourceInfo.class.getName(), ori); diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java index cd633718bc3..013f3b638dd 100644 --- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java +++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsApplicationTest.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.Arrays; +import java.util.Collection; import java.util.Map; import jakarta.ws.rs.InternalServerErrorException; @@ -415,7 +416,7 @@ public void testJaxrsProxyFailedMetric() { entry("exception", "None"), entry("method", "GET"), entry("operation", "getBook"), - entry("uri", "http://localhost:" + port + "/api/app/library/100"), + entry("uri", "http://localhost:" + port + "/api/app/library/{id}"), entry("outcome", "CLIENT_ERROR"), entry("status", "404")); } @@ -448,10 +449,51 @@ public void testJaxrsProxyClientExceptionMetric() { entry("status", "UNKNOWN")); } + @Test + public void testJaxrsProxySubresourceSuccessMetric() { + final LibraryApi api = createApi(port); + + final Collection books = api.catalog().getCatalog("cxf"); + assertThat(books).hasSize(1); + + await() + .atMost(Duration.ofSeconds(1)) + .ignoreException(MeterNotFoundException.class) + .until(() -> registry.get("cxf.server.requests").timers(), not(empty())); + RequiredSearch serverRequestMetrics = registry.get("cxf.server.requests"); + + Map 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 clientTags = clientRequestMetrics.timer().getId().getTags().stream() + .collect(toMap(Tag::getKey, Tag::getValue)); + + assertThat(clientTags) + .containsOnly( + entry("exception", "None"), + entry("method", "GET"), + entry("operation", "getCatalog"), + entry("uri", "http://localhost:" + port + "/api/app/library/catalog/{catalog}"), + entry("outcome", "SUCCESS"), + entry("status", "200")); + } + private LibraryApi createApi(int portToUse) { final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean(); factory.setAddress("http://localhost:" + portToUse + "/api/app/library"); factory.setFeatures(Arrays.asList(new MetricsFeature(metricsProvider))); + factory.setProvider(new JacksonJsonProvider()); factory.setResourceClass(LibraryApi.class); return factory.create(LibraryApi.class); } 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 f7617f3a1b4..65d7a775a20 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 @@ -385,7 +385,7 @@ public void testJaxrsProxyFailedMetric() { entry("exception", "None"), entry("method", "GET"), entry("operation", "getBook"), - entry("uri", "http://localhost:" + port + "/api/library/100"), + entry("uri", "http://localhost:" + port + "/api/library/{id}"), entry("outcome", "CLIENT_ERROR"), entry("status", "404")); }