
io.quarkus.vertx.http.deployment.VertxHttpProcessor Maven / Gradle / Ivy
package io.quarkus.vertx.http.deployment;
import static io.quarkus.runtime.TemplateHtmlBuilder.adjustRoot;
import static io.quarkus.vertx.http.deployment.RouteBuildItem.RouteType.FRAMEWORK_ROUTE;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.logging.Logger;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.builder.BuildException;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ApplicationStartBuildItem;
import io.quarkus.deployment.builditem.ExecutorBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LogCategoryBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.ShutdownListenerBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.kubernetes.spi.KubernetesPortBuildItem;
import io.quarkus.netty.runtime.virtual.VirtualServerChannel;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.LiveReloadConfig;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.shutdown.ShutdownConfig;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
import io.quarkus.vertx.core.deployment.EventLoopCountBuildItem;
import io.quarkus.vertx.http.HttpServerOptionsCustomizer;
import io.quarkus.vertx.http.deployment.devmode.HttpRemoteDevClientProvider;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.deployment.spi.FrameworkEndpointsBuildItem;
import io.quarkus.vertx.http.deployment.spi.UseManagementInterfaceBuildItem;
import io.quarkus.vertx.http.runtime.BasicRoute;
import io.quarkus.vertx.http.runtime.CurrentRequestProducer;
import io.quarkus.vertx.http.runtime.CurrentVertxRequest;
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.http.runtime.VertxConfigBuilder;
import io.quarkus.vertx.http.runtime.VertxHttpRecorder;
import io.quarkus.vertx.http.runtime.attribute.ExchangeAttributeBuilder;
import io.quarkus.vertx.http.runtime.cors.CORSRecorder;
import io.quarkus.vertx.http.runtime.filters.Filter;
import io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
import io.vertx.core.Handler;
import io.vertx.core.http.impl.Http1xServerRequest;
import io.vertx.core.impl.VertxImpl;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
class VertxHttpProcessor {
private static final String META_INF_SERVICES_EXCHANGE_ATTRIBUTE_BUILDER = "META-INF/services/io.quarkus.vertx.http.runtime.attribute.ExchangeAttributeBuilder";
private static final Logger logger = Logger.getLogger(VertxHttpProcessor.class);
@BuildStep
LogCategoryBuildItem logging() {
//this log is only used to log an error about an incorrect URI, which results in a 400 response
//we don't want to log this
return new LogCategoryBuildItem(Http1xServerRequest.class.getName(), Level.OFF);
}
@BuildStep
HttpRootPathBuildItem httpRoot(HttpBuildTimeConfig httpBuildTimeConfig) {
return new HttpRootPathBuildItem(httpBuildTimeConfig.rootPath);
}
@BuildStep
NonApplicationRootPathBuildItem frameworkRoot(HttpBuildTimeConfig httpBuildTimeConfig,
ManagementInterfaceBuildTimeConfig managementBuildTimeConfig) {
String mrp = null;
if (managementBuildTimeConfig.enabled) {
mrp = managementBuildTimeConfig.rootPath;
}
return new NonApplicationRootPathBuildItem(httpBuildTimeConfig.rootPath, httpBuildTimeConfig.nonApplicationRootPath,
mrp);
}
@BuildStep
FrameworkEndpointsBuildItem frameworkEndpoints(NonApplicationRootPathBuildItem nonApplicationRootPath,
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig, LaunchModeBuildItem launchModeBuildItem,
List routes) {
List frameworkEndpoints = new ArrayList<>();
for (RouteBuildItem route : routes) {
if (FRAMEWORK_ROUTE.equals(route.getRouteType())) {
if (route.getConfiguredPathInfo() != null) {
frameworkEndpoints.add(route.getConfiguredPathInfo().getEndpointPath(nonApplicationRootPath,
managementInterfaceBuildTimeConfig, launchModeBuildItem));
continue;
}
if (route.getRouteFunction() != null && route.getRouteFunction() instanceof BasicRoute) {
BasicRoute basicRoute = (BasicRoute) route.getRouteFunction();
if (basicRoute.getPath() != null) {
// Calling TemplateHtmlBuilder does not see very correct here, but it is the underlying API for ConfiguredPathInfo
frameworkEndpoints
.add(adjustRoot(nonApplicationRootPath.getNonApplicationRootPath(), basicRoute.getPath()));
}
}
}
}
return new FrameworkEndpointsBuildItem(frameworkEndpoints);
}
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
FilterBuildItem cors(CORSRecorder recorder) {
return new FilterBuildItem(recorder.corsHandler(), FilterBuildItem.CORS);
}
@BuildStep
AdditionalBeanBuildItem additionalBeans() {
return AdditionalBeanBuildItem.builder()
.setUnremovable()
.addBeanClass(CurrentVertxRequest.class)
.addBeanClass(CurrentRequestProducer.class)
.build();
}
@BuildStep
UnremovableBeanBuildItem shouldNotRemoveHttpServerOptionsCustomizers() {
return UnremovableBeanBuildItem.beanTypes(HttpServerOptionsCustomizer.class);
}
@BuildStep
UseManagementInterfaceBuildItem useManagementInterfaceBuildItem(ManagementInterfaceBuildTimeConfig config) {
if (config.enabled) {
return new UseManagementInterfaceBuildItem();
}
return null;
}
/**
* Workaround for https://github.com/quarkusio/quarkus/issues/4720 by filtering Vertx multiple instance warning in dev
* mode.
*/
@BuildStep
void filterMultipleVertxInstancesWarning(LaunchModeBuildItem launchModeBuildItem,
BuildProducer logCleanupFilterBuildItemBuildProducer) {
if (launchModeBuildItem.getLaunchMode().equals(LaunchMode.DEVELOPMENT)) {
logCleanupFilterBuildItemBuildProducer.produce(new LogCleanupFilterBuildItem(VertxImpl.class.getName(),
"You're already on a Vert.x context, are you sure you want to create a new Vertx instance"));
}
}
@BuildStep
public void kubernetes(BuildProducer kubernetesPorts) {
HttpConfiguration.InsecureRequests insecureRequests = ConfigProvider.getConfig()
.getOptionalValue("quarkus.http.insecure-requests", HttpConfiguration.InsecureRequests.class)
.orElse(HttpConfiguration.InsecureRequests.ENABLED);
if (insecureRequests != HttpConfiguration.InsecureRequests.DISABLED) {
// ssl is not disabled
int sslPort = ConfigProvider.getConfig()
.getOptionalValue("quarkus.http.ssl-port", Integer.class)
.orElse(8443);
kubernetesPorts.produce(new KubernetesPortBuildItem(sslPort, "https"));
}
int port = ConfigProvider.getConfig().getOptionalValue("quarkus.http.port", Integer.class).orElse(8080);
kubernetesPorts.produce(new KubernetesPortBuildItem(port, "http"));
}
@BuildStep
public KubernetesPortBuildItem kubernetesForManagement(
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig) {
if (managementInterfaceBuildTimeConfig.enabled) {
int port = ConfigProvider.getConfig().getOptionalValue("quarkus.management.port", Integer.class).orElse(9000);
return new KubernetesPortBuildItem(port, "management");
}
return null;
}
@BuildStep
void notFoundRoutes(
List routes,
BuildProducer notFound) {
for (RouteBuildItem i : routes) {
if (i.getNotFoundPageDisplayableEndpoint() != null) {
notFound.produce(i.getNotFoundPageDisplayableEndpoint());
}
}
}
@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,
ManagementInterfaceBuildTimeConfig managementBuildTimeConfig,
NonApplicationRootPathBuildItem nonApplicationRootPath,
ShutdownContextBuildItem shutdown) {
RuntimeValue httpRouteRouter = initialRouter.getHttpRouter();
RuntimeValue mutinyRouter = initialRouter.getMutinyRouter();
RuntimeValue frameworkRouter = null;
RuntimeValue mainRouter = null;
RuntimeValue managementRouter = null;
List redirectRoutes = new ArrayList<>();
boolean frameworkRouterCreated = false;
boolean mainRouterCreated = false;
boolean managementRouterCreated = false;
boolean isManagementInterfaceEnabled = managementBuildTimeConfig.enabled;
for (RouteBuildItem route : routes) {
if (route.isManagement() && isManagementInterfaceEnabled) {
if (!managementRouterCreated) {
managementRouter = recorder.initializeRouter(vertx.getVertx());
managementRouterCreated = true;
}
recorder.addRoute(managementRouter, route.getRouteFunction(), route.getHandler(), route.getType());
} else if (nonApplicationRootPath.isDedicatedRouterRequired() && route.isRouterFramework()) {
// Non-application endpoints on a separate path
if (!frameworkRouterCreated) {
frameworkRouter = recorder.initializeRouter(vertx.getVertx());
frameworkRouterCreated = true;
}
recorder.addRoute(frameworkRouter, route.getRouteFunction(), route.getHandler(), route.getType());
} else if (route.isRouterAbsolute()) {
// Add Route to "/"
if (!mainRouterCreated) {
mainRouter = recorder.initializeRouter(vertx.getVertx());
mainRouterCreated = true;
}
recorder.addRoute(mainRouter, route.getRouteFunction(), route.getHandler(), route.getType());
} else {
// Add Route to "/${quarkus.http.root-path}/
recorder.addRoute(httpRouteRouter, route.getRouteFunction(), route.getHandler(), route.getType());
}
}
if (frameworkRouterCreated) {
if (redirectRoutes.size() > 0) {
recorder.setNonApplicationRedirectHandler(nonApplicationRootPath.getNonApplicationRootPath(),
nonApplicationRootPath.getNormalizedHttpRootPath());
redirectRoutes.forEach(route -> recorder.addRoute(httpRouteRouter, route.getRouteFunction(),
recorder.getNonApplicationRedirectHandler(),
route.getType()));
}
}
return new VertxWebRouterBuildItem(httpRouteRouter, mainRouter, frameworkRouter, managementRouter, mutinyRouter);
}
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
BodyHandlerBuildItem bodyHandler(VertxHttpRecorder recorder) {
return new BodyHandlerBuildItem(recorder.createBodyHandler());
}
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
ServiceStartBuildItem finalizeRouter(
VertxHttpRecorder recorder, BeanContainerBuildItem beanContainer, CoreVertxBuildItem vertx,
LaunchModeBuildItem launchMode,
List defaultRoutes,
List filters,
List managementInterfacefilters,
VertxWebRouterBuildItem httpRouteRouter,
HttpRootPathBuildItem httpRootPathBuildItem,
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
HttpBuildTimeConfig httpBuildTimeConfig,
List requireBodyHandlerBuildItems,
BodyHandlerBuildItem bodyHandlerBuildItem,
BuildProducer shutdownListenerBuildItemBuildProducer,
ShutdownConfig shutdownConfig,
LiveReloadConfig lrc,
CoreVertxBuildItem core, // Injected to be sure that Vert.x has been produced before calling this method.
ExecutorBuildItem executorBuildItem)
throws BuildException, IOException {
Optional defaultRoute;
if (defaultRoutes == null || defaultRoutes.isEmpty()) {
defaultRoute = Optional.empty();
} else {
if (defaultRoutes.size() > 1) {
// this should never happen
throw new BuildException("Too many default routes.", Collections.emptyList());
} else {
defaultRoute = Optional.of(defaultRoutes.get(0));
}
}
HttpRemoteDevClientProvider.liveReloadConfig = lrc;
GracefulShutdownFilter gracefulShutdownFilter = recorder.createGracefulShutdownHandler();
shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem(gracefulShutdownFilter));
List listOfFilters = filters.stream()
.filter(f -> f.getHandler() != null)
.map(FilterBuildItem::toFilter).collect(Collectors.toList());
List listOfManagementInterfaceFilters = managementInterfacefilters.stream()
.filter(f -> f.getHandler() != null)
.map(ManagementInterfaceFilterBuildItem::toFilter).collect(Collectors.toList());
//if the body handler is required then we know it is installed for all routes, so we don't need to register it here
Handler bodyHandler = !requireBodyHandlerBuildItems.isEmpty() ? bodyHandlerBuildItem.getHandler()
: null;
Optional> mainRouter = httpRouteRouter.getMainRouter() != null
? Optional.of(httpRouteRouter.getMainRouter())
: Optional.empty();
if (httpRouteRouter.getFrameworkRouter() != null) {
if (nonApplicationRootPathBuildItem.isAttachedToMainRouter()) {
// Mount nested framework router
recorder.mountFrameworkRouter(httpRouteRouter.getHttpRouter(),
httpRouteRouter.getFrameworkRouter(),
nonApplicationRootPathBuildItem.getVertxRouterPath());
} else {
// Create main router, not mounted under application router
if (!mainRouter.isPresent()) {
mainRouter = Optional.of(recorder.initializeRouter(vertx.getVertx()));
}
// Mount independent framework router under new main router
recorder.mountFrameworkRouter(mainRouter.get(), httpRouteRouter.getFrameworkRouter(),
nonApplicationRootPathBuildItem.getVertxRouterPath());
}
}
recorder.finalizeRouter(beanContainer.getValue(),
defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null),
listOfFilters, listOfManagementInterfaceFilters,
vertx.getVertx(), lrc, mainRouter, httpRouteRouter.getHttpRouter(),
httpRouteRouter.getMutinyRouter(), httpRouteRouter.getFrameworkRouter(),
httpRouteRouter.getManagementRouter(),
httpRootPathBuildItem.getRootPath(),
nonApplicationRootPathBuildItem.getNonApplicationRootPath(),
launchMode.getLaunchMode(),
!requireBodyHandlerBuildItems.isEmpty(), bodyHandler, gracefulShutdownFilter,
shutdownConfig, executorBuildItem.getExecutorProxy());
return new ServiceStartBuildItem("vertx-http");
}
@BuildStep
void config(BuildProducer runtimeConfigBuilder) {
runtimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(VertxConfigBuilder.class));
}
@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void openSocket(ApplicationStartBuildItem start,
LaunchModeBuildItem launchMode,
CoreVertxBuildItem vertx,
ShutdownContextBuildItem shutdown,
BuildProducer reflectiveClass,
HttpBuildTimeConfig httpBuildTimeConfig,
Optional requireVirtual,
EventLoopCountBuildItem eventLoopCount,
List websocketSubProtocols,
Capabilities capabilities,
VertxHttpRecorder recorder) throws IOException {
boolean startVirtual = requireVirtual.isPresent() || httpBuildTimeConfig.virtual;
if (startVirtual) {
reflectiveClass
.produce(ReflectiveClassBuildItem.builder(VirtualServerChannel.class)
.build());
}
boolean startSocket = (!startVirtual || launchMode.getLaunchMode() != LaunchMode.NORMAL)
&& (requireVirtual.isEmpty() || !requireVirtual.get().isAlwaysVirtual());
recorder.startServer(vertx.getVertx(), shutdown,
launchMode.getLaunchMode(), startVirtual, startSocket,
eventLoopCount.getEventLoopCount(),
websocketSubProtocols.stream().map(bi -> bi.getWebsocketSubProtocols())
.collect(Collectors.toList()),
launchMode.isAuxiliaryApplication(), !capabilities.isPresent(Capability.VERTX_WEBSOCKETS));
}
@BuildStep
void configureNativeCompilation(BuildProducer runtimeInitializedClasses) {
runtimeInitializedClasses
.produce(new RuntimeInitializedClassBuildItem("io.vertx.ext.web.handler.sockjs.impl.XhrTransport"));
runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("io.vertx.ext.auth.impl.jose.JWT"));
}
/**
* Register the {@link ExchangeAttributeBuilder} services for native image consumption
*
* @param exchangeAttributeBuilderService
* @throws BuildException
*/
@BuildStep
void registerExchangeAttributeBuilders(final BuildProducer exchangeAttributeBuilderService)
throws BuildException {
// get hold of the META-INF/services/io.quarkus.vertx.http.runtime.attribute.ExchangeAttributeBuilder
// from within the jar containing the ExchangeAttributeBuilder class
final List elements = QuarkusClassLoader.getElements(META_INF_SERVICES_EXCHANGE_ATTRIBUTE_BUILDER,
false);
if (elements.isEmpty()) {
logger.debug("Skipping registration of service providers for " + ExchangeAttributeBuilder.class);
return;
}
for (ClassPathElement cpe : elements) {
cpe.apply(tree -> {
tree.accept(META_INF_SERVICES_EXCHANGE_ATTRIBUTE_BUILDER, visit -> {
if (visit == null) {
logger.debug("Skipping registration of service providers for " + ExchangeAttributeBuilder.class
+ " since no service descriptor file found");
} else {
// we register all the listed providers since the access log configuration is a runtime construct
// and we won't know at build time which attributes the user application will choose
final ServiceProviderBuildItem serviceProviderBuildItem;
try {
serviceProviderBuildItem = ServiceProviderBuildItem
.allProviders(ExchangeAttributeBuilder.class.getName(), visit.getPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
exchangeAttributeBuilderService.produce(serviceProviderBuildItem);
}
});
return null;
});
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy