Add tests for required context injection with CDI. Signed-off-by: James R. Perkins <jperkins@ibm.com>
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/common/util/CdiSupport.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/common/util/CdiSupport.java new file mode 100644 index 0000000..619c2fb --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/common/util/CdiSupport.java
@@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.common.util; + +import org.jboss.shrinkwrap.api.asset.StringAsset; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +public class CdiSupport { + + public static final StringAsset BEANS_XML = new StringAsset(""" + <?xml version="1.0" encoding="UTF-8"?> + <beans xmlns="https://jakarta.ee/xml/ns/jakartaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd" + version="4.0" bean-discovery-mode="annotated"> + </beans>"""); +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectFilter.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectFilter.java new file mode 100644 index 0000000..6fc5158 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectFilter.java
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.client.inject; + +import jakarta.inject.Inject; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientRequestFilter; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.Providers; + +/** + * A client-side filter that verifies {@code @Inject} works for {@link Configuration} and {@link Providers} as a + * replacement for the deprecated {@code @Context} annotation on the client side. The Jakarta REST runtime is + * responsible for resolving these injection points regardless of whether a CDI container is present. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +public class ClientInjectFilter implements ClientRequestFilter { + + @Inject + private Configuration configuration; + + @Inject + private Providers providers; + + @Override + public void filter(final ClientRequestContext requestContext) { + final ClientInjectionDescriptor descriptor = ClientInjectionDescriptor.of(configuration, providers); + requestContext.abortWith(Response.ok(descriptor).type(MediaType.APPLICATION_JSON_TYPE).build()); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionDescriptor.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionDescriptor.java new file mode 100644 index 0000000..b9db055 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionDescriptor.java
@@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.client.inject; + +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.ext.Providers; + +public record ClientInjectionDescriptor(String configuration, String providers) { + + public static ClientInjectionDescriptor of(final Configuration configuration, final Providers providers) { + return new ClientInjectionDescriptor( + configuration == null ? null : String.valueOf(configuration.getRuntimeType()), + providers == null ? null : providers.getClass().getName()); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionIT.java new file mode 100644 index 0000000..8d9bed7 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/client/inject/ClientInjectionIT.java
@@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.client.inject; + +import jakarta.ws.rs.RuntimeType; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.MediaType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests that {@code @Inject} works for {@link jakarta.ws.rs.core.Configuration} and + * {@link jakarta.ws.rs.ext.Providers} in client-side providers. The Jakarta REST runtime is responsible for + * resolving these injection points regardless of whether a CDI container is present. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@Tag("client") +public class ClientInjectionIT { + + @Test + void clientFilterInjection() { + try (Client client = ClientBuilder.newBuilder().register(ClientInjectFilter.class).build()) { + // We abort the client response so any URI should be acceptable. + final ClientInjectionDescriptor descriptor = client.target("http://localhost:8080") + .request(MediaType.APPLICATION_JSON_TYPE) + .get(ClientInjectionDescriptor.class); + Assertions.assertAll( + () -> Assertions.assertNotNull(descriptor.configuration(), + "Configuration not injected into client filter"), + () -> Assertions.assertEquals(RuntimeType.CLIENT.name(), descriptor.configuration(), + "Configuration runtime type should be CLIENT"), + () -> Assertions.assertNotNull(descriptor.providers(), + "Providers not injected into client filter") + ); + } + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/ContextInjectionDescriptor.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/ContextInjectionDescriptor.java new file mode 100644 index 0000000..3696af7 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/ContextInjectionDescriptor.java
@@ -0,0 +1,64 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import jakarta.ws.rs.container.ResourceContext; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.Providers; +import jakarta.ws.rs.sse.Sse; + +public record ContextInjectionDescriptor( + String application, + String configuration, + String httpHeaders, + String providers, + String request, + String resourceContext, + String resourceInfo, + String securityContext, + String sse, + String uriInfo) { + + public static ContextInjectionDescriptor of(final Application application, + final Configuration configuration, + final HttpHeaders httpHeaders, + final Providers providers, + final Request request, + final ResourceContext resourceContext, + final ResourceInfo resourceInfo, + final SecurityContext securityContext, + final Sse sse, + final UriInfo uriInfo) { + return new ContextInjectionDescriptor( + application == null ? null : application.getClass().getName(), + configuration == null ? null : String.valueOf(configuration.getRuntimeType()), + httpHeaders == null ? null : httpHeaders.getHeaderString("test-header"), + providers == null ? null : providers.getClass().getName(), + request == null ? null : request.getMethod(), + resourceContext == null ? null : resourceContext.getClass().getName(), + resourceInfo == null ? null : resourceInfo.getResourceMethod().getName(), + securityContext == null ? null : String.valueOf(securityContext.isSecure()), + sse == null ? null : sse.getClass().getName(), + uriInfo == null ? null : uriInfo.getPath()); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextApplication.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextApplication.java new file mode 100644 index 0000000..25fe628 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextApplication.java
@@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import java.util.Collections; +import java.util.Map; + +import jakarta.inject.Inject; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.container.ResourceContext; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.Providers; + +@ApplicationPath("/") +public class RequiredContextApplication extends Application { + + @Inject + UriInfo info; + + @Inject + Request request; + + @Inject + HttpHeaders headers; + + @Inject + SecurityContext security; + + @Inject + Providers providers; + + @Inject + ResourceContext resources; + + @Override + public Map<String, Object> getProperties() { + return Collections.singletonMap("test.property", "test value"); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextConstructorResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextConstructorResource.java new file mode 100644 index 0000000..e18989f --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextConstructorResource.java
@@ -0,0 +1,91 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.container.ResourceContext; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.Providers; +import jakarta.ws.rs.sse.Sse; + +@Path("/inject-constructor") +public class RequiredContextConstructorResource { + + private final Application application; + private final Configuration configuration; + private final HttpHeaders httpHeaders; + private final Providers providers; + private final Request request; + private final ResourceContext resourceContext; + private final ResourceInfo resourceInfo; + private final SecurityContext securityContext; + private final Sse sse; + private final UriInfo uriInfo; + + RequiredContextConstructorResource() { + this.application = null; + this.configuration = null; + this.httpHeaders = null; + this.providers = null; + this.request = null; + this.resourceContext = null; + this.resourceInfo = null; + this.securityContext = null; + this.sse = null; + this.uriInfo = null; + } + + @Inject + public RequiredContextConstructorResource(final Application application, + final Configuration configuration, + final HttpHeaders httpHeaders, + final Providers providers, + final Request request, + final ResourceContext resourceContext, + final ResourceInfo resourceInfo, + final SecurityContext securityContext, + final Sse sse, + final UriInfo uriInfo) { + this.application = application; + this.configuration = configuration; + this.httpHeaders = httpHeaders; + this.providers = providers; + this.request = request; + this.resourceContext = resourceContext; + this.resourceInfo = resourceInfo; + this.securityContext = securityContext; + this.sse = sse; + this.uriInfo = uriInfo; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public ContextInjectionDescriptor get() { + return ContextInjectionDescriptor.of(application, configuration, httpHeaders, providers, + request, resourceContext, resourceInfo, securityContext, sse, uriInfo); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextInjectionIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextInjectionIT.java new file mode 100644 index 0000000..596466b --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextInjectionIT.java
@@ -0,0 +1,279 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import ee.jakarta.tck.ws.rs.common.provider.PrintingErrorHandler; +import ee.jakarta.tck.ws.rs.common.provider.StringBean; +import ee.jakarta.tck.ws.rs.common.provider.StringBeanEntityProvider; +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import ee.jakarta.tck.ws.rs.lib.util.TestUtil; +import jakarta.ws.rs.RuntimeType; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.sse.SseEventSource; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +@ArquillianTest +@Tag("cdi") +class RequiredContextInjectionIT { + + @Deployment(testable = false) + public static WebArchive createDeployment() throws IOException { + return ShrinkWrap.create(WebArchive.class, "rest-cdi-injection.war") + .addClasses(RequiredContextApplication.class, + RequiredContextResource.class, + RequiredContextConstructorResource.class, + ContextInjectionDescriptor.class, + StringBeanEntityProviderWithInjectables.class, + StringBeanEntityProvider.class, + PrintingErrorHandler.class, + StringBean.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + private static Client client; + + @ArquillianResource + private URI baseUri; + + @BeforeAll + static void createClient() { + client = ClientBuilder.newBuilder() + .build(); + } + + @AfterAll + static void closeClient() { + if (client != null) { + client.close(); + } + } + + @BeforeEach + void logStartTest(final TestInfo testInfo) { + TestUtil.logMsg("STARTING TEST : " + testInfo.getDisplayName()); + } + + @AfterEach + void logFinishTest(final TestInfo testInfo) { + TestUtil.logMsg("FINISHED TEST : " + testInfo.getDisplayName()); + } + + @Test + void constructorInjection() { + final ContextInjectionDescriptor descriptor = getDescriptor("inject-constructor"); + assertAllInjected(descriptor, "constructor"); + Assertions.assertEquals("GET", descriptor.request(), + () -> "Request method incorrect via constructor injection: %s".formatted(descriptor)); + Assertions.assertEquals(RuntimeType.SERVER.name(), descriptor.configuration(), + () -> "Configuration runtime type incorrect via constructor injection: %s".formatted(descriptor)); + Assertions.assertEquals("test-value", descriptor.httpHeaders(), + () -> "HttpHeaders value incorrect via constructor injection: %s".formatted(descriptor)); + Assertions.assertEquals("get", descriptor.resourceInfo(), + () -> "ResourceInfo method name incorrect via constructor injection: %s".formatted(descriptor)); + } + + @Test + void fieldInjection() { + final ContextInjectionDescriptor descriptor = getDescriptor("inject/field"); + assertAllInjected(descriptor, "field"); + Assertions.assertEquals("GET", descriptor.request(), + () -> "Request method incorrect via field injection: %s".formatted(descriptor)); + Assertions.assertEquals(RuntimeType.SERVER.name(), descriptor.configuration(), + () -> "Configuration runtime type incorrect via field injection: %s".formatted(descriptor)); + Assertions.assertEquals("test-value", descriptor.httpHeaders(), + () -> "HttpHeaders value incorrect via field injection: %s".formatted(descriptor)); + Assertions.assertEquals("field", descriptor.resourceInfo(), + () -> "ResourceInfo method name incorrect via field injection: %s".formatted(descriptor)); + } + + @Test + void methodInjection() { + final ContextInjectionDescriptor descriptor = getDescriptor("inject/method"); + assertAllInjected(descriptor, "method parameter"); + Assertions.assertEquals("GET", descriptor.request(), + () -> "Request method incorrect via method parameter injection: %s".formatted(descriptor)); + Assertions.assertEquals(RuntimeType.SERVER.name(), descriptor.configuration(), + () -> "Configuration runtime type incorrect via method parameter injection: %s".formatted(descriptor)); + Assertions.assertEquals("test-value", descriptor.httpHeaders(), + () -> "HttpHeaders value incorrect via method parameter injection: %s".formatted(descriptor)); + Assertions.assertEquals("method", descriptor.resourceInfo(), + () -> "ResourceInfo method name incorrect via method parameter injection: %s".formatted(descriptor)); + } + + @Test + void providerInjection() { + final Response response = getJson("inject/providers"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus(), + () -> "Failed to look up provider: " + response.readEntity(String.class)); + final ContextInjectionDescriptor descriptor = response.readEntity(ContextInjectionDescriptor.class); + Assertions.assertAll( + () -> Assertions.assertNotNull(descriptor.application(), () -> "Application not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.configuration(), () -> "Configuration not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.httpHeaders(), () -> "HttpHeaders not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.providers(), () -> "Providers not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.request(), () -> "Request not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.resourceContext(), () -> "ResourceContext not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.securityContext(), () -> "SecurityContext not injected into provider: %s".formatted(descriptor)), + () -> Assertions.assertNotNull(descriptor.uriInfo(), () -> "UriInfo not injected into provider: %s".formatted(descriptor)) + ); + } + + @Test + void application() { + final Response response = get("inject/application/test.property"); + final var found = response.readEntity(String.class); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus(), found); + Assertions.assertEquals("test value", found); + } + + @Test + void configuration() { + final Response response = get("inject/configuration"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals(RuntimeType.SERVER.name(), response.readEntity(String.class)); + } + + @Test + void httpHeader() { + final Response response = get("inject/httpHeaders/test-header"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals("test-value", response.readEntity(String.class)); + } + + @Test + void request() { + final Response response = get("inject/request"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals("GET", response.readEntity(String.class)); + } + + @Test + void resourceContext() { + final Response response = get("inject/resourceContext"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertTrue(response.readEntity(String.class) + .startsWith(RequiredContextResource.class.getCanonicalName())); + } + + @Test + void resourceInfo() { + final Response response = get("inject/resourceInfo"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals("resourceInfo", response.readEntity(String.class)); + } + + @Test + void securityContext() { + final Response response = get("inject/securityContext"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals("false", response.readEntity(String.class)); + } + + @Test + void uriInfo() { + final Response response = get("inject/uriInfo"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals("/inject/uriInfo", response.readEntity(String.class)); + } + + @Test + void sse() throws Exception { + final WebTarget target = client.target(generateUri("inject/sse")); + final CompletableFuture<String> cf = new CompletableFuture<>(); + try (SseEventSource source = SseEventSource.target(target).build()) { + source.register(event -> { + try { + cf.complete(event.readData()); + } catch (Throwable t) { + cf.completeExceptionally(t); + } + }); + source.open(); + Assertions.assertEquals("test", cf.get(5, TimeUnit.SECONDS)); + } + } + + @Test + void asyncResponseByTypeAlone() { + final Response response = get("inject/async"); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus(), + () -> "AsyncResponse by type alone failed: %s".formatted(response.readEntity(String.class))); + Assertions.assertEquals("async-test", response.readEntity(String.class)); + } + + private static void assertAllInjected(final ContextInjectionDescriptor descriptor, final String injectionType) { + Assertions.assertAll( + () -> Assertions.assertNotNull(descriptor.application(), () -> "Application not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.configuration(), () -> "Configuration not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.httpHeaders(), () -> "HttpHeaders not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.providers(), () -> "Providers not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.request(), () -> "Request not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.resourceContext(), () -> "ResourceContext not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.resourceInfo(), () -> "ResourceInfo not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.securityContext(), () -> "SecurityContext not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.sse(), () -> "Sse not injected via %s: %s".formatted(injectionType, descriptor)), + () -> Assertions.assertNotNull(descriptor.uriInfo(), () -> "UriInfo not injected via %s: %s".formatted(injectionType, descriptor)) + ); + } + + private ContextInjectionDescriptor getDescriptor(final String path) { + final Response response = getJson(path); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus(), + () -> "Unexpected status for " + path + ": " + response.readEntity(String.class)); + return response.readEntity(ContextInjectionDescriptor.class); + } + + private Response get(final String path) { + return client.target(generateUri(path)) + .request() + .header("test-header", "test-value") + .get(); + } + + private Response getJson(final String path) { + return client.target(generateUri(path)) + .request(MediaType.APPLICATION_JSON_TYPE) + .header("test-header", "test-value") + .get(); + } + + private URI generateUri(final String path) { + return UriBuilder.fromUri(baseUri).path(path).build(); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextResource.java new file mode 100644 index 0000000..ec12e62 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/RequiredContextResource.java
@@ -0,0 +1,227 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; + +import ee.jakarta.tck.ws.rs.common.provider.StringBean; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.ResourceContext; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.Providers; +import jakarta.ws.rs.sse.Sse; +import jakarta.ws.rs.sse.SseEventSink; + +@Path("/inject") +public class RequiredContextResource { + + @Inject + Application application; + + @Inject + UriInfo uriInfo; + + @Inject + Request request; + + @Inject + HttpHeaders httpHeaders; + + @Inject + SecurityContext securityContext; + + @Inject + Providers providers; + + @Inject + ResourceContext resourceContext; + + @Inject + Configuration configuration; + + @Inject + ResourceInfo resourceInfo; + + @Inject + Sse sse; + + @GET + @Path("field") + @Produces(MediaType.APPLICATION_JSON) + public ContextInjectionDescriptor field() { + return ContextInjectionDescriptor.of(application, configuration, httpHeaders, providers, + request, resourceContext, resourceInfo, securityContext, sse, uriInfo); + } + + @GET + @Path("method") + @Produces(MediaType.APPLICATION_JSON) + public ContextInjectionDescriptor method(final Application application, final UriInfo uriInfo, + final Request request, final HttpHeaders httpHeaders, + final SecurityContext securityContext, final Providers providers, + final ResourceContext resourceContext, final Configuration configuration, + final ResourceInfo resourceInfo, final Sse sse) { + return ContextInjectionDescriptor.of(application, configuration, httpHeaders, providers, + request, resourceContext, resourceInfo, securityContext, sse, uriInfo); + } + + @GET + @Path("/application/{propertyName}") + @Produces(MediaType.TEXT_PLAIN) + public Response application(@PathParam("propertyName") final String propertyName) { + final var properties = application.getProperties(); + if (properties.containsKey(propertyName)) { + return Response.ok(properties.get(propertyName)).build(); + } + return Response.status(Response.Status.BAD_REQUEST) + .entity("Failed to find property %s in %s".formatted(propertyName, properties)) + .build(); + } + + @GET + @Path("/configuration") + @Produces(MediaType.TEXT_PLAIN) + public Response configuration() { + if (configuration == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The configuration field is null.").build(); + } + return Response.ok(configuration.getRuntimeType().toString()).build(); + } + + @GET + @Path("/httpHeaders/{name}") + @Produces(MediaType.TEXT_PLAIN) + public Response httpHeaders(@PathParam("name") final String name) { + if (httpHeaders == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The httpHeaders field is null.").build(); + } + return Response.ok(httpHeaders.getHeaderString(name)).build(); + } + + @GET + @Path("/providers") + @Produces(MediaType.APPLICATION_JSON) + public Response providers() { + final var mbw = providers.getMessageBodyWriter(StringBean.class, null, null, MediaType.WILDCARD_TYPE); + if (!(mbw instanceof StringBeanEntityProviderWithInjectables instance)) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Failed to find MessageBodyWriter for StringBean. This should be %s, but was %s" + .formatted(StringBeanEntityProviderWithInjectables.class, mbw)) + .build(); + } + return Response.ok(instance.describe()).build(); + } + + @GET + @Path("/request") + @Produces(MediaType.TEXT_PLAIN) + public Response request() { + if (request == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The request field is null.").build(); + } + return Response.ok(request.getMethod()).build(); + } + + @GET + @Path("resourceContext") + @Produces(MediaType.TEXT_PLAIN) + public Response resourceContext() { + if (resourceContext == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The resourceContext field is null.").build(); + } + return Response.ok(resourceContext.getResource(getClass()).getClass().getCanonicalName()).build(); + } + + @GET + @Path("resourceInfo") + @Produces(MediaType.TEXT_PLAIN) + public Response resourceInfo() { + if (resourceInfo == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The resourceInfo field is null.").build(); + } + return Response.ok(resourceInfo.getResourceMethod().getName()).build(); + } + + @GET + @Path("/securityContext") + @Produces(MediaType.TEXT_PLAIN) + public Response securityContext() { + if (securityContext == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The securityContext field is null.").build(); + } + return Response.ok(securityContext.isSecure()).build(); + } + + @GET + @Path("/sse") + @Produces(MediaType.SERVER_SENT_EVENTS) + public CompletionStage<?> sse(final SseEventSink eventSink) throws IOException { + if (eventSink == null) { + return CompletableFuture.failedFuture(new WebApplicationException("The eventSink parameter is null")); + } + if (sse == null) { + return CompletableFuture.failedFuture(new WebApplicationException("The sse field is null")); + } + return eventSink.send(sse.newEvent("test")) + .whenComplete((BiConsumer<Object, Throwable>) (unused, throwable) -> { + try { + eventSink.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + @GET + @Path("/async") + @Produces(MediaType.TEXT_PLAIN) + public void async(final AsyncResponse asyncResponse) { + if (asyncResponse == null) { + throw new WebApplicationException("The asyncResponse parameter is null"); + } + asyncResponse.resume("async-test"); + } + + @GET + @Path("/uriInfo") + @Produces(MediaType.TEXT_PLAIN) + public Response uriInfo() { + if (uriInfo == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("The uriInfo field is null.").build(); + } + return Response.ok(uriInfo.getPath()).build(); + } + +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/StringBeanEntityProviderWithInjectables.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/StringBeanEntityProviderWithInjectables.java new file mode 100644 index 0000000..7ab9de0 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/StringBeanEntityProviderWithInjectables.java
@@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi; + +import ee.jakarta.tck.ws.rs.common.provider.StringBeanEntityProvider; +import jakarta.inject.Inject; +import jakarta.ws.rs.container.ResourceContext; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.Provider; +import jakarta.ws.rs.ext.Providers; + +@Provider +public class StringBeanEntityProviderWithInjectables extends StringBeanEntityProvider { + + @Inject + Application application; + + @Inject + UriInfo uriInfo; + + @Inject + Request request; + + @Inject + HttpHeaders httpHeaders; + + @Inject + SecurityContext securityContext; + + @Inject + Providers providers; + + @Inject + ResourceContext resourceContext; + + @Inject + Configuration configuration; + + public ContextInjectionDescriptor describe() { + return ContextInjectionDescriptor.of(application, configuration, httpHeaders, providers, + request, resourceContext, null, securityContext, null, uriInfo); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/AbstractParamIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/AbstractParamIT.java new file mode 100644 index 0000000..c543299 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/AbstractParamIT.java
@@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.net.URI; + +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Form; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@Tag("cdi") +abstract class AbstractParamIT { + + private final String contextPath; + + @ArquillianResource + protected URI baseUri; + + AbstractParamIT(final String contextPath) { + this.contextPath = contextPath; + } + + static WebArchive defaultDeployment(final Class<? extends AbstractParamIT> testClass) { + return ShrinkWrap.create(WebArchive.class, testClass.getSimpleName() + ".war") + .addClasses(ParamApplication.class, ParamDescriptor.class, ParamResource.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + @Test + void constructorParameters() { + final ParamDescriptor param = invokeRequest("constructor"); + // Check the parameters + Assertions.assertEquals("cookieParamValue", param.getCookieParam(), + () -> "@CookieParam parameter was not set on the constructor: %s".formatted(param)); + Assertions.assertEquals("formParamValue", param.getFormParam(), + () -> "@FormParam parameter was not set on the constructor: %s".formatted(param)); + Assertions.assertEquals("headerParamValue", param.getHeaderParam(), + () -> "@HeaderParam parameter was not set on the constructor: %s".formatted(param)); + Assertions.assertEquals(10, param.getMatrixParam(), + () -> "@MatrixParam parameter was not set on the constructor: %s".formatted(param)); + Assertions.assertEquals("constructorParameters", param.getPathParam(), + () -> "@PathParam parameter was not set on the constructor: %s".formatted(param)); + Assertions.assertEquals(100, param.getQueryParam(), + () -> "@QueryParam parameter was not set on the constructor: %s".formatted(param)); + } + + @Test + void fieldParameters() { + final ParamDescriptor param = invokeRequest("field/fieldParamValue"); + // Check the parameters + Assertions.assertEquals("fieldCookieParamValue", param.getCookieParam(), + () -> "@CookieParam parameter was not set on the field: %s".formatted(param)); + Assertions.assertEquals("fieldFormParamValue", param.getFormParam(), + () -> "@FormParam parameter was not set on the field: %s".formatted(param)); + Assertions.assertEquals("fieldHeaderParamValue", param.getHeaderParam(), + () -> "@HeaderParam parameter was not set on the field: %s".formatted(param)); + Assertions.assertEquals(20, param.getMatrixParam(), + () -> "@MatrixParam parameter was not set on the field: %s".formatted(param)); + Assertions.assertEquals("fieldParamValue", param.getPathParam(), + () -> "@PathParam parameter was not set on the field: %s".formatted(param)); + Assertions.assertEquals(200, param.getQueryParam(), + () -> "@QueryParam parameter was not set on the field: %s".formatted(param)); + } + + @Test + void methodParameters() { + final ParamDescriptor param = invokeRequest("method/methodParamValue"); + // Check the parameters + Assertions.assertEquals("methodCookieParamValue", param.getCookieParam(), + () -> "@CookieParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodFormParamValue", param.getFormParam(), + () -> "@FormParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodHeaderParamValue", param.getHeaderParam(), + () -> "@HeaderParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals(30, param.getMatrixParam(), + () -> "@MatrixParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodParamValue", param.getPathParam(), + () -> "@PathParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals(300, param.getQueryParam(), + () -> "@QueryParam parameter was not set on the method: %s".formatted(param)); + } + + @Test + void methodParametersAnnotated() { + final ParamDescriptor param = invokeRequest("method-param/methodParamValuePa"); + // Check the parameters + Assertions.assertEquals("methodCookieParamValuePa", param.getCookieParam(), + () -> "@CookieParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodFormParamValuePa", param.getFormParam(), + () -> "@FormParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodHeaderParamValuePa", param.getHeaderParam(), + () -> "@HeaderParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals(40, param.getMatrixParam(), + () -> "@MatrixParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals("methodParamValuePa", param.getPathParam(), + () -> "@PathParam parameter was not set on the method: %s".formatted(param)); + Assertions.assertEquals(400, param.getQueryParam(), + () -> "@QueryParam parameter was not set on the method: %s".formatted(param)); + } + + private ParamDescriptor invokeRequest(final String path) { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path(contextPath + "/constructorParameters/" + path) + .matrixParam("matrixParam", 10) + .matrixParam("fieldMatrixParam", 20) + .matrixParam("methodMatrixParam", 30) + .matrixParam("methodMatrixParamPa", 40) + .queryParam("queryParam", 100) + .queryParam("fieldQueryParam", 200) + .queryParam("methodQueryParam", 300) + .queryParam("methodQueryParamPa", 400); + final Form form = new Form() + .param("formParam", "formParamValue") + .param("fieldFormParam", "fieldFormParamValue") + .param("methodFormParam", "methodFormParamValue") + .param("methodFormParamPa", "methodFormParamValuePa"); + final WebTarget target = client.target(uriBuilder); + final ParamDescriptor param = target.request(MediaType.APPLICATION_JSON_TYPE) + .cookie("cookieParam", "cookieParamValue") + .cookie("fieldCookieParam", "fieldCookieParamValue") + .cookie("methodCookieParam", "methodCookieParamValue") + .cookie("methodCookieParamPa", "methodCookieParamValuePa") + .header("headerParam", "headerParamValue") + .header("fieldHeaderParam", "fieldHeaderParamValue") + .header("methodHeaderParam", "methodHeaderParamValue") + .header("methodHeaderParamPa", "methodHeaderParamValuePa") + .post(Entity.form(form), ParamDescriptor.class); + Assertions.assertNotNull(param, () -> "Failed to find the parameters with URI %s".formatted(uriBuilder.build())); + return param; + } + } + +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamIT.java new file mode 100644 index 0000000..3998cac --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamIT.java
@@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.net.URI; + +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests that a {@link jakarta.ws.rs.BeanParam @BeanParam} works with CDI. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ArquillianTest +@Tag("cdi") +class CdiBeanParamIT { + + @ArquillianResource + private URI baseUri; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, CdiBeanParamIT.class.getSimpleName() + ".war") + .addClasses(ParamApplication.class, CdiBeanParamResource.class, SearchParams.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + @Test + void beanParam() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("bean-param") + .queryParam("q", "testValue") + .queryParam("limit", 25); + final SearchParams result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .header("Accept-Language", "en-US") + .get(SearchParams.class); + Assertions.assertNotNull(result); + Assertions.assertEquals("testValue", result.getQuery(), + () -> "Expected a result of 'testValue' in the query parameter: %s".formatted(result)); + Assertions.assertEquals(25, result.getLimit(), + () -> "Expected a result of '25' in the limit parameter: %s".formatted(result)); + Assertions.assertEquals("en-US", result.getLanguage(), + () -> "Expected a result of 'en-US' in the Accepted-Language parameter: %s".formatted(result)); + } + } + + @Test + void beanParamDefaults() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("bean-param") + .queryParam("q", "test"); + final SearchParams result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .get(SearchParams.class); + Assertions.assertNotNull(result); + Assertions.assertEquals("test", result.getQuery(), + () -> "Expected a result of 'test' in the query parameter: %s".formatted(result)); + Assertions.assertEquals(10, result.getLimit(), + () -> "Expected a result of '10', @DefaultValue, in the limit parameter: %s".formatted(result)); + Assertions.assertNull(result.getLanguage(), () -> "Accept-Language header should be null: %s".formatted(result)); + } + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamResource.java new file mode 100644 index 0000000..a4fe70f --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiBeanParamResource.java
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/bean-param") +@RequestScoped +public class CdiBeanParamResource { + + @Inject + @BeanParam + private SearchParams params; + + CdiBeanParamResource() { + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public SearchParams get() { + return params; + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedIT.java new file mode 100644 index 0000000..21af788 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedIT.java
@@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.net.URI; + +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests that CDI injected parameters work with {@link jakarta.ws.rs.Encoded @Encoded} + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ArquillianTest +@Tag("cdi") +class CdiParamEncodedIT { + + @ArquillianResource + private URI baseUri; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, CdiParamEncodedIT.class.getSimpleName() + ".war") + .addClasses(ParamApplication.class, CdiParamEncodedResource.class, + CdiParamNotEncodedResource.class, EncodedDescriptor.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + @Test + void classLevelEncoded() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("encoded/hello%20world") + .queryParam("q", "hello%20world"); + final EncodedDescriptor result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .get(EncodedDescriptor.class); + Assertions.assertNotNull(result); + Assertions.assertTrue(result.getQueryValue().contains("%20"), + () -> "Class-level @Encoded query should retain %%20, got: %s".formatted(result.getQueryValue())); + Assertions.assertTrue(result.getPathValue().contains("%20"), + () -> "Class-level @Encoded path should retain %%20, got: %s".formatted(result.getPathValue())); + } + } + + @Test + void fieldLevelEncoded() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("not-encoded/hello%20world") + .queryParam("q", "hello%20world") + .queryParam("q2", "hello%20world"); + final EncodedDescriptor result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .get(EncodedDescriptor.class); + Assertions.assertNotNull(result); + Assertions.assertTrue(result.getQueryValue().contains("%20"), + () -> "Field-level @Encoded query should retain %%20, got: %s".formatted(result.getQueryValue())); + Assertions.assertEquals("hello world", result.getDecodedQuery(), + () -> "non-encoded query should decode %%20 to space: %s".formatted(result)); + Assertions.assertEquals("hello world", result.getPathValue(), + () -> "non-encoded path should decode %%20 to space: %s".formatted(result)); + } + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedResource.java new file mode 100644 index 0000000..64d1102 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamEncodedResource.java
@@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Encoded; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +@Path("/encoded/{p}") +@RequestScoped +@Encoded +public class CdiParamEncodedResource { + + @Inject + @QueryParam("q") + private String queryValue; + + @Inject + @PathParam("p") + private String pathValue; + + CdiParamEncodedResource() { + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public EncodedDescriptor get() { + return EncodedDescriptor.of() + .setQueryValue(queryValue) + .setPathValue(pathValue); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamIT.java new file mode 100644 index 0000000..1728b40 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamIT.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.shrinkwrap.api.spec.WebArchive; + +/** + * Test that parameter extraction annotation annotated fields, constructor parameters and method parameters are injected. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ArquillianTest +class CdiParamIT extends AbstractParamIT { + + CdiParamIT() { + super("params"); + } + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return defaultDeployment(CdiParamIT.class).addClass(CdiParamResource.class); + } + +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedIT.java new file mode 100644 index 0000000..bc9ca14 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedIT.java
@@ -0,0 +1,75 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.net.URI; + +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests that CDI injection works on a constructor with both parameter extraction annotations and standard CDI beans. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ArquillianTest +@Tag("cdi") +class CdiParamMixedIT { + + @ArquillianResource + private URI baseUri; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, CdiParamMixedIT.class.getSimpleName() + ".war") + .addClasses(ParamApplication.class, CdiParamMixedResource.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + @Test + void mixedConstructor() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("mixed") + .queryParam("q", "testValue"); + final JsonObject result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .header("Custom", "custom-value") + .get(JsonObject.class); + Assertions.assertNotNull(result); + Assertions.assertEquals("testValue", result.getString("query"), + () -> "@QueryParam should be injected in mixed constructor: %s".formatted(result)); + Assertions.assertFalse(result.getString("path").isEmpty(), + () -> "UriInfo should be injected in mixed constructor: %s".formatted(result)); + Assertions.assertEquals("custom-value", result.getString("customHeader"), + () -> "@HeaderParam should be injected in mixed constructor: %s".formatted(result)); + } + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedResource.java new file mode 100644 index 0000000..5aa85bd --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamMixedResource.java
@@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriInfo; + +@Path("/mixed") +@RequestScoped +public class CdiParamMixedResource { + + private final String query; + private final UriInfo uriInfo; + private final String customHeader; + + CdiParamMixedResource() { + this.query = null; + this.uriInfo = null; + this.customHeader = null; + } + + @Inject + public CdiParamMixedResource(@QueryParam("q") final String query, + final UriInfo uriInfo, + @HeaderParam("Custom") final String customHeader) { + this.query = query; + this.uriInfo = uriInfo; + this.customHeader = customHeader; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject get() { + return Json.createObjectBuilder() + .add("query", query != null ? query : "") + .add("path", uriInfo != null ? uriInfo.getPath() : "") + .add("customHeader", customHeader != null ? customHeader : "") + .build(); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamNotEncodedResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamNotEncodedResource.java new file mode 100644 index 0000000..49262b0 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamNotEncodedResource.java
@@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Encoded; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +@Path("/not-encoded/{p}") +@RequestScoped +public class CdiParamNotEncodedResource { + + @Inject + @Encoded + @QueryParam("q") + private String encodedQuery; + + @Inject + @QueryParam("q2") + private String decodedQuery; + + @Inject + @PathParam("p") + private String pathValue; + + CdiParamNotEncodedResource() { + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public EncodedDescriptor get() { + return EncodedDescriptor.of() + .setQueryValue(encodedQuery) + .setDecodedQuery(decodedQuery) + .setPathValue(pathValue); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamResource.java new file mode 100644 index 0000000..f3c1bd9 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamResource.java
@@ -0,0 +1,211 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.io.InputStream; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.MatrixParam; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@Path("/params/{pathParam}") +@RequestScoped +public class CdiParamResource implements ParamResource { + private final String cookieParam; + private final String formParam; + private final String headerParam; + private final long matrixParam; + private final String pathParam; + private final int queryParam; + + @Inject + @CookieParam("fieldCookieParam") + private String fieldCookieParam; + @Inject + @FormParam("fieldFormParam") + private String fieldFormParam; + @Inject + @HeaderParam("fieldHeaderParam") + private String fieldHeaderParam; + @Inject + @MatrixParam("fieldMatrixParam") + private int fieldMatrixParam; + @Inject + @PathParam("fieldPathParam") + private String fieldPathParam; + @Inject + @QueryParam("fieldQueryParam") + private int fieldQueryParam; + + private String methodCookieParam; + private String methodFormParam; + private String methodHeaderParam; + private int methodMatrixParam; + private String methodPathParam; + private int methodQueryParam; + + private String methodCookieParamPa; + private String methodFormParamPa; + private String methodHeaderParamPa; + private int methodMatrixParamPa; + private String methodPathParamPa; + private int methodQueryParamPa; + + CdiParamResource() { + cookieParam = null; + formParam = null; + headerParam = null; + matrixParam = -1; + pathParam = null; + queryParam = -1; + } + + @Inject + public CdiParamResource(@CookieParam("cookieParam") final String cookieParam, + @FormParam("formParam") final String formParam, + @HeaderParam("headerParam") final String headerParam, + @MatrixParam("matrixParam") final long matrixParam, + @PathParam("pathParam") final String pathParam, + @QueryParam("queryParam") final int queryParam) { + this.cookieParam = cookieParam; + this.formParam = formParam; + this.headerParam = headerParam; + this.matrixParam = matrixParam; + this.pathParam = pathParam; + this.queryParam = queryParam; + } + + @Override + public ParamDescriptor constructor(final InputStream body) { + return ParamDescriptor.of() + .setCookieParam(cookieParam) + .setFormParam(formParam) + .setHeaderParam(headerParam) + .setMatrixParam(matrixParam) + .setPathParam(pathParam) + .setQueryParam(queryParam); + } + + @Override + public ParamDescriptor field(final InputStream body) { + return ParamDescriptor.of() + .setCookieParam(fieldCookieParam) + .setFormParam(fieldFormParam) + .setHeaderParam(fieldHeaderParam) + .setMatrixParam(fieldMatrixParam) + .setPathParam(fieldPathParam) + .setQueryParam(fieldQueryParam); + } + + @Override + public ParamDescriptor method(final InputStream body) { + return ParamDescriptor.of() + .setCookieParam(methodCookieParam) + .setFormParam(methodFormParam) + .setHeaderParam(methodHeaderParam) + .setMatrixParam(methodMatrixParam) + .setPathParam(methodPathParam) + .setQueryParam(methodQueryParam); + } + + @Override + public ParamDescriptor methodParamsAnnotated(final InputStream body) { + return ParamDescriptor.of() + .setCookieParam(methodCookieParamPa) + .setFormParam(methodFormParamPa) + .setHeaderParam(methodHeaderParamPa) + .setMatrixParam(methodMatrixParamPa) + .setPathParam(methodPathParamPa) + .setQueryParam(methodQueryParamPa); + } + + @Inject + @CookieParam("methodCookieParam") + public void setCookieParam(final String methodCookieParam) { + this.methodCookieParam = methodCookieParam; + } + + @Inject + @FormParam("methodFormParam") + public void setFormParam(final String methodFormParam) { + this.methodFormParam = methodFormParam; + } + + @Inject + @HeaderParam("methodHeaderParam") + public void setHeaderParam(final String methodHeaderParam) { + this.methodHeaderParam = methodHeaderParam; + } + + @Inject + @MatrixParam("methodMatrixParam") + public void setMatrixParam(final int methodMatrixParam) { + this.methodMatrixParam = methodMatrixParam; + } + + @Inject + @PathParam("methodPathParam") + public void setPathParam(final String methodPathParam) { + this.methodPathParam = methodPathParam; + } + + @Inject + @QueryParam("methodQueryParam") + public void setQueryParam(final int methodQueryParam) { + this.methodQueryParam = methodQueryParam; + } + + @Inject + public void setCookieParamPa(@CookieParam("methodCookieParamPa") final String cookieParam) { + this.methodCookieParamPa = cookieParam; + } + + @Inject + public void setFormParamPa(@FormParam("methodFormParamPa") final String formParam) { + this.methodFormParamPa = formParam; + } + + @Inject + public void setHeaderParamPa(@HeaderParam("methodHeaderParamPa") final String headerParam) { + this.methodHeaderParamPa = headerParam; + } + + @Inject + public void setMatrixParamPa(@MatrixParam("methodMatrixParamPa") final int matrixParam) { + this.methodMatrixParamPa = matrixParam; + } + + @Inject + public void setPathParamPa(@PathParam("methodPathParamPa") final String pathParam) { + this.methodPathParamPa = pathParam; + } + + @Inject + public void setQueryParamPa(@QueryParam("methodQueryParamPa") final int queryParam) { + this.methodQueryParamPa = queryParam; + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesIT.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesIT.java new file mode 100644 index 0000000..1fa5961 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesIT.java
@@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.net.URI; +import java.util.List; +import java.util.Set; + +import ee.jakarta.tck.ws.rs.common.util.CdiSupport; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests various parameter extraction annotation injections work with various required types. + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ArquillianTest +@Tag("cdi") +class CdiParamTypesIT { + + @ArquillianResource + private URI baseUri; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, CdiParamTypesIT.class.getSimpleName() + ".war") + .addClasses(ParamApplication.class, CdiParamTypesResource.class, + TypesDescriptor.class, ParamEnum.class) + .addAsWebInfResource(CdiSupport.BEANS_XML, "beans.xml"); + } + + @Test + void allTypes() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("types/path1/path2") + .queryParam("list", "a", "b", "c") + .queryParam("set", "x", "y", "z") + .queryParam("sortedSet", "c", "a", "b") + .queryParam("array", "one", "two") + .queryParam("wrapper", 99) + .queryParam("bool", true) + .queryParam("enum", "VALUE_ONE") + .queryParam("default", "explicit") + .queryParam("defaultInt", 7); + final TypesDescriptor result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .get(TypesDescriptor.class); + Assertions.assertNotNull(result); + + // Collection types + Assertions.assertNotNull(result.getListParam(), () -> "List should not be null: %s".formatted(result)); + Assertions.assertEquals(3, result.getListParam().size(), + () -> "Expected list size of 3: %s".formatted(result)); + Assertions.assertTrue(result.getListParam().containsAll(List.of("a", "b", "c")), + () -> "Expected list to contain [a, b, c]: %s".formatted(result)); + + Assertions.assertNotNull(result.getSetParam(), + () -> "Set should not be null: %s".formatted(result)); + Assertions.assertEquals(3, result.getSetParam().size(), + () -> "Expected set size of 3: %s".formatted(result)); + Assertions.assertTrue(result.getSetParam().containsAll(Set.of("x", "y", "z")), + () -> "Expected set to contain [x, y, z]: %s".formatted(result)); + + Assertions.assertNotNull(result.getSortedSetParam(), + () -> "SortedSet should not be null: %s".formatted(result)); + Assertions.assertEquals(3, result.getSortedSetParam().size(), + () -> "Expected sortedSet size of 3: %s".formatted(result)); + Assertions.assertEquals("a", result.getSortedSetParam().first(), + () -> "SortedSet should be sorted, expected 'a' first: %s".formatted(result)); + + // Array + Assertions.assertNotNull(result.getArrayParam(), + () -> "Array should not be null: %s".formatted(result)); + Assertions.assertArrayEquals(new String[] {"one", "two"}, result.getArrayParam(), + () -> "Expected array [one, two]: %s".formatted(result)); + + // Wrapper and primitive types + Assertions.assertEquals(99, result.getWrapperParam(), + () -> "Expected wrapper value of 99: %s".formatted(result)); + Assertions.assertEquals(true, result.getBooleanParam(), + () -> "Expected boolean value of true: %s".formatted(result)); + + // Enum + Assertions.assertEquals(ParamEnum.VALUE_ONE, result.getEnumParam(), + () -> "Expected enum VALUE_ONE: %s".formatted(result)); + + // Explicit values override @DefaultValue + Assertions.assertEquals("explicit", result.getDefaultParam(), + () -> "Expected default string to be 'explicit': %s".formatted(result)); + Assertions.assertEquals(7, result.getDefaultIntParam(), + () -> "Expected default int to be 7: %s".formatted(result)); + + // PathSegment + final List<String> listPaths = result.getListPaths(); + Assertions.assertEquals(2, listPaths.size(), () -> "Expected two PathSegments in %s".formatted(result)); + Assertions.assertEquals("path1", listPaths.get(0), + () -> "Expected the first PathSegment to be path1 in %s".formatted(result)); + Assertions.assertEquals("path2", listPaths.get(1), + () -> "Expected the first PathSegment to be path2 in %s".formatted(result)); + Assertions.assertEquals("path2", result.getSinglePath(), + () -> "Expected the single PathSegment to be path2 in %s".formatted(result)); + } + } + + @Test + void defaultValues() { + try (Client client = ClientBuilder.newClient()) { + final UriBuilder uriBuilder = UriBuilder.fromUri(baseUri) + .path("types/ignored"); + final TypesDescriptor result = client.target(uriBuilder) + .request(MediaType.APPLICATION_JSON_TYPE) + .get(TypesDescriptor.class); + Assertions.assertNotNull(result); + + // @DefaultValue should apply when params are absent + Assertions.assertEquals("fallback", result.getDefaultParam(), + () -> "Expected default string 'fallback' when param is absent: %s".formatted(result)); + Assertions.assertEquals(42, result.getDefaultIntParam(), + () -> "Expected default int 42 when param is absent: %s".formatted(result)); + + // Absent optional params without @DefaultValue + Assertions.assertNull(result.getWrapperParam(), + () -> "Absent wrapper should be null: %s".formatted(result)); + Assertions.assertNull(result.getBooleanParam(), + () -> "Absent boolean should be null: %s".formatted(result)); + Assertions.assertNull(result.getEnumParam(), + () -> "Absent enum should be null: %s".formatted(result)); + } + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesResource.java new file mode 100644 index 0000000..7f022f2 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/CdiParamTypesResource.java
@@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.util.List; +import java.util.Set; +import java.util.SortedSet; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.PathSegment; + +@Path("/types/{paths:.+}") +@RequestScoped +public class CdiParamTypesResource { + + @Inject + @QueryParam("list") + private List<String> listParam; + + @Inject + @QueryParam("set") + private Set<String> setParam; + + @Inject + @QueryParam("sortedSet") + private SortedSet<String> sortedSetParam; + + @Inject + @QueryParam("array") + private String[] arrayParam; + + @Inject + @QueryParam("wrapper") + private Integer wrapperParam; + + @Inject + @QueryParam("bool") + private Boolean booleanParam; + + @Inject + @QueryParam("enum") + private ParamEnum enumParam; + + @Inject + @QueryParam("default") + @DefaultValue("fallback") + private String defaultParam; + + @Inject + @QueryParam("defaultInt") + @DefaultValue("42") + private int defaultIntParam; + + @Inject + @PathParam("paths") + private List<PathSegment> listPaths; + + @Inject + @PathParam("paths") + private PathSegment singlePath; + + CdiParamTypesResource() { + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public TypesDescriptor get() { + return TypesDescriptor.of() + .setListParam(listParam) + .setSetParam(setParam) + .setSortedSetParam(sortedSetParam) + .setArrayParam(arrayParam) + .setWrapperParam(wrapperParam) + .setBooleanParam(booleanParam) + .setEnumParam(enumParam) + .setDefaultParam(defaultParam) + .setDefaultIntParam(defaultIntParam) + .setListPaths(listPaths.stream().map(PathSegment::getPath).toList()) + .setSinglePath(singlePath.getPath()); + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/EncodedDescriptor.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/EncodedDescriptor.java new file mode 100644 index 0000000..b24e122 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/EncodedDescriptor.java
@@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +public class EncodedDescriptor { + private String queryValue; + private String pathValue; + private String decodedQuery; + + public static EncodedDescriptor of() { + return new EncodedDescriptor(); + } + + public String getQueryValue() { + return queryValue; + } + + public EncodedDescriptor setQueryValue(final String queryValue) { + this.queryValue = queryValue; + return this; + } + + public String getPathValue() { + return pathValue; + } + + public EncodedDescriptor setPathValue(final String pathValue) { + this.pathValue = pathValue; + return this; + } + + public String getDecodedQuery() { + return decodedQuery; + } + + public EncodedDescriptor setDecodedQuery(final String decodedQuery) { + this.decodedQuery = decodedQuery; + return this; + } + + @Override + public String toString() { + return "EncodedDescriptor{" + "queryValue='" + queryValue + '\'' + + ", pathValue='" + pathValue + '\'' + + ", decodedQuery='" + decodedQuery + '\'' + + '}'; + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamApplication.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamApplication.java new file mode 100644 index 0000000..e6891d0 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamApplication.java
@@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@ApplicationPath("/") +public class ParamApplication extends Application { + +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamDescriptor.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamDescriptor.java new file mode 100644 index 0000000..2b88f12 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamDescriptor.java
@@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +public class ParamDescriptor { + private String cookieParam; + private String formParam; + private String headerParam; + private long matrixParam; + private String pathParam; + private int queryParam; + + public static ParamDescriptor of() { + return new ParamDescriptor(); + } + + public String getCookieParam() { + return cookieParam; + } + + public ParamDescriptor setCookieParam(final String cookieParam) { + this.cookieParam = cookieParam; + return this; + } + + public String getFormParam() { + return formParam; + } + + public ParamDescriptor setFormParam(final String formParam) { + this.formParam = formParam; + return this; + } + + public String getHeaderParam() { + return headerParam; + } + + public ParamDescriptor setHeaderParam(final String headerParam) { + this.headerParam = headerParam; + return this; + } + + public long getMatrixParam() { + return matrixParam; + } + + public ParamDescriptor setMatrixParam(final long matrixParam) { + this.matrixParam = matrixParam; + return this; + } + + public String getPathParam() { + return pathParam; + } + + public ParamDescriptor setPathParam(final String pathParam) { + this.pathParam = pathParam; + return this; + } + + public int getQueryParam() { + return queryParam; + } + + public ParamDescriptor setQueryParam(final int queryParam) { + this.queryParam = queryParam; + return this; + } + + @Override + public String toString() { + return "ParamDescriptor{" + "cookieParam='" + cookieParam + '\'' + + ", formParam='" + formParam + '\'' + + ", headerParam='" + headerParam + '\'' + + ", matrixParam=" + matrixParam + + ", pathParam='" + pathParam + '\'' + + ", queryParam=" + queryParam + + '}'; + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamEnum.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamEnum.java new file mode 100644 index 0000000..dba4258 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamEnum.java
@@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +public enum ParamEnum { + VALUE_ONE, + VALUE_TWO +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamResource.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamResource.java new file mode 100644 index 0000000..f029413 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/ParamResource.java
@@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.io.InputStream; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +/** + * + * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a> + */ +@Consumes(MediaType.APPLICATION_FORM_URLENCODED) +@Produces(MediaType.APPLICATION_JSON) +public interface ParamResource { + + @Path("constructor") + @POST + ParamDescriptor constructor(InputStream body); + + @Path("field/{fieldPathParam}") + @POST + ParamDescriptor field(InputStream body); + + @Path("method/{methodPathParam}") + @POST + ParamDescriptor method(InputStream body); + + @Path("method-param/{methodPathParamPa}") + @POST + ParamDescriptor methodParamsAnnotated(InputStream body); +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/SearchParams.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/SearchParams.java new file mode 100644 index 0000000..cbcddc1 --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/SearchParams.java
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.QueryParam; + +@RequestScoped +public class SearchParams { + + @Inject + @QueryParam("q") + private String query; + + @Inject + @QueryParam("limit") + @DefaultValue("10") + private int limit; + + @Inject + @HeaderParam("Accept-Language") + private String language; + + public String getQuery() { + return query; + } + + public void setQuery(final String query) { + this.query = query; + } + + public int getLimit() { + return limit; + } + + public void setLimit(final int limit) { + this.limit = limit; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(final String language) { + this.language = language; + } + + @Override + public String toString() { + return "SearchParams{" + "query='" + query + '\'' + + ", limit=" + limit + + ", language='" + language + '\'' + + '}'; + } +}
diff --git a/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/TypesDescriptor.java b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/TypesDescriptor.java new file mode 100644 index 0000000..4d2306b --- /dev/null +++ b/jaxrs-tck/src/main/java/ee/jakarta/tck/ws/rs/spec/context/cdi/param/TypesDescriptor.java
@@ -0,0 +1,155 @@ +/* + * Copyright (c) 2026 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 ee.jakarta.tck.ws.rs.spec.context.cdi.param; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; + +public class TypesDescriptor { + private List<String> listParam; + private Set<String> setParam; + private SortedSet<String> sortedSetParam; + private String[] arrayParam; + private Integer wrapperParam; + private Boolean booleanParam; + private ParamEnum enumParam; + private String defaultParam; + private int defaultIntParam; + private List<String> listPaths; + private String singlePath; + + public static TypesDescriptor of() { + return new TypesDescriptor(); + } + + public List<String> getListParam() { + return listParam; + } + + public TypesDescriptor setListParam(final List<String> listParam) { + this.listParam = listParam; + return this; + } + + public Set<String> getSetParam() { + return setParam; + } + + public TypesDescriptor setSetParam(final Set<String> setParam) { + this.setParam = setParam; + return this; + } + + public SortedSet<String> getSortedSetParam() { + return sortedSetParam; + } + + public TypesDescriptor setSortedSetParam(final SortedSet<String> sortedSetParam) { + this.sortedSetParam = sortedSetParam; + return this; + } + + public String[] getArrayParam() { + return arrayParam; + } + + public TypesDescriptor setArrayParam(final String[] arrayParam) { + this.arrayParam = arrayParam; + return this; + } + + public Integer getWrapperParam() { + return wrapperParam; + } + + public TypesDescriptor setWrapperParam(final Integer wrapperParam) { + this.wrapperParam = wrapperParam; + return this; + } + + public Boolean getBooleanParam() { + return booleanParam; + } + + public TypesDescriptor setBooleanParam(final Boolean booleanParam) { + this.booleanParam = booleanParam; + return this; + } + + public ParamEnum getEnumParam() { + return enumParam; + } + + public TypesDescriptor setEnumParam(final ParamEnum enumParam) { + this.enumParam = enumParam; + return this; + } + + public String getDefaultParam() { + return defaultParam; + } + + public TypesDescriptor setDefaultParam(final String defaultParam) { + this.defaultParam = defaultParam; + return this; + } + + public int getDefaultIntParam() { + return defaultIntParam; + } + + public TypesDescriptor setDefaultIntParam(final int defaultIntParam) { + this.defaultIntParam = defaultIntParam; + return this; + } + + public List<String> getListPaths() { + return listPaths; + } + + public TypesDescriptor setListPaths(final List<String> listPaths) { + this.listPaths = listPaths; + return this; + } + + public String getSinglePath() { + return singlePath; + } + + public TypesDescriptor setSinglePath(final String singlePath) { + this.singlePath = singlePath; + return this; + } + + @Override + public String toString() { + return "TypesDescriptor{" + "listParam=" + listParam + + ", setParam=" + setParam + + ", sortedSetParam=" + sortedSetParam + + ", arrayParam=" + Arrays.toString(arrayParam) + + ", wrapperParam=" + wrapperParam + + ", booleanParam=" + booleanParam + + ", enumParam=" + enumParam + + ", defaultParam='" + defaultParam + '\'' + + ", defaultIntParam=" + defaultIntParam + + ", listPaths=" + listPaths + + ", singlePath=" + singlePath + + '}'; + } +}