diff --git a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java index b87f5c19a40291..c8882b07d9f1df 100644 --- a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java +++ b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java @@ -46,7 +46,7 @@ import io.quarkus.info.runtime.spi.InfoContributor; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; -import io.quarkus.vertx.http.deployment.RouteBuildItem; +import io.quarkus.vertx.http.deployment.spi.RouteBuildItem; public class InfoProcessor { @@ -283,14 +283,12 @@ RouteBuildItem defineRoute(InfoBuildTimeConfig buildTimeConfig, List infoContributors = contributors.stream() .map(InfoBuildTimeContributorBuildItem::getInfoContributor) .collect(Collectors.toList()); + unremovableBeanBuildItemBuildProducer.produce(UnremovableBeanBuildItem.beanTypes(InfoContributor.class)); - return nonApplicationRootPathBuildItem.routeBuilder() - .management() - .route(buildTimeConfig.path()) - .routeConfigKey("quarkus.info.path") - .handler(recorder.handler(buildTimeInfo, infoContributors)) - .displayOnNotFoundPage() - .blockingRoute() + + return RouteBuildItem.newManagementRoute(buildTimeConfig.path()) + .withRoutePathConfigKey("quarkus.info.path") + .withRequestHandler(recorder.handler(buildTimeInfo, infoContributors)) .build(); } } diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java index c9a12f3a1264a7..b5308159a27d31 100644 --- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java +++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java @@ -107,9 +107,9 @@ import io.quarkus.vertx.http.deployment.FilterBuildItem; import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; -import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.deployment.SecurityInformationBuildItem; import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem; +import io.quarkus.vertx.http.deployment.spi.RouteBuildItem; import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig; import io.quarkus.vertx.http.runtime.management.ManagementInterfaceConfiguration; import io.smallrye.openapi.api.OpenApiConfig; @@ -305,32 +305,31 @@ void handler(LaunchModeBuildItem launch, } } - routes.produce(nonApplicationRootPathBuildItem.routeBuilder() - .management("quarkus.smallrye-openapi.management.enabled") - .routeFunction(openApiConfig.path, corsFilter) - .routeConfigKey("quarkus.smallrye-openapi.path") - .handler(handler) + routes.produce(RouteBuildItem.newManagementRoute(openApiConfig.path, "quarkus.smallrye-openapi.management.enabled") + .withRouteCustomizer(corsFilter) + .withRoutePathConfigKey("quarkus.smallrye-openapi.path") + .withRequestHandler(handler) .displayOnNotFoundPage("Open API Schema document") - .blockingRoute() + .asBlockingRoute() .build()); - routes.produce(nonApplicationRootPathBuildItem.routeBuilder() - .management("quarkus.smallrye-openapi.management.enabled") - .routeFunction(openApiConfig.path + ".json", corsFilter) - .handler(handler) - .build()); - - routes.produce(nonApplicationRootPathBuildItem.routeBuilder() - .management("quarkus.smallrye-openapi.management.enabled") - .routeFunction(openApiConfig.path + ".yaml", corsFilter) - .handler(handler) - .build()); - - routes.produce(nonApplicationRootPathBuildItem.routeBuilder() - .management("quarkus.smallrye-openapi.management.enabled") - .routeFunction(openApiConfig.path + ".yml", corsFilter) - .handler(handler) - .build()); + routes.produce( + RouteBuildItem.newManagementRoute(openApiConfig.path + ".json", "quarkus.smallrye-openapi.management.enabled") + .withRouteCustomizer(corsFilter) + .withRequestHandler(handler) + .build()); + + routes.produce( + RouteBuildItem.newManagementRoute(openApiConfig.path + ".yaml", "quarkus.smallrye-openapi.management.enabled") + .withRouteCustomizer(corsFilter) + .withRequestHandler(handler) + .build()); + + routes.produce( + RouteBuildItem.newManagementRoute(openApiConfig.path + ".yml", "quarkus.smallrye-openapi.management.enabled") + .withRouteCustomizer(corsFilter) + .withRequestHandler(handler) + .build()); // If management is enabled and swagger-ui is part of management, we need to add CORS so that swagger can hit the endpoint if (isManagement(managementInterfaceBuildTimeConfig, openApiConfig, launch)) { diff --git a/extensions/vertx-http/deployment-spi/pom.xml b/extensions/vertx-http/deployment-spi/pom.xml index b56cb27d89a19c..6b7d3426684ffb 100644 --- a/extensions/vertx-http/deployment-spi/pom.xml +++ b/extensions/vertx-http/deployment-spi/pom.xml @@ -18,6 +18,11 @@ io.quarkus quarkus-core-deployment + + + io.vertx + vertx-web + diff --git a/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/RouteBuildItem.java b/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/RouteBuildItem.java new file mode 100644 index 00000000000000..7529e99b93fe8f --- /dev/null +++ b/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/RouteBuildItem.java @@ -0,0 +1,230 @@ +package io.quarkus.vertx.http.deployment.spi; + +import java.util.OptionalInt; +import java.util.function.Consumer; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; + +import io.quarkus.builder.item.MultiBuildItem; +import io.vertx.core.Handler; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; + +/** + * A build item that represents a route that should be added to the router. + *

+ * Producing this build item does not mean the HTTP server is available. + * It will be consumed if the Quarkus Vert.x HTTP extension is present. + */ +public final class RouteBuildItem extends MultiBuildItem { + + /** + * The type of route handler + */ + public enum HandlerType { + + /** + * A regular route handler invoked on the event loop. + * + * @see io.vertx.ext.web.Route#handler(Handler) + */ + NORMAL, + /** + * A blocking route handler, invoked on a worker thread. + * + * @see io.vertx.ext.web.Route#blockingHandler(Handler) + */ + BLOCKING, + /** + * A failure handler, invoked when an exception is thrown from a route handler. + * This is invoked on the event loop. + * + * @see io.vertx.ext.web.Route#failureHandler(Handler) + */ + FAILURE + + } + + /** + * Type of routes. + */ + public enum RouteType { + + /** + * Framework routes are provided by the Quarkus framework (or extensions). + * They are not related to the application business logic, but provide a non-functional feature (health, metrics...). + *

+ * Framework route can be mounted on the application router (under the non application route path) or on the management + * router when enabled. + */ + FRAMEWORK_ROUTE, + /** + * Application routes are part of the application business logic. + * They are mounted on the application router (so the application prefix is applied). + */ + APPLICATION_ROUTE, + /** + * Absolute routes are part of the application business logic, and are mounted on the root router (exposed on /). + */ + ABSOLUTE_ROUTE + } + + private RouteType typeOfRoute = RouteType.APPLICATION_ROUTE; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + private OptionalInt order = OptionalInt.empty(); + + private String path; + private Consumer customizer; + + private boolean isManagement; + + private Handler handler; + + private HandlerType typeOfHandler = HandlerType.NORMAL; + + private boolean displayOnNotFoundPage; + private String notFoundPageTitle; + + private String routeConfigKey; + + public RouteType getTypeOfRoute() { + return typeOfRoute; + } + + public boolean hasOrder() { + return order.isPresent(); + } + + public int getOrder() { + if (order.isPresent()) { + return order.getAsInt(); + } else { + throw new IllegalStateException("No order set"); + } + } + + public boolean hasRouteConfigKey() { + return routeConfigKey != null; + } + + public String getRouteConfigKey() { + return routeConfigKey; + } + + public Handler getHandler() { + return handler; + } + + public HandlerType getHandlerType() { + return typeOfHandler; + } + + public String getPath() { + return path; + } + + public Consumer getCustomizer() { + return customizer; + } + + public String getNotFoundPageTitle() { + return notFoundPageTitle; + } + + public boolean isDisplayOnNotFoundPage() { + return displayOnNotFoundPage; + } + + public static Builder newApplicationRoute(String path) { + return new Builder(RouteType.APPLICATION_ROUTE, path, false); + } + + public static Builder newAbsoluteRoute(String path) { + return new Builder(RouteType.ABSOLUTE_ROUTE, path, false); + } + + public static Builder newFrameworkRoute(String path) { + return new Builder(RouteType.FRAMEWORK_ROUTE, path, false); + } + + public static Builder newManagementRoute(String path) { + return new Builder(RouteType.FRAMEWORK_ROUTE, path, true); + } + + public static Builder newManagementRoute(String path, String managementConfigKey) { + return new Builder(RouteType.FRAMEWORK_ROUTE, path, + (managementConfigKey == null || shouldInclude(managementConfigKey))); + } + + private static boolean shouldInclude(String managementConfigKey) { + Config config = ConfigProvider.getConfig(); + return config.getValue(managementConfigKey, boolean.class); + } + + public boolean isManagement() { + return isManagement; + } + + public static class Builder { + + private final RouteBuildItem item; + + private Builder(RouteType type, String path, boolean isManagement) { + item = new RouteBuildItem(); + item.typeOfRoute = type; + item.path = path; + item.isManagement = isManagement; + } + + public Builder withRouteCustomizer(Consumer customizer) { + item.customizer = customizer; + return this; + } + + public Builder withOrder(int order) { + item.order = OptionalInt.of(order); + return this; + } + + public Builder withRequestHandler(Handler handler) { + item.handler = handler; + return this; + } + + public Builder asBlockingRoute() { + item.typeOfHandler = HandlerType.BLOCKING; + return this; + } + + public Builder failureRoute() { + item.typeOfHandler = HandlerType.FAILURE; + return this; + } + + public Builder displayOnNotFoundPage() { + item.displayOnNotFoundPage = true; + return this; + } + + public Builder displayOnNotFoundPage(String notFoundPageTitle) { + item.displayOnNotFoundPage = true; + item.notFoundPageTitle = notFoundPageTitle; + return this; + } + + public Builder withRoutePathConfigKey(String attributeName) { + item.routeConfigKey = attributeName; + return this; + } + + public RouteBuildItem build() { + if (item.handler == null) { + throw new IllegalArgumentException("The route handler must be set"); + } + + return item; + } + } + +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteConverter.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteConverter.java new file mode 100644 index 00000000000000..606889a3d2f815 --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteConverter.java @@ -0,0 +1,44 @@ +package io.quarkus.vertx.http.deployment; + +import io.quarkus.vertx.http.runtime.HandlerType; + +/** + * Convert the route build item from the SPI to the internal representation + */ +public class RouteConverter { + + public static RouteBuildItem convert(io.quarkus.vertx.http.deployment.spi.RouteBuildItem item, + HttpRootPathBuildItem httpRootPathBuildItem, + NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem) { + // The builder depends on the type of route + RouteBuildItem.Builder builder; + if (item.getTypeOfRoute() == io.quarkus.vertx.http.deployment.spi.RouteBuildItem.RouteType.FRAMEWORK_ROUTE) { + builder = nonApplicationRootPathBuildItem.routeBuilder(); + } else { + builder = httpRootPathBuildItem.routeBuilder(); + } + + if (item.isManagement()) { + builder = builder.management(); + } + if (item.hasRouteConfigKey()) { + builder = builder.routeConfigKey(item.getRouteConfigKey()); + } + + builder = builder.handler(item.getHandler()).handlerType(HandlerType.valueOf(item.getHandlerType().name())); + if (item.isDisplayOnNotFoundPage()) { + builder = builder + .displayOnNotFoundPage(item.getNotFoundPageTitle()); + } + + if (item.hasOrder()) { + builder = builder.orderedRoute(item.getPath(), item.getOrder(), item.getCustomizer()); + } else { + builder = builder.routeFunction(item.getPath(), item.getCustomizer()); + } + + return builder.build(); + + } + +} 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 0d6835565655e1..dc19568107b9a6 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 @@ -95,6 +95,19 @@ HttpRootPathBuildItem httpRoot(HttpBuildTimeConfig httpBuildTimeConfig) { return new HttpRootPathBuildItem(httpBuildTimeConfig.rootPath); } + @BuildStep + List convertRoutes( + List items, + HttpRootPathBuildItem httpRootPathBuildItem, + NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem) { + List list = new ArrayList<>(); + for (io.quarkus.vertx.http.deployment.spi.RouteBuildItem item : items) { + RouteBuildItem converted = RouteConverter.convert(item, httpRootPathBuildItem, nonApplicationRootPathBuildItem); + list.add(converted); + } + return list; + } + @BuildStep NonApplicationRootPathBuildItem frameworkRoot(HttpBuildTimeConfig httpBuildTimeConfig, ManagementInterfaceBuildTimeConfig managementBuildTimeConfig) { @@ -276,7 +289,7 @@ VertxWebRouterBuildItem initializeRouter(VertxHttpRecorder recorder, } } - /** + /* * To create mainrouter when `${quarkus.http.root-path}` is not {@literal /} * Refer https://github.com/quarkusio/quarkus/issues/34261 */ @@ -475,7 +488,7 @@ void registerExchangeAttributeBuilders(final BuildProducer "quarkus.http.insecure-requests" is not explicitly disabled * <2> any of the http SSL runtime properties are set at build time - * + *

* If any of the above rules applied, the port "https" will be generated as part of the Kubernetes resources. */ private static boolean isSslConfigured() { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HandlerType.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HandlerType.java index a8ae971d3a4795..881dbb672bbef5 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HandlerType.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HandlerType.java @@ -2,22 +2,26 @@ import io.vertx.core.Handler; +/** + * The type of route handler + */ public enum HandlerType { /** - * A request handler. + * A regular route handler invoked on the event loop. * * @see io.vertx.ext.web.Route#handler(Handler) */ NORMAL, /** - * A blocking request handler. + * A blocking route handler, invoked on a worker thread. * * @see io.vertx.ext.web.Route#blockingHandler(Handler) */ BLOCKING, /** - * A failure handler. + * A failure handler, invoked when an exception is thrown from a route handler. + * This is invoked on the event loop. * * @see io.vertx.ext.web.Route#failureHandler(Handler) */