diff --git a/docs/src/main/asciidoc/reactive-routes.adoc b/docs/src/main/asciidoc/reactive-routes.adoc index aa96d8d0c23b1..77124c09b2d39 100644 --- a/docs/src/main/asciidoc/reactive-routes.adoc +++ b/docs/src/main/asciidoc/reactive-routes.adoc @@ -609,6 +609,15 @@ Check the https://vertx.io/docs/vertx-web/java/#_basic_vert_x_web_concepts[Vert. If you use `quarkus-resteasy` or `quarkus-reactive-routes`, the extension will be added automatically. ==== +You can also receive the Mutiny variant of the Router (`io.vertx.mutiny.ext.web.Router`): + +[source,java] +---- +public void init(@Observes io.vertx.mutiny.ext.web.Router router) { + router.get("/my-route").handler(rc -> rc.response().endAndForget("Hello from my route")); +} +---- + == Intercepting HTTP requests You can also register filters that would intercept incoming HTTP requests. diff --git a/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java b/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java index 16d72b2bf45a0..c882d214faae7 100644 --- a/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java +++ b/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java @@ -4,7 +4,6 @@ import java.util.function.Function; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.vertx.http.runtime.RouterProducer; import io.vertx.core.Handler; import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Router; @@ -18,7 +17,7 @@ public Handler createHandler(String handlerClassName) { try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { - cl = RouterProducer.class.getClassLoader(); + cl = VertxWebRecorder.class.getClassLoader(); } Class> handlerClazz = (Class>) cl .loadClass(handlerClassName); diff --git a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java index 8561b96d185db..099e0ff32e5b2 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java @@ -5,7 +5,6 @@ import java.util.Optional; -import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; @@ -72,7 +71,6 @@ public void boot(ShutdownContextBuildItem shutdown, BuildProducer defaultRoutes, BuildProducer routes, CoreVertxBuildItem vertx, - BeanContainerBuildItem beanContainer, ResteasyStandaloneBuildItem standalone, Optional requireVirtual, ExecutorBuildItem executorBuildItem, @@ -86,7 +84,7 @@ public void boot(ShutdownContextBuildItem shutdown, // Handler used for both the default and non-default deployment path (specified as application path or resteasyConfig.path) // Routes use the order VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1 to ensure the default route is called before the resteasy one - Handler handler = recorder.vertxRequestHandler(vertx.getVertx(), beanContainer.getValue(), + Handler handler = recorder.vertxRequestHandler(vertx.getVertx(), executorBuildItem.getExecutorProxy(), httpConfiguration, resteasyVertxConfig); // Exact match for resources matched to the root path routes.produce( diff --git a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java index eba273dd2cf41..3217b421ec309 100644 --- a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java +++ b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java @@ -8,7 +8,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; -import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.resteasy.runtime.ResteasyVertxConfig; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @@ -52,10 +51,10 @@ public void run() { useDirect = !isVirtual; } - public Handler vertxRequestHandler(Supplier vertx, - BeanContainer beanContainer, Executor executor, HttpConfiguration readTimeout, ResteasyVertxConfig config) { + public Handler vertxRequestHandler(Supplier vertx, Executor executor, HttpConfiguration readTimeout, + ResteasyVertxConfig config) { if (deployment != null) { - return new VertxRequestHandler(vertx.get(), beanContainer, deployment, contextPath, + return new VertxRequestHandler(vertx.get(), deployment, contextPath, new ResteasyVertxAllocator(config.responseBufferSize), executor, readTimeout.readTimeout.toMillis()); } diff --git a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java index 1feb0aab5be6d..c38dc6e74d7dd 100644 --- a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java +++ b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java @@ -19,8 +19,8 @@ import org.jboss.resteasy.spi.Failure; import org.jboss.resteasy.spi.ResteasyDeployment; +import io.quarkus.arc.Arc; import io.quarkus.arc.ManagedContext; -import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.resteasy.runtime.ContextUtil; import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.security.identity.CurrentIdentityAssociation; @@ -45,19 +45,16 @@ public class VertxRequestHandler implements Handler { protected final RequestDispatcher dispatcher; protected final String rootPath; protected final BufferAllocator allocator; - protected final BeanContainer beanContainer; protected final CurrentIdentityAssociation association; protected final CurrentVertxRequest currentVertxRequest; protected final Executor executor; protected final long readTimeout; public VertxRequestHandler(Vertx vertx, - BeanContainer beanContainer, ResteasyDeployment deployment, String rootPath, BufferAllocator allocator, Executor executor, long readTimeout) { this.vertx = vertx; - this.beanContainer = beanContainer; this.dispatcher = new RequestDispatcher((SynchronousDispatcher) deployment.getDispatcher(), deployment.getProviderFactory(), null, Thread.currentThread().getContextClassLoader()); this.rootPath = rootPath; @@ -101,7 +98,7 @@ public void run() { } private void dispatch(RoutingContext routingContext, InputStream is, VertxOutput output) { - ManagedContext requestContext = beanContainer.requestContext(); + ManagedContext requestContext = Arc.container().requestContext(); requestContext.activate(); routingContext.remove(QuarkusHttpUser.AUTH_FAILURE_HANDLER); if (association != null) { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/InitialRouterBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/InitialRouterBuildItem.java new file mode 100644 index 0000000000000..c0b50a1cc4b52 --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/InitialRouterBuildItem.java @@ -0,0 +1,25 @@ +package io.quarkus.vertx.http.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; +import io.vertx.ext.web.Router; + +final class InitialRouterBuildItem extends SimpleBuildItem { + + private final RuntimeValue httpRouter; + private final RuntimeValue mutinyRouter; + + public InitialRouterBuildItem(RuntimeValue httpRouter, RuntimeValue mutinyRouter) { + this.httpRouter = httpRouter; + this.mutinyRouter = mutinyRouter; + } + + public RuntimeValue getHttpRouter() { + return httpRouter; + } + + public RuntimeValue getMutinyRouter() { + return mutinyRouter; + } + +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 08f032956fb34..53ed43ed0beca 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -20,6 +20,8 @@ import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.bootstrap.util.ZipUtils; import io.quarkus.builder.BuildException; import io.quarkus.deployment.annotations.BuildProducer; @@ -53,7 +55,6 @@ import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.http.runtime.HttpHostConfigSource; -import io.quarkus.vertx.http.runtime.RouterProducer; import io.quarkus.vertx.http.runtime.VertxHttpRecorder; import io.quarkus.vertx.http.runtime.attribute.ExchangeAttributeBuilder; import io.quarkus.vertx.http.runtime.cors.CORSRecorder; @@ -96,7 +97,6 @@ FilterBuildItem cors(CORSRecorder recorder, HttpConfiguration configuration) { AdditionalBeanBuildItem additionalBeans() { return AdditionalBeanBuildItem.builder() .setUnremovable() - .addBeanClass(RouterProducer.class) .addBeanClass(CurrentVertxRequest.class) .addBeanClass(CurrentRequestProducer.class) .build(); @@ -132,16 +132,38 @@ void notFoundRoutes( } } + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void preinitializeRouter(CoreVertxBuildItem vertx, VertxHttpRecorder recorder, + BuildProducer initialRouter, BuildProducer syntheticBeans) { + // We need to initialize the routers that are exposed as synthetic beans in a separate build step to avoid cycles in the build chain + RuntimeValue httpRouteRouter = recorder.initializeRouter(vertx.getVertx()); + RuntimeValue mutinyRouter = recorder.createMutinyRouter(httpRouteRouter); + initialRouter.produce(new InitialRouterBuildItem(httpRouteRouter, mutinyRouter)); + + // Also note that we need a client proxy to handle the use case where a bean also @Observes Router + syntheticBeans.produce(SyntheticBeanBuildItem.configure(Router.class) + .scope(BuiltinScope.APPLICATION.getInfo()) + .setRuntimeInit() + .runtimeValue(httpRouteRouter).done()); + syntheticBeans.produce(SyntheticBeanBuildItem.configure(io.vertx.mutiny.ext.web.Router.class) + .scope(BuiltinScope.APPLICATION.getInfo()) + .setRuntimeInit() + .runtimeValue(mutinyRouter).done()); + } + @BuildStep @Record(ExecutionTime.RUNTIME_INIT) VertxWebRouterBuildItem initializeRouter(VertxHttpRecorder recorder, + InitialRouterBuildItem initialRouter, CoreVertxBuildItem vertx, List routes, HttpBuildTimeConfig httpBuildTimeConfig, NonApplicationRootPathBuildItem nonApplicationRootPath, ShutdownContextBuildItem shutdown) { - RuntimeValue httpRouteRouter = recorder.initializeRouter(vertx.getVertx()); + RuntimeValue httpRouteRouter = initialRouter.getHttpRouter(); + RuntimeValue mutinyRouter = initialRouter.getMutinyRouter(); RuntimeValue frameworkRouter = null; RuntimeValue mainRouter = null; @@ -182,7 +204,7 @@ VertxWebRouterBuildItem initializeRouter(VertxHttpRecorder recorder, } } - return new VertxWebRouterBuildItem(httpRouteRouter, mainRouter, frameworkRouter); + return new VertxWebRouterBuildItem(httpRouteRouter, mainRouter, frameworkRouter, mutinyRouter); } @BuildStep @@ -258,6 +280,7 @@ ServiceStartBuildItem finalizeRouter( recorder.finalizeRouter(beanContainer.getValue(), defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null), listOfFilters, vertx.getVertx(), lrc, mainRouter, httpRouteRouter.getHttpRouter(), + httpRouteRouter.getMutinyRouter(), httpRootPathBuildItem.getRootPath(), launchMode.getLaunchMode(), !requireBodyHandlerBuildItems.isEmpty(), bodyHandler, httpConfiguration, gracefulShutdownFilter, diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxWebRouterBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxWebRouterBuildItem.java index b3c9a7a064564..391a48592fdee 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxWebRouterBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxWebRouterBuildItem.java @@ -6,21 +6,27 @@ public final class VertxWebRouterBuildItem extends SimpleBuildItem { - private RuntimeValue httpRouter; - private RuntimeValue mainRouter; - private RuntimeValue frameworkRouter; + private final RuntimeValue httpRouter; + private final RuntimeValue mainRouter; + private final RuntimeValue frameworkRouter; + private final RuntimeValue mutinyRouter; VertxWebRouterBuildItem(RuntimeValue httpRouter, RuntimeValue mainRouter, - RuntimeValue frameworkRouter) { + RuntimeValue frameworkRouter, RuntimeValue mutinyRouter) { this.httpRouter = httpRouter; this.mainRouter = mainRouter; this.frameworkRouter = frameworkRouter; + this.mutinyRouter = mutinyRouter; } public RuntimeValue getHttpRouter() { return httpRouter; } + public RuntimeValue getMutinyRouter() { + return mutinyRouter; + } + /** * Will be {@code null} if `${quarkus.http.root-path}` is {@literal /}. * diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/UserRouteRegistrationTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/UserRouteRegistrationTest.java index 6c8fda5dad616..389ce9e89c7e6 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/UserRouteRegistrationTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/UserRouteRegistrationTest.java @@ -23,13 +23,21 @@ public class UserRouteRegistrationTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(BeanRegisteringRouteUsingObserves.class, BeanRegisteringRouteUsingInject.class)); + .addClasses(BeanRegisteringRouteUsingObserves.class, + BeanRegisteringRouteUsingObservesWithMutinyRouter.class, + BeanRegisteringRouteUsingInject.class, + BeanRegisteringRouteUsingInjectWithMutinyRouter.class)); @Test public void test() { assertThat(RestAssured.get("/observes").asString()).isEqualTo("observers - ok"); + assertThat(RestAssured.get("/observes-mutiny").asString()).isEqualTo("observers mutiny - ok"); assertThat(RestAssured.get("/inject").asString()).isEqualTo("inject - ok"); + assertThat(RestAssured.get("/inject-mutiny").asString()).isEqualTo("inject mutiny - ok"); assertThat(given().body("test").contentType("text/plain").post("/body").asString()).isEqualTo("test"); + assertThat(given().body("test mutiny").contentType("text/plain").post("/body-mutiny").asString()) + .isEqualTo("test mutiny"); + } @ApplicationScoped @@ -41,6 +49,15 @@ public void register(@Observes Router router) { } + @ApplicationScoped + static class BeanRegisteringRouteUsingObservesWithMutinyRouter { + + public void register(@Observes io.vertx.mutiny.ext.web.Router router) { + router.route("/observes-mutiny").handler(rc -> rc.response().endAndForget("observers mutiny - ok")); + } + + } + @ApplicationScoped static class BeanRegisteringRouteUsingInject { @@ -55,4 +72,18 @@ public void register(@Observes StartupEvent ignored) { } } + + @ApplicationScoped + static class BeanRegisteringRouteUsingInjectWithMutinyRouter { + + @Inject + io.vertx.mutiny.ext.web.Router router; + + public void register(@Observes StartupEvent ignored) { + router.route("/inject-mutiny").handler(rc -> rc.response().endAndForget("inject mutiny - ok")); + router.route("/body-mutiny").consumes("text/plain").handler(io.vertx.mutiny.ext.web.handler.BodyHandler.create()) + .handler(rc -> rc.response().endAndForget(rc.getBodyAsString())); + } + + } } diff --git a/extensions/vertx-http/runtime/pom.xml b/extensions/vertx-http/runtime/pom.xml index acf9408a0b7ba..a7ab8fd5933b6 100644 --- a/extensions/vertx-http/runtime/pom.xml +++ b/extensions/vertx-http/runtime/pom.xml @@ -43,6 +43,10 @@ io.quarkus quarkus-vertx + + io.smallrye.reactive + smallrye-mutiny-vertx-web + io.vertx vertx-web diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/RouterProducer.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/RouterProducer.java deleted file mode 100644 index c0d72920dbec4..0000000000000 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/RouterProducer.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.quarkus.vertx.http.runtime; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import io.vertx.ext.web.Router; - -@Singleton -public class RouterProducer { - - private volatile Router router; - - void initialize(Router router) { - this.router = router; - } - - // Note that we need a client proxy because if a bean also @Observes Router a null value would be injected - @ApplicationScoped - @Produces - Router produceRouter() { - return router; - } - -} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 8ca1b45eff1e5..a7cf733fbb442 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -234,6 +234,10 @@ public RuntimeValue initializeRouter(final Supplier vertxRuntimeV return new RuntimeValue<>(router); } + public RuntimeValue createMutinyRouter(final RuntimeValue router) { + return new RuntimeValue<>(new io.vertx.mutiny.ext.web.Router(router.getValue())); + } + public void startServer(Supplier vertx, ShutdownContext shutdown, HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration, LaunchMode launchMode, @@ -271,7 +275,8 @@ public void mountFrameworkRouter(RuntimeValue mainRouter, RuntimeValue defaultRouteHandler, List filterList, Supplier vertx, LiveReloadConfig liveReloadConfig, Optional> mainRouterRuntimeValue, - RuntimeValue httpRouterRuntimeValue, String rootPath, LaunchMode launchMode, boolean requireBodyHandler, + RuntimeValue httpRouterRuntimeValue, RuntimeValue mutinyRouter, + String rootPath, LaunchMode launchMode, boolean requireBodyHandler, Handler bodyHandler, HttpConfiguration httpConfiguration, GracefulShutdownFilter gracefulShutdownFilter, ShutdownConfig shutdownConfig, Executor executor) { @@ -289,6 +294,8 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute // Then, fire the resuming router event.select(Router.class).fire(httpRouteRouter); + // Also fires the Mutiny one + event.select(io.vertx.mutiny.ext.web.Router.class).fire(mutinyRouter.getValue()); for (Filter filter : filterList) { if (filter.getHandler() != null) { @@ -301,7 +308,6 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute defaultRouteHandler.accept(httpRouteRouter.route().order(DEFAULT_ROUTE_ORDER)); } - container.instance(RouterProducer.class).initialize(httpRouteRouter); httpRouteRouter.route().last().failureHandler(new QuarkusErrorHandler(launchMode.isDevOrTest())); if (requireBodyHandler) {