diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java index e003b25985c..a6140a5063d 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. + * Copyright (c) 2022, 2023 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -184,7 +184,14 @@ private static URI baseUri(ServerRequest req) { } private void handle(ServerRequest req, ServerResponse res) { - Contexts.runInContext(req.context(), () -> doHandle(req.context(), req, res)); + Context context = req.context(); + + // make these available in context for ServerCdiExtension + context.supply(ServerRequest.class, () -> req); + context.supply(ServerResponse.class, () -> res); + + // call doHandle in active context + Contexts.runInContext(context, () -> doHandle(context, req, res)); } private void doHandle(Context ctx, ServerRequest req, ServerResponse res) { diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 4aa7d73f1ef..3d9785e148b 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -43,14 +43,21 @@ import io.helidon.nima.webserver.context.ContextFeature; import io.helidon.nima.webserver.http.HttpRouting; import io.helidon.nima.webserver.http.HttpService; +import io.helidon.nima.webserver.http.ServerRequest; +import io.helidon.nima.webserver.http.ServerResponse; import io.helidon.nima.webserver.staticcontent.StaticContentService; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.BeforeDestroyed; import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.CDI; @@ -361,6 +368,27 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( STARTUP_LOGGER.log(Level.TRACE, "Server created"); } + /** + * Make Nima's {@code ServerRequest} and {@code ServerResponse} available for injection + * via CDI by registering them as beans. + * + * @param event after bean discovery event + */ + private void afterBeanDiscovery(@Observes AfterBeanDiscovery event) { + event.addBean() + .qualifiers(Set.of(Default.Literal.INSTANCE, Any.Literal.INSTANCE)) + .addTransitiveTypeClosure(ServerRequest.class) + .scope(RequestScoped.class) + .createWith(cc -> Contexts.context().flatMap(c -> c.get(ServerRequest.class)) + .orElseThrow(() -> new CreationException("Unable to retrieve ServerRequest from context"))); + event.addBean() + .qualifiers(Set.of(Default.Literal.INSTANCE, Any.Literal.INSTANCE)) + .addTransitiveTypeClosure(ServerResponse.class) + .scope(RequestScoped.class) + .createWith(cc -> Contexts.context().flatMap(c -> c.get(ServerResponse.class)) + .orElseThrow(() -> new CreationException("Unable to retrieve ServerResponse from context"))); + } + private void registerJaxRsApplications(BeanManager beanManager) { JaxRsCdiExtension jaxRs = beanManager.getExtension(JaxRsCdiExtension.class); diff --git a/tests/functional/pom.xml b/tests/functional/pom.xml index 2912264af02..e224578e232 100644 --- a/tests/functional/pom.xml +++ b/tests/functional/pom.xml @@ -42,6 +42,7 @@ mp-compression request-scope request-scope-cdi + request-scope-injection jax-rs-multiple-apps param-converter-provider config-profiles diff --git a/tests/functional/request-scope-injection/pom.xml b/tests/functional/request-scope-injection/pom.xml new file mode 100644 index 00000000000..154d98e8846 --- /dev/null +++ b/tests/functional/request-scope-injection/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + helidon-tests-functional-project + io.helidon.tests.functional + 4.0.0-SNAPSHOT + + + helidon-tests-functional-request-scope-injection + Helidon Functional Test: Helidon Request Scope Injection + + + + io.helidon.microprofile.bundles + helidon-microprofile-core + + + io.helidon.config + helidon-config-yaml + + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + org.junit.jupiter + junit-jupiter-params + test + + + diff --git a/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/CheckInjectionResource.java b/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/CheckInjectionResource.java new file mode 100644 index 00000000000..d85f9d3abb5 --- /dev/null +++ b/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/CheckInjectionResource.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.tests.functional.context.injection; + +import java.util.Objects; + +import io.helidon.nima.webserver.http.ServerRequest; +import io.helidon.nima.webserver.http.ServerResponse; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; + +/** + * Verifies that {@code ServerRequest} and {@code ServerResponse} are injectable + * both via {@code @Context} and {@code @Inject}. + */ +@Path("/check") +public class CheckInjectionResource { + + @Context + private ServerRequest serverRequest; + + @Context + private ServerResponse serverResponse; + + @Inject + private ServerRequest serverRequestCdi; + + @Inject + private ServerResponse serverResponseCdi; + + @GET + public Response checkInjection() { + Objects.requireNonNull(serverRequest); + Objects.requireNonNull(serverResponse); + Objects.requireNonNull(serverRequestCdi); + Objects.requireNonNull(serverResponseCdi); + if (!serverRequestCdi.path().equals(serverRequest.path()) + || !serverResponseCdi.status().equals(serverResponse.status())) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + return Response.ok().build(); + } +} diff --git a/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/package-info.java b/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/package-info.java new file mode 100644 index 00000000000..a597378ffd6 --- /dev/null +++ b/tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Simple app to verify injection of {@code ServerRequest} and {@code ServerResponse}. + */ +package io.helidon.tests.functional.context.injection; diff --git a/tests/functional/request-scope-injection/src/main/resources/META-INF/beans.xml b/tests/functional/request-scope-injection/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..cf179d28185 --- /dev/null +++ b/tests/functional/request-scope-injection/src/main/resources/META-INF/beans.xml @@ -0,0 +1,25 @@ + + + + diff --git a/tests/functional/request-scope-injection/src/test/java/io/helidon/tests/functional/context/injection/CheckInjectionTest.java b/tests/functional/request-scope-injection/src/test/java/io/helidon/tests/functional/context/injection/CheckInjectionTest.java new file mode 100644 index 00000000000..d29e2383545 --- /dev/null +++ b/tests/functional/request-scope-injection/src/test/java/io/helidon/tests/functional/context/injection/CheckInjectionTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.tests.functional.context.injection; + +import io.helidon.microprofile.tests.junit5.HelidonTest; +import jakarta.inject.Inject; +import jakarta.ws.rs.client.WebTarget; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Unit test for {@link CheckInjectionResource}. + */ +@HelidonTest +class CheckInjectionTest { + + private final WebTarget baseTarget; + + @Inject + CheckInjectionTest(WebTarget baseTarget) { + this.baseTarget = baseTarget; + } + + @Test + void testCheckInjection() { + WebTarget target = baseTarget.path("/check"); + assertThat(target.request().get().getStatus(), is(200)); + } +} \ No newline at end of file diff --git a/tests/functional/request-scope-injection/src/test/resources/logging.properties b/tests/functional/request-scope-injection/src/test/resources/logging.properties new file mode 100644 index 00000000000..2f5fd9c9515 --- /dev/null +++ b/tests/functional/request-scope-injection/src/test/resources/logging.properties @@ -0,0 +1,30 @@ +# +# Copyright (c) 2023 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.logging.jul.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Component specific log levels +AUDIT.level=FINEST