diff --git a/appserver/tests/application/pom.xml b/appserver/tests/application/pom.xml
index e8254f07296..3c6eab72eeb 100755
--- a/appserver/tests/application/pom.xml
+++ b/appserver/tests/application/pom.xml
@@ -71,6 +71,11 @@
jakarta.annotation-api
provided
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
jakarta.enterprise.concurrent
jakarta.enterprise.concurrent-api
diff --git a/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterApplication.java b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterApplication.java
new file mode 100644
index 00000000000..17d554aa5e5
--- /dev/null
+++ b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * 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 org.glassfish.main.test.app.rest.convert.webapp;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+import java.util.Set;
+
+@ApplicationPath("/")
+public class ParamConverterApplication extends Application {
+
+ @Override
+ public Set> getClasses() {
+ return Set.of(ParamConverterResource.class, PatternParamConverterProvider.class);
+ }
+}
diff --git a/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterResource.java b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterResource.java
new file mode 100644
index 00000000000..17a75fed76f
--- /dev/null
+++ b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/ParamConverterResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * 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 org.glassfish.main.test.app.rest.convert.webapp;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
+import java.util.regex.Pattern;
+
+@Path("pattern")
+public class ParamConverterResource {
+
+ @GET
+ @Path("{pattern}")
+ public String find(
+ @PathParam("pattern")
+ Pattern pattern) {
+
+ return "1234";
+ }
+}
diff --git a/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/PatternParamConverterProvider.java b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/PatternParamConverterProvider.java
new file mode 100644
index 00000000000..b4084deb56d
--- /dev/null
+++ b/appserver/tests/application/src/main/java/org/glassfish/main/test/app/rest/convert/webapp/PatternParamConverterProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * 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 org.glassfish.main.test.app.rest.convert.webapp;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.ext.ParamConverter;
+import jakarta.ws.rs.ext.ParamConverterProvider;
+import jakarta.ws.rs.ext.Provider;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+@Provider
+@Singleton
+public class PatternParamConverterProvider implements ParamConverterProvider {
+ private static final Logger LOG = System.getLogger(PatternParamConverterProvider.class.getName());
+
+ @Override
+ public ParamConverter getConverter(final Class rawType, final Type genericType, final Annotation[] annotations) {
+ LOG.log(Level.INFO, () -> "getConverter(rawType=" + rawType + ", genericType=" + genericType + ", annotations="
+ + Arrays.toString(annotations) + ")");
+ if (rawType.isAssignableFrom(Pattern.class)) {
+ return new PatternConverter<>();
+ }
+ return null;
+ }
+
+ private static class PatternConverter implements ParamConverter {
+
+ private static final Logger LOG = System.getLogger(PatternConverter.class.getName());
+
+ @Override
+ public final T fromString(final String regex) {
+ LOG.log(Level.INFO, "fromString(regex={0})", regex);
+ if (regex == null) {
+ throw new IllegalArgumentException("Expected IAE exception");
+ }
+ return (T) Pattern.compile(regex);
+ }
+
+
+ @Override
+ public final String toString(final T pattern) {
+ LOG.log(Level.INFO, "toString(pattern={0})", pattern);
+ return pattern == null ? null : pattern.toString();
+ }
+ }
+}
diff --git a/appserver/tests/application/src/test/java/org/glassfish/main/test/app/rest/convert/RestParamConverterTest.java b/appserver/tests/application/src/test/java/org/glassfish/main/test/app/rest/convert/RestParamConverterTest.java
new file mode 100644
index 00000000000..744c8cb46c0
--- /dev/null
+++ b/appserver/tests/application/src/test/java/org/glassfish/main/test/app/rest/convert/RestParamConverterTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * 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 org.glassfish.main.test.app.rest.convert;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+import org.glassfish.main.itest.tools.GlassFishTestEnvironment;
+import org.glassfish.main.itest.tools.asadmin.Asadmin;
+import org.glassfish.main.itest.tools.asadmin.AsadminResult;
+import org.glassfish.main.itest.tools.asadmin.AsadminResultMatcher;
+import org.glassfish.main.test.app.rest.convert.webapp.ParamConverterResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RestParamConverterTest {
+
+ private static final Logger LOG = System.getLogger(RestParamConverterTest.class.getName());
+
+ private static final String APP_NAME = RestParamConverterTest.class.getSimpleName();
+ private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
+
+ @BeforeAll
+ static void deploy() {
+ File war = createDeployment();
+ try {
+ AsadminResult result = ASADMIN.exec("deploy", "--contextroot", "/", "--name", APP_NAME,
+ war.getAbsolutePath());
+ assertThat(result, AsadminResultMatcher.asadminOK());
+ } finally {
+ war.delete();
+ }
+ }
+
+
+ @AfterAll
+ static void undeploy() {
+ AsadminResult result = ASADMIN.exec("undeploy", APP_NAME);
+ assertThat(result, AsadminResultMatcher.asadminOK());
+ }
+
+
+ @Test
+ public void testValidPattern() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ WebTarget target = client.target("http://localhost:8080/");
+ Response response = target.path("pattern").path("[a-z]+").request(TEXT_PLAIN_TYPE).buildGet().invoke();
+ assertEquals(200, response.getStatus());
+ assertEquals("1234", response.readEntity(String.class));
+ }
+ }
+
+
+ @Test
+ public void testEmptyPattern() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ WebTarget target = client.target("http://localhost:8080/");
+ assertEquals(404,
+ target.path("pattern").path("").request().buildGet().invoke().getStatus());
+ }
+ }
+
+
+ @Test
+ public void testInvalidPattern() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ WebTarget target = client.target("http://localhost:8080/");
+ assertEquals(404,
+ target.path("pattern").path("[a-z").request().buildGet().invoke().getStatus());
+ }
+ }
+
+
+ private static File createDeployment() {
+ WebArchive war = ShrinkWrap.create(WebArchive.class, "RestParamConverter.war")
+ .addPackage(ParamConverterResource.class.getPackage());
+ LOG.log(Level.INFO, war.toString(true));
+ try {
+ File tempFile = File.createTempFile(APP_NAME, ".war");
+ war.as(ZipExporter.class).exportTo(tempFile, true);
+ return tempFile;
+ } catch (IOException e) {
+ throw new IllegalStateException("Deployment failed - cannot load the input archive!", e);
+ }
+ }
+}