From 14e6655a2b626c85df6c3ce64aaf30134307c589 Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Wed, 10 May 2023 09:46:12 -0400 Subject: [PATCH] Support for injection of ServerRequest and ServerResponse also via CDI. New functional test to verify both types of injection. --- .../server/ServerCdiExtension.java | 28 +++++++++ tests/functional/pom.xml | 1 + .../request-scope-injection/pom.xml | 61 +++++++++++++++++++ .../injection/CheckInjectionResource.java | 60 ++++++++++++++++++ .../context/injection/package-info.java | 20 ++++++ .../src/main/resources/META-INF/beans.xml | 25 ++++++++ .../context/injection/CheckInjectionTest.java | 45 ++++++++++++++ .../src/test/resources/logging.properties | 30 +++++++++ .../webserver/jersey/JerseySupport.java | 6 +- 9 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tests/functional/request-scope-injection/pom.xml create mode 100644 tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/CheckInjectionResource.java create mode 100644 tests/functional/request-scope-injection/src/main/java/io/helidon/tests/functional/context/injection/package-info.java create mode 100644 tests/functional/request-scope-injection/src/main/resources/META-INF/beans.xml create mode 100644 tests/functional/request-scope-injection/src/test/java/io/helidon/tests/functional/context/injection/CheckInjectionTest.java create mode 100644 tests/functional/request-scope-injection/src/test/resources/logging.properties 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 23f4cfd0652..0d795ecaa13 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 @@ -48,6 +48,8 @@ import io.helidon.microprofile.cdi.RuntimeStart; import io.helidon.webserver.KeyPerformanceIndicatorSupport; import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; import io.helidon.webserver.Service; import io.helidon.webserver.WebServer; import io.helidon.webserver.jersey.JerseySupport; @@ -57,8 +59,13 @@ 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; @@ -224,6 +231,27 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( STARTUP_LOGGER.finest("Server created"); } + /** + * Make {@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 05beb975384..e99e121a105 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..38a6e515a02 --- /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 + 3.2.1-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..3b51372d142 --- /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.webserver.ServerRequest; +import io.helidon.webserver.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..03a4eb7b127 --- /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.common.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 diff --git a/webserver/jersey/src/main/java/io/helidon/webserver/jersey/JerseySupport.java b/webserver/jersey/src/main/java/io/helidon/webserver/jersey/JerseySupport.java index b3e6fe215ab..acadac84583 100644 --- a/webserver/jersey/src/main/java/io/helidon/webserver/jersey/JerseySupport.java +++ b/webserver/jersey/src/main/java/io/helidon/webserver/jersey/JerseySupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. + * Copyright (c) 2017, 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. @@ -275,6 +275,10 @@ public void accept(ServerRequest req, ServerResponse res) { .orElseThrow(() -> new IllegalStateException("Context must be propagated from server")); Context jerseyContext = Context.create(parent); + // make these available in context for ServerCdiExtension + jerseyContext.supply(ServerRequest.class, () -> req); + jerseyContext.supply(ServerResponse.class, () -> res); + Contexts.runInContext(jerseyContext, () -> doAccept(req, res)); }