diff --git a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java b/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java index 90fe316bcd0c6..e0fc5649c5d52 100644 --- a/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java +++ b/extensions/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java @@ -3,21 +3,10 @@ import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.HashSet; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.builder.item.SimpleBuildItem; -import io.quarkus.deployment.ApplicationArchive; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; @@ -31,32 +20,25 @@ import io.quarkus.resteasy.common.deployment.ResteasyInjectionReadyBuildItem; import io.quarkus.resteasy.runtime.standalone.ResteasyStandaloneRecorder; import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentBuildItem; -import io.quarkus.runtime.util.ClassPathUtils; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; import io.quarkus.vertx.http.deployment.DefaultRouteBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; +import io.quarkus.vertx.http.runtime.BasicRoute; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpConfiguration; +import io.quarkus.vertx.http.runtime.VertxHttpRecorder; import io.vertx.core.Handler; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; public class ResteasyStandaloneBuildStep { - protected static final String META_INF_RESOURCES_SLASH = "META-INF/resources/"; - protected static final String META_INF_RESOURCES = "META-INF/resources"; - public static final class ResteasyStandaloneBuildItem extends SimpleBuildItem { final String deploymentRootPath; public ResteasyStandaloneBuildItem(String deploymentRootPath) { - if (deploymentRootPath != null) { - this.deploymentRootPath = deploymentRootPath.startsWith("/") ? deploymentRootPath : "/" + deploymentRootPath; - } else { - this.deploymentRootPath = null; - } + this.deploymentRootPath = deploymentRootPath.startsWith("/") ? deploymentRootPath : "/" + deploymentRootPath; } } @@ -74,7 +56,6 @@ public void staticInit(ResteasyStandaloneRecorder recorder, return; } - Set knownPaths = getClasspathResources(applicationArchivesBuildItem); String deploymentRootPath = null; // The context path + the resources path String rootPath = httpConfig.rootPath; @@ -93,64 +74,11 @@ public void staticInit(ResteasyStandaloneRecorder recorder, } rootPath += deploymentRootPath; } - recorder.staticInit(deployment.getDeployment(), rootPath, knownPaths); - - } else if (!knownPaths.isEmpty()) { - recorder.staticInit(null, rootPath, knownPaths); - } - - if (deployment != null || !knownPaths.isEmpty()) { + recorder.staticInit(deployment.getDeployment(), rootPath); standalone.produce(new ResteasyStandaloneBuildItem(deploymentRootPath)); } } - /** - * Find all static file resources that are available from classpath. - * - * @param applicationArchivesBuildItem - * @return - * @throws Exception - */ - private Set getClasspathResources(ApplicationArchivesBuildItem applicationArchivesBuildItem) throws Exception { - Set knownPaths = new HashSet<>(); - for (ApplicationArchive i : applicationArchivesBuildItem.getAllApplicationArchives()) { - Path resource = i.getChildPath(META_INF_RESOURCES); - if (resource != null && Files.exists(resource)) { - collectKnownPaths(resource, knownPaths); - } - } - - ClassPathUtils.consumeAsPaths(META_INF_RESOURCES, resource -> { - collectKnownPaths(resource, knownPaths); - }); - - return knownPaths; - } - - private void collectKnownPaths(Path resource, Set knownPaths) { - try { - Files.walkFileTree(resource, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) - throws IOException { - String file = resource.relativize(p).toString(); - if (file.equals("index.html") || file.equals("index.htm")) { - knownPaths.add("/"); - } - if (!file.startsWith("/")) { - file = "/" + file; - } - // Windows has a backslash - file = file.replace('\\', '/'); - knownPaths.add(file); - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - @BuildStep @Record(RUNTIME_INIT) public void boot(ShutdownContextBuildItem shutdown, @@ -170,31 +98,23 @@ public void boot(ShutdownContextBuildItem shutdown, } feature.produce(new FeatureBuildItem(Feature.RESTEASY)); - boolean isDefaultOrNullDeploymentPath = standalone.deploymentRootPath == null - || standalone.deploymentRootPath.equals("/"); - if (!isDefaultOrNullDeploymentPath) { - // We need to register a special handler for non-default deployment path (specified as application path or resteasyConfig.path) - Handler handler = recorder.vertxRequestHandler(vertx.getVertx(), beanContainer.getValue(), - executorBuildItem.getExecutorProxy(), httpConfiguration); - // Exact match for resources matched to the root path - routes.produce(new RouteBuildItem(standalone.deploymentRootPath, handler, false)); - String matchPath = standalone.deploymentRootPath; - if (matchPath.endsWith("/")) { - matchPath += "*"; - } else { - matchPath += "/*"; - } - // Match paths that begin with the deployment path - routes.produce(new RouteBuildItem(matchPath, handler, false)); + // 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(), + executorBuildItem.getExecutorProxy(), httpConfiguration); + // Exact match for resources matched to the root path + routes.produce(new RouteBuildItem( + new BasicRoute(standalone.deploymentRootPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1), handler)); + String matchPath = standalone.deploymentRootPath; + if (matchPath.endsWith("/")) { + matchPath += "*"; + } else { + matchPath += "/*"; } + // Match paths that begin with the deployment path + routes.produce(new RouteBuildItem(new BasicRoute(matchPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1), handler)); - boolean isVirtual = requireVirtual.isPresent(); - Consumer ut = recorder.start(vertx.getVertx(), - shutdown, - beanContainer.getValue(), - isVirtual, isDefaultOrNullDeploymentPath, executorBuildItem.getExecutorProxy(), httpConfiguration); - - defaultRoutes.produce(new DefaultRouteBuildItem(ut)); + recorder.start(shutdown, requireVirtual.isPresent()); } } diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java index 00c3e8cecf0ab..c40e32aec28b7 100644 --- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java +++ b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java @@ -1,11 +1,6 @@ package io.quarkus.resteasy.runtime.standalone; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; import java.util.concurrent.Executor; -import java.util.function.Consumer; import java.util.function.Supplier; import org.jboss.resteasy.spi.ResteasyDeployment; @@ -16,12 +11,9 @@ import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.vertx.http.runtime.HttpConfiguration; -import io.quarkus.vertx.http.runtime.ThreadLocalHandler; import io.vertx.core.Handler; import io.vertx.core.Vertx; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.StaticHandler; /** * Provides the runtime methods to bootstrap Resteasy in standalone mode. @@ -70,30 +62,18 @@ public int getBufferSize() { } }; - private static volatile List hotDeploymentResourcePaths; - - public static void setHotDeploymentResources(List resources) { - hotDeploymentResourcePaths = resources; - } - private static ResteasyDeployment deployment; - private static Set knownPaths; private static String contextPath; - public void staticInit(ResteasyDeployment dep, String path, Set known) { + public void staticInit(ResteasyDeployment dep, String path) { if (dep != null) { deployment = dep; deployment.start(); } - knownPaths = known; contextPath = path; } - public Consumer start(Supplier vertx, - ShutdownContext shutdown, - BeanContainer beanContainer, - boolean isVirtual, boolean isDefaultResourcesPath, - Executor executor, HttpConfiguration httpConfiguration) { + public void start(ShutdownContext shutdown, boolean isVirtual) { shutdown.addShutdownTask(new Runnable() { @Override @@ -104,64 +84,6 @@ public void run() { } }); useDirect = !isVirtual; - List> handlers = new ArrayList<>(); - - if (hotDeploymentResourcePaths != null && !hotDeploymentResourcePaths.isEmpty()) { - for (Path resourcePath : hotDeploymentResourcePaths) { - String root = resourcePath.toAbsolutePath().toString(); - ThreadLocalHandler staticHandler = new ThreadLocalHandler(new Supplier>() { - @Override - public Handler get() { - StaticHandler staticHandler = StaticHandler.create(); - staticHandler.setCachingEnabled(false); - staticHandler.setAllowRootFileSystemAccess(true); - staticHandler.setWebRoot(root); - staticHandler.setDefaultContentEncoding("UTF-8"); - return staticHandler; - } - }); - handlers.add(event -> { - try { - staticHandler.handle(event); - } catch (Exception e) { - // on Windows, the drive in file path screws up cache lookup - // so just punt to next handler - event.next(); - } - }); - } - } - if (!knownPaths.isEmpty()) { - ThreadLocalHandler staticHandler = new ThreadLocalHandler(new Supplier>() { - @Override - public Handler get() { - return StaticHandler.create(META_INF_RESOURCES) - .setDefaultContentEncoding("UTF-8"); - } - }); - handlers.add(ctx -> { - String rel = ctx.mountPoint() == null ? ctx.normalisedPath() - : ctx.normalisedPath().substring(ctx.mountPoint().length()); - if (knownPaths.contains(rel)) { - staticHandler.handle(ctx); - } else { - ctx.next(); - } - }); - } - - if (deployment != null && isDefaultResourcesPath) { - handlers.add(vertxRequestHandler(vertx, beanContainer, executor, httpConfiguration)); - } - return new Consumer() { - - @Override - public void accept(Route route) { - for (Handler i : handlers) { - route.handler(i); - } - } - }; } public Handler vertxRequestHandler(Supplier vertx, diff --git a/extensions/resteasy/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup b/extensions/resteasy/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup deleted file mode 100644 index 5100a407f5e12..0000000000000 --- a/extensions/resteasy/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.resteasy.runtime.devmode.ResteasyHotReplacementSetup \ No newline at end of file diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java index 111bb4fae199c..a993ba90ad40f 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java @@ -1,7 +1,5 @@ package io.quarkus.smallrye.graphql.runtime; -import java.util.function.Supplier; - import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.CDI; @@ -10,7 +8,6 @@ import io.quarkus.runtime.annotations.Recorder; import io.quarkus.security.identity.CurrentIdentityAssociation; import io.quarkus.smallrye.graphql.runtime.spi.QuarkusClassloadingService; -import io.quarkus.vertx.http.runtime.ThreadLocalHandler; import io.smallrye.graphql.cdi.producer.GraphQLProducer; import io.smallrye.graphql.schema.model.Schema; import io.vertx.core.Handler; @@ -45,14 +42,9 @@ public Handler schemaHandler() { public Handler uiHandler(String graphqlUiFinalDestination, String graphqlUiPath) { - Handler handler = new ThreadLocalHandler(new Supplier>() { - @Override - public Handler get() { - return StaticHandler.create().setAllowRootFileSystemAccess(true) - .setWebRoot(graphqlUiFinalDestination) - .setDefaultContentEncoding("UTF-8"); - } - }); + StaticHandler staticHandler = StaticHandler.create().setAllowRootFileSystemAccess(true) + .setWebRoot(graphqlUiFinalDestination) + .setDefaultContentEncoding("UTF-8"); return new Handler() { @Override @@ -68,7 +60,7 @@ public void handle(RoutingContext event) { return; } - handler.handle(event); + staticHandler.handle(event); } }; } diff --git a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthRecorder.java b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthRecorder.java index f018d407488c8..ef24518be6bf0 100644 --- a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthRecorder.java +++ b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthRecorder.java @@ -1,12 +1,9 @@ package io.quarkus.smallrye.health.runtime; -import java.util.function.Supplier; - import org.eclipse.microprofile.health.HealthCheckResponse; import org.eclipse.microprofile.health.spi.HealthCheckResponseProvider; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.vertx.http.runtime.ThreadLocalHandler; import io.vertx.core.Handler; import io.vertx.core.http.HttpHeaders; import io.vertx.ext.web.RoutingContext; @@ -26,14 +23,9 @@ public void registerHealthCheckResponseProvider(Class uiHandler(String healthUiFinalDestination, String healthUiPath) { - Handler handler = new ThreadLocalHandler(new Supplier>() { - @Override - public Handler get() { - return StaticHandler.create().setAllowRootFileSystemAccess(true) - .setWebRoot(healthUiFinalDestination) - .setDefaultContentEncoding("UTF-8"); - } - }); + StaticHandler staticHandler = StaticHandler.create().setAllowRootFileSystemAccess(true) + .setWebRoot(healthUiFinalDestination) + .setDefaultContentEncoding("UTF-8"); return new Handler() { @Override @@ -49,7 +41,7 @@ public void handle(RoutingContext event) { return; } - handler.handle(event); + staticHandler.handle(event); } }; } diff --git a/extensions/swagger-ui/runtime/src/main/java/io/quarkus/swaggerui/runtime/SwaggerUiRecorder.java b/extensions/swagger-ui/runtime/src/main/java/io/quarkus/swaggerui/runtime/SwaggerUiRecorder.java index c59d3ee98cbed..745e8122f276b 100644 --- a/extensions/swagger-ui/runtime/src/main/java/io/quarkus/swaggerui/runtime/SwaggerUiRecorder.java +++ b/extensions/swagger-ui/runtime/src/main/java/io/quarkus/swaggerui/runtime/SwaggerUiRecorder.java @@ -1,9 +1,6 @@ package io.quarkus.swaggerui.runtime; -import java.util.function.Supplier; - import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.vertx.http.runtime.ThreadLocalHandler; import io.vertx.core.Handler; import io.vertx.core.http.HttpHeaders; import io.vertx.ext.web.RoutingContext; @@ -13,14 +10,9 @@ public class SwaggerUiRecorder { public Handler handler(String swaggerUiFinalDestination, String swaggerUiPath) { - Handler handler = new ThreadLocalHandler(new Supplier>() { - @Override - public Handler get() { - return StaticHandler.create().setAllowRootFileSystemAccess(true) - .setWebRoot(swaggerUiFinalDestination) - .setDefaultContentEncoding("UTF-8"); - } - }); + StaticHandler staticHandler = StaticHandler.create().setAllowRootFileSystemAccess(true) + .setWebRoot(swaggerUiFinalDestination) + .setDefaultContentEncoding("UTF-8"); return new Handler() { @Override @@ -36,7 +28,7 @@ public void handle(RoutingContext event) { return; } - handler.handle(event); + staticHandler.handle(event); } }; } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/StaticResourcesProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/StaticResourcesProcessor.java new file mode 100644 index 0000000000000..609e0b659698d --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/StaticResourcesProcessor.java @@ -0,0 +1,127 @@ +package io.quarkus.vertx.http.deployment; + +import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; +import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.deployment.ApplicationArchive; +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.Record; +import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; +import io.quarkus.runtime.util.ClassPathUtils; +import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; +import io.quarkus.vertx.http.runtime.StaticResourcesRecorder; + +/** + * Handles all static file resources found in {@code META-INF/resources} unless the servlet container is present. + */ +public class StaticResourcesProcessor { + + public static final class StaticResourcesBuildItem extends SimpleBuildItem { + + private final Set paths; + + public StaticResourcesBuildItem(Set paths) { + this.paths = paths; + } + + public Set getPaths() { + return paths; + } + + } + + @BuildStep + void collectStaticResources(Capabilities capabilities, ApplicationArchivesBuildItem applicationArchivesBuildItem, + BuildProducer staticResources) throws Exception { + if (capabilities.isPresent(Capability.SERVLET)) { + // Servlet container handles static resources + return; + } + Set paths = getClasspathResources(applicationArchivesBuildItem); + if (!paths.isEmpty()) { + staticResources.produce(new StaticResourcesBuildItem(paths)); + } + } + + @BuildStep + @Record(STATIC_INIT) + public void staticInit(Optional staticResources, + StaticResourcesRecorder recorder) throws Exception { + if (staticResources.isPresent()) { + recorder.staticInit(staticResources.get().getPaths()); + } + } + + @BuildStep + @Record(RUNTIME_INIT) + public void runtimeInit(Optional staticResources, StaticResourcesRecorder recorder, + CoreVertxBuildItem vertx, BeanContainerBuildItem beanContainer, BuildProducer defaultRoutes) + throws Exception { + if (staticResources.isPresent()) { + defaultRoutes.produce(new DefaultRouteBuildItem(recorder.start())); + } + } + + /** + * Find all static file resources that are available from classpath. + * + * @param applicationArchivesBuildItem + * @return the set of static resources + * @throws Exception + */ + private Set getClasspathResources(ApplicationArchivesBuildItem applicationArchivesBuildItem) throws Exception { + Set knownPaths = new HashSet<>(); + for (ApplicationArchive i : applicationArchivesBuildItem.getAllApplicationArchives()) { + Path resource = i.getChildPath(StaticResourcesRecorder.META_INF_RESOURCES); + if (resource != null && Files.exists(resource)) { + collectKnownPaths(resource, knownPaths); + } + } + + ClassPathUtils.consumeAsPaths(StaticResourcesRecorder.META_INF_RESOURCES, resource -> { + collectKnownPaths(resource, knownPaths); + }); + + return knownPaths; + } + + private void collectKnownPaths(Path resource, Set knownPaths) { + try { + Files.walkFileTree(resource, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) + throws IOException { + String file = resource.relativize(p).toString(); + if (file.equals("index.html") || file.equals("index.htm")) { + knownPaths.add("/"); + } + if (!file.startsWith("/")) { + file = "/" + file; + } + // Windows has a backslash + file = file.replace('\\', '/'); + knownPaths.add(file); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} 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 fe889614b01e7..818bc05666dda 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 @@ -135,7 +135,7 @@ ServiceStartBuildItem finalizeRouter( CoreVertxBuildItem core, // Injected to be sure that Vert.x has been produced before calling this method. ExecutorBuildItem executorBuildItem) throws BuildException, IOException { - HttpRemoteDevClientProvider.liveReloadConfig = lrc; + Optional defaultRoute; if (defaultRoutes == null || defaultRoutes.isEmpty()) { defaultRoute = Optional.empty(); @@ -148,6 +148,7 @@ ServiceStartBuildItem finalizeRouter( } } + HttpRemoteDevClientProvider.liveReloadConfig = lrc; GracefulShutdownFilter gracefulShutdownFilter = recorder.createGracefulShutdownHandler(); shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem(gracefulShutdownFilter)); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java index 5af7355c2006b..181e5cbd8d761 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java @@ -9,8 +9,15 @@ public class BasicRoute implements Function { private String path; + private Integer order; + public BasicRoute(String path) { + this(path, null); + } + + public BasicRoute(String path, Integer order) { this.path = path; + this.order = order; } public BasicRoute() { @@ -24,8 +31,20 @@ public void setPath(String path) { this.path = path; } + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + @Override public Route apply(Router router) { - return router.route(path); + Route route = router.route(path); + if (order != null) { + route.order(order); + } + return route; } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java new file mode 100644 index 0000000000000..9fb213e27e2ad --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java @@ -0,0 +1,78 @@ +package io.quarkus.vertx.http.runtime; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.StaticHandler; + +@Recorder +public class StaticResourcesRecorder { + + public static final String META_INF_RESOURCES = "META-INF/resources"; + + private static volatile Set knownPaths; + private static volatile List hotDeploymentResourcePaths; + + public static void setHotDeploymentResources(List resources) { + hotDeploymentResourcePaths = resources; + } + + public void staticInit(Set knownPaths) { + StaticResourcesRecorder.knownPaths = knownPaths; + } + + public Consumer start() { + + List> handlers = new ArrayList<>(); + + if (hotDeploymentResourcePaths != null && !hotDeploymentResourcePaths.isEmpty()) { + for (Path resourcePath : hotDeploymentResourcePaths) { + String root = resourcePath.toAbsolutePath().toString(); + StaticHandler staticHandler = StaticHandler.create(); + staticHandler.setCachingEnabled(false); + staticHandler.setAllowRootFileSystemAccess(true); + staticHandler.setWebRoot(root); + staticHandler.setDefaultContentEncoding("UTF-8"); + handlers.add(event -> { + try { + staticHandler.handle(event); + } catch (Exception e) { + // on Windows, the drive in file path screws up cache lookup + // so just punt to next handler + event.next(); + } + }); + } + } + if (!knownPaths.isEmpty()) { + StaticHandler staticHandler = StaticHandler.create(META_INF_RESOURCES).setDefaultContentEncoding("UTF-8"); + handlers.add(ctx -> { + String rel = ctx.mountPoint() == null ? ctx.normalisedPath() + : ctx.normalisedPath().substring(ctx.mountPoint().length()); + if (knownPaths.contains(rel)) { + staticHandler.handle(ctx); + } else { + ctx.next(); + } + }); + } + + return new Consumer() { + + @Override + public void accept(Route route) { + for (Handler i : handlers) { + route.handler(i); + } + } + }; + } + +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ThreadLocalHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ThreadLocalHandler.java deleted file mode 100644 index e3be0dede558c..0000000000000 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ThreadLocalHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.quarkus.vertx.http.runtime; - -import java.util.function.Supplier; - -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; - -public class ThreadLocalHandler implements Handler { - - private final ThreadLocal> threadLocal; - - public ThreadLocalHandler(Supplier> supplier) { - threadLocal = new ThreadLocal>() { - @Override - protected Handler initialValue() { - return supplier.get(); - } - }; - } - - @Override - public void handle(RoutingContext event) { - threadLocal.get().handle(event); - } -} 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 28e79a142dd0a..5a39a7b39d5e4 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 @@ -105,6 +105,9 @@ public class VertxHttpRecorder { public static final String MAX_REQUEST_SIZE_KEY = "io.quarkus.max-request-size"; + // We do not use Integer.MAX on purpose to allow advanced users to register a route AFTER the default route + public static final int DEFAULT_ROUTE_ORDER = 10_000; + private static final Logger LOGGER = Logger.getLogger(VertxHttpRecorder.class.getName()); private static volatile Handler hotReplacementHandler; @@ -252,7 +255,7 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute } if (defaultRouteHandler != null) { - defaultRouteHandler.accept(router.route().order(10_000)); + defaultRouteHandler.accept(router.route().order(DEFAULT_ROUTE_ORDER)); } container.instance(RouterProducer.class).initialize(router); diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/devmode/ResteasyHotReplacementSetup.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/StaticResourcesHotReplacementSetup.java similarity index 56% rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/devmode/ResteasyHotReplacementSetup.java rename to extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/StaticResourcesHotReplacementSetup.java index 03d6fa0fac145..dd531a7a0520b 100644 --- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/devmode/ResteasyHotReplacementSetup.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/StaticResourcesHotReplacementSetup.java @@ -1,4 +1,4 @@ -package io.quarkus.resteasy.runtime.devmode; +package io.quarkus.vertx.http.runtime.devmode; import java.nio.file.Files; import java.nio.file.Path; @@ -7,22 +7,20 @@ import io.quarkus.dev.spi.HotReplacementContext; import io.quarkus.dev.spi.HotReplacementSetup; -import io.quarkus.resteasy.runtime.standalone.ResteasyStandaloneRecorder; +import io.quarkus.vertx.http.runtime.StaticResourcesRecorder; -public class ResteasyHotReplacementSetup implements HotReplacementSetup { - - public static final String META_INF_RESOURCES = "META-INF/resources"; +public class StaticResourcesHotReplacementSetup implements HotReplacementSetup { @Override public void setupHotDeployment(HotReplacementContext context) { List resources = new ArrayList<>(); for (Path resourceDir : context.getResourcesDir()) { - Path resource = resourceDir.resolve(META_INF_RESOURCES); + Path resource = resourceDir.resolve(StaticResourcesRecorder.META_INF_RESOURCES); if (Files.exists(resource)) { resources.add(resource); } } - ResteasyStandaloneRecorder.setHotDeploymentResources(resources); + StaticResourcesRecorder.setHotDeploymentResources(resources); } @Override @@ -31,6 +29,6 @@ public void handleFailedInitialStart() { @Override public void close() { - ResteasyStandaloneRecorder.setHotDeploymentResources(null); + StaticResourcesRecorder.setHotDeploymentResources(null); } } diff --git a/extensions/vertx-http/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup b/extensions/vertx-http/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup index a145c9cb76597..6d2e0a33a76df 100644 --- a/extensions/vertx-http/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup +++ b/extensions/vertx-http/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup @@ -1 +1,2 @@ io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup +io.quarkus.vertx.http.runtime.devmode.StaticResourcesHotReplacementSetup \ No newline at end of file