Skip to content

Commit

Permalink
Merge pull request #10912 from mkouba/move-static-handler-to-vertx-http
Browse files Browse the repository at this point in the history
Handle static resources in vertx-http
  • Loading branch information
stuartwdouglas authored Jul 26, 2020
2 parents e19f10e + 9d408d8 commit 8d892f9
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

}
Expand All @@ -74,7 +56,6 @@ public void staticInit(ResteasyStandaloneRecorder recorder,
return;
}

Set<String> knownPaths = getClasspathResources(applicationArchivesBuildItem);
String deploymentRootPath = null;
// The context path + the resources path
String rootPath = httpConfig.rootPath;
Expand All @@ -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<String> getClasspathResources(ApplicationArchivesBuildItem applicationArchivesBuildItem) throws Exception {
Set<String> 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<String> knownPaths) {
try {
Files.walkFileTree(resource, new SimpleFileVisitor<Path>() {
@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,
Expand All @@ -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<RoutingContext> 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<RoutingContext> 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<Route> ut = recorder.start(vertx.getVertx(),
shutdown,
beanContainer.getValue(),
isVirtual, isDefaultOrNullDeploymentPath, executorBuildItem.getExecutorProxy(), httpConfiguration);

defaultRoutes.produce(new DefaultRouteBuildItem(ut));
recorder.start(shutdown, requireVirtual.isPresent());
}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -70,30 +62,18 @@ public int getBufferSize() {
}
};

private static volatile List<Path> hotDeploymentResourcePaths;

public static void setHotDeploymentResources(List<Path> resources) {
hotDeploymentResourcePaths = resources;
}

private static ResteasyDeployment deployment;
private static Set<String> knownPaths;
private static String contextPath;

public void staticInit(ResteasyDeployment dep, String path, Set<String> known) {
public void staticInit(ResteasyDeployment dep, String path) {
if (dep != null) {
deployment = dep;
deployment.start();
}
knownPaths = known;
contextPath = path;
}

public Consumer<Route> start(Supplier<Vertx> 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
Expand All @@ -104,64 +84,6 @@ public void run() {
}
});
useDirect = !isVirtual;
List<Handler<RoutingContext>> handlers = new ArrayList<>();

if (hotDeploymentResourcePaths != null && !hotDeploymentResourcePaths.isEmpty()) {
for (Path resourcePath : hotDeploymentResourcePaths) {
String root = resourcePath.toAbsolutePath().toString();
ThreadLocalHandler staticHandler = new ThreadLocalHandler(new Supplier<Handler<RoutingContext>>() {
@Override
public Handler<RoutingContext> 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<Handler<RoutingContext>>() {
@Override
public Handler<RoutingContext> 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<Route>() {

@Override
public void accept(Route route) {
for (Handler<RoutingContext> i : handlers) {
route.handler(i);
}
}
};
}

public Handler<RoutingContext> vertxRequestHandler(Supplier<Vertx> vertx,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -45,14 +42,9 @@ public Handler<RoutingContext> schemaHandler() {

public Handler<RoutingContext> uiHandler(String graphqlUiFinalDestination, String graphqlUiPath) {

Handler<RoutingContext> handler = new ThreadLocalHandler(new Supplier<Handler<RoutingContext>>() {
@Override
public Handler<RoutingContext> 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<RoutingContext>() {
@Override
Expand All @@ -68,7 +60,7 @@ public void handle(RoutingContext event) {
return;
}

handler.handle(event);
staticHandler.handle(event);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -26,14 +23,9 @@ public void registerHealthCheckResponseProvider(Class<? extends HealthCheckRespo

public Handler<RoutingContext> uiHandler(String healthUiFinalDestination, String healthUiPath) {

Handler<RoutingContext> handler = new ThreadLocalHandler(new Supplier<Handler<RoutingContext>>() {
@Override
public Handler<RoutingContext> 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<RoutingContext>() {
@Override
Expand All @@ -49,7 +41,7 @@ public void handle(RoutingContext event) {
return;
}

handler.handle(event);
staticHandler.handle(event);
}
};
}
Expand Down
Loading

0 comments on commit 8d892f9

Please sign in to comment.