Added UriInfo#getMatchedResourceTemplate method (#1236)
* Added UriInfo#getMatchedResourceTemplate method
Signed-off-by: jansupol <jan.supol@oracle.com>
* Reflected review comments
Added TCK tests
Signed-off-by: jansupol <jan.supol@oracle.com>
* Renamed the client class
Signed-off-by: jansupol <jan.supol@oracle.com>
* Added @Path to Sub-resource method
Signed-off-by: jansupol <jan.supol@oracle.com>
---------
Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/core/UriInfo.java b/jaxrs-api/src/main/java/jakarta/ws/rs/core/UriInfo.java
index 26a0a9f..2fad294 100644
--- a/jaxrs-api/src/main/java/jakarta/ws/rs/core/UriInfo.java
+++ b/jaxrs-api/src/main/java/jakarta/ws/rs/core/UriInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,8 @@
import java.net.URI;
import java.util.List;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ApplicationPath;
/**
* An injectable interface that provides access to application and request URI information. Relative URIs are relative
@@ -231,6 +233,71 @@
public List<String> getMatchedURIs();
/**
+ * <p>
+ * Get a URI template that includes all {@link Path Paths} (including {@link ApplicationPath})
+ * matched by the current request's URI.
+ * </p>
+ * <p>
+ * Each {@link Path} value used to match a resource class, a sub-resource method or a sub-resource locator is concatenated
+ * into a single {@code String} value. The template does not include query parameters but does include matrix parameters
+ * if present in the request URI. The concatenation is ordered in the request URI matching order, with the
+ * {@link ApplicationPath} value first and current resource URI last. E.g. given the following resource classes:
+ * </p>
+ *
+ * <pre>
+ * @Path("foo")
+ * public class FooResource {
+ * @GET
+ * @Path("{foo:[f-z][a-z]*}")
+ * public String getFoo() {...}
+ *
+ * @Path("{bar:[b-e][a-z]*}")
+ * public BarResource getBarResource() {...}
+ * }
+ *
+ * public class BarResource {
+ * @GET
+ * @Path("{id}{id:[0-9]}")
+ * public String getBar() {...}
+ * }
+ * </pre>
+ *
+ * <p>
+ * The values returned by this method based on request uri and where the method is called from are:
+ * </p>
+ *
+ * <table border="1">
+ * <caption>Matched URIs from requests</caption>
+ * <tr>
+ * <th>Request</th>
+ * <th>Called from</th>
+ * <th>Value(s)</th>
+ * </tr>
+ * <tr>
+ * <td>GET /foo</td>
+ * <td>FooResource.getFoo</td>
+ * <td>/foo/{foo:[f-z][a-z]*}</td>
+ * </tr>
+ * <tr>
+ * <td>GET /foo/bar</td>
+ * <td>FooResource.getBarResource</td>
+ * <td>/foo/{bar:[b-e][a-z]*}</td>
+ * </tr>
+ * <tr>
+ * <td>GET /foo/bar/id0</td>
+ * <td>BarResource.getBar</td>
+ * <td>/foo/{bar:[b-e][a-z]*}/{id}{id:[0-9]}</td>
+ * </tr>
+ * </table>
+ *
+ * In case the method is invoked prior to the request matching (e.g. from a pre-matching filter), the method returns an
+ * empty string.
+ *
+ * @return A concatenated string of {@link Path} templates.
+ */
+ public String getMatchedResourceTemplate();
+
+ /**
* Get a read-only list of URIs for matched resources.
*
* Each entry is a relative URI that matched a resource class, a sub-resource method or a sub-resource locator. Entries
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/TSAppConfig.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/TSAppConfig.java
new file mode 100644
index 0000000..afcfdda
--- /dev/null
+++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/TSAppConfig.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package ee.jakarta.tck.ws.rs.jaxrs40.ee.rs.core.uriinfo;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@ApplicationPath("/app")
+public class TSAppConfig extends Application {
+
+ public Set<Class<?>> getClasses() {
+ Set<Class<?>> resources = new HashSet<Class<?>>();
+ resources.add(UriInfoTestResource.class);
+ return resources;
+ }
+}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfo40ClientIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfo40ClientIT.java
new file mode 100644
index 0000000..f6c20ad
--- /dev/null
+++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfo40ClientIT.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package ee.jakarta.tck.ws.rs.jaxrs40.ee.rs.core.uriinfo;
+
+import ee.jakarta.tck.ws.rs.common.JAXRSCommonClient;
+import ee.jakarta.tck.ws.rs.lib.util.TestUtil;
+import jakarta.ws.rs.core.UriInfo;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit5.ArquillianExtension;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.io.IOException;
+
+/*
+ * @class.setup_props: webServerHost;
+ * webServerPort;
+ */
+@ExtendWith(ArquillianExtension.class)
+public class UriInfo40ClientIT extends JAXRSCommonClient {
+ protected static final String ROOT = "jaxrs40_ee_core_uriinfo_web";
+
+ protected static final String RESOURCE = "app/resource";
+
+ public UriInfo40ClientIT() {
+ setup();
+ setContextRoot("/" + ROOT + "/" + RESOURCE);
+ }
+
+
+ @BeforeEach
+ void logStartTest(TestInfo testInfo) {
+ TestUtil.logMsg("STARTING TEST : " + testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ void logFinishTest(TestInfo testInfo) {
+ TestUtil.logMsg("FINISHED TEST : " + testInfo.getDisplayName());
+ }
+
+ @Deployment(testable = false)
+ public static WebArchive createDeployment() throws IOException{
+ WebArchive archive = ShrinkWrap.create(WebArchive.class, "jaxrs40_ee_core_uriinfo_web.war");
+ archive.addClasses(TSAppConfig.class, UriInfoTestResource.class);
+ return archive;
+ }
+
+ /* Run test */
+
+ /**
+ * @testName: getMatchedResourceTemplateOneTest
+ *
+ * @assertion_ids: JAXRS:JAVADOC:97;
+ *
+ * @test_Strategy: Check the template containing {@link UriInfoTestResource#ONE_POST}
+ */
+ @Test
+ public void getMatchedResourceTemplateOneTest() throws Fault {
+ setProperty(Property.REQUEST, buildRequest(Request.POST, "one/azazaz00"));
+ setProperty(Property.SEARCH_STRING, "/app/resource/one/" + UriInfoTestResource.ONE_POST);
+ invoke();
+ }
+
+ /**
+ * @testName: getMatchedResourceTemplateTwoGetTest
+ *
+ * @assertion_ids: JAXRS:JAVADOC:97;
+ *
+ * @test_Strategy: Check the template containing {@link UriInfoTestResource#TWO_GET}
+ */
+ @Test
+ public void getMatchedResourceTemplateTwoGetTest() throws Fault {
+ setProperty(Property.REQUEST, buildRequest(Request.GET, "two/P/abc/MyNumber"));
+ setProperty(Property.SEARCH_STRING, "/app/resource/two/" + UriInfoTestResource.TWO_GET);
+ invoke();
+ }
+
+ /**
+ * @testName: getMatchedResourceTemplateTwoPostTest
+ *
+ * @assertion_ids: JAXRS:JAVADOC:97;
+ *
+ * @test_Strategy: Check the template containing {@link UriInfoTestResource#TWO_POST}
+ */
+ @Test
+ public void getMatchedResourceTemplateTwoPostTest() throws Fault {
+ setProperty(Property.REQUEST, buildRequest(Request.POST, "two/P/abc/MyNumber"));
+ setProperty(Property.SEARCH_STRING, "/app/resource/two/" + UriInfoTestResource.TWO_POST);
+ invoke();
+ }
+
+ /**
+ * @testName: getMatchedResourceTemplateSubTest
+ *
+ * @assertion_ids: JAXRS:JAVADOC:97;
+ *
+ * @test_Strategy: Check the template including subresource containing {@link UriInfoTestResource#THREE_SUB}
+ */
+ @Test
+ public void getMatchedResourceTemplateSubTest() throws Fault {
+ setProperty(Property.REQUEST, buildRequest(Request.PUT, "three/a/z"));
+ setProperty(Property.SEARCH_STRING,
+ "/app/resource/three/" + UriInfoTestResource.THREE_SUB + "/" + UriInfoTestResource.THREE_SUB);
+ invoke();
+ }
+}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfoTestResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfoTestResource.java
new file mode 100644
index 0000000..f648482
--- /dev/null
+++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/jaxrs40/ee/rs/core/uriinfo/UriInfoTestResource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package ee.jakarta.tck.ws.rs.jaxrs40.ee.rs.core.uriinfo;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
+
+@Path("/resource")
+public class UriInfoTestResource {
+ public static final String ONE_POST = "{name:[a-zA-Z][a-zA-Z_0-9]*}";
+ public static final String TWO_GET = "{Prefix}{p:/?}{id: ((\\d+)?)}/abc{p2:/?}{number: (([A-Za-z0-9]*)?)}";
+ public static final String TWO_POST = "{Prefix}{p:/?}{id: ((\\d+)?)}/abc/{yeah}";
+ public static final String THREE_SUB = "{x:[a-z]}";
+
+ public static class SubGet {
+ @PUT
+ @Path(THREE_SUB)
+ public String get(@Context UriInfo uriInfo) {
+ return uriInfo.getMatchedResourceTemplate();
+ }
+ }
+
+ @POST
+ @Path("one/" + ONE_POST)
+ public Response post(@Context UriInfo info) {
+ return Response.ok(info.getMatchedResourceTemplate()).build();
+ }
+
+ @GET
+ @Path("two/" + TWO_GET)
+ public Response get(@Context UriInfo info) {
+ return Response.ok(info.getMatchedResourceTemplate()).build();
+ }
+
+ @POST
+ @Path("two/" + TWO_POST)
+ public Response postTwo(@Context UriInfo info) {
+ return Response.ok(info.getMatchedResourceTemplate()).build();
+ }
+
+ @Path("three/" + THREE_SUB)
+ public SubGet doAnything4() {
+ return new SubGet();
+ }
+}