Skip to content

Commit

Permalink
Merge pull request #4814 from mkouba/issue-4787
Browse files Browse the repository at this point in the history
RESTEasy standalone - handle @ApplicationPath value correctly
  • Loading branch information
gsmet authored Oct 25, 2019
2 parents eb50076 + f2c336c commit c170cd8
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,29 @@
import io.quarkus.vertx.core.deployment.InternalWebVertxBuildItem;
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.HttpBuildTimeConfig;
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;
}
}

}

@BuildStep()
Expand All @@ -49,31 +63,40 @@ public void staticInit(ResteasyStandaloneRecorder recorder,
ResteasyDeploymentBuildItem deployment,
ApplicationArchivesBuildItem applicationArchivesBuildItem,
ResteasyInjectionReadyBuildItem resteasyInjectionReady,
HttpBuildTimeConfig httpBuildTimeConfig,
HttpBuildTimeConfig httpConfig,
BuildProducer<ResteasyStandaloneBuildItem> standalone) throws Exception {
if (capabilities.isCapabilityPresent(Capabilities.SERVLET)) {
return;
}

Set<String> knownPaths = getClasspathResources(applicationArchivesBuildItem);
String deploymentRootPath = null;
// The context path + the resources path
String rootPath = httpConfig.rootPath;

if (deployment != null) {
String rootPath = deployment.getRootPath();
if (!httpBuildTimeConfig.rootPath.equals("/")) {
rootPath = httpBuildTimeConfig.rootPath + rootPath;
deploymentRootPath = deployment.getRootPath();
if (rootPath.endsWith("/")) {
if (deploymentRootPath.startsWith("/")) {
rootPath += deploymentRootPath.substring(1, deploymentRootPath.length());
} else {
rootPath += deploymentRootPath;
}
} else if (!deploymentRootPath.equals("/")) {
if (!deploymentRootPath.startsWith("/")) {
rootPath += "/";
}
rootPath += deploymentRootPath;
}
recorder.staticInit(deployment.getDeployment(), rootPath, knownPaths);

} else if (!knownPaths.isEmpty()) {
String rootPath = "/";
if (!httpBuildTimeConfig.rootPath.equals("/")) {
rootPath = httpBuildTimeConfig.rootPath;
}
recorder.staticInit(null, rootPath, knownPaths);
}

if (deployment != null || !knownPaths.isEmpty()) {
standalone.produce(new ResteasyStandaloneBuildItem());
standalone.produce(new ResteasyStandaloneBuildItem(deploymentRootPath));
}

}

/**
Expand Down Expand Up @@ -170,24 +193,43 @@ public void accept(Path path) {
public void boot(ShutdownContextBuildItem shutdown,
ResteasyStandaloneRecorder recorder,
BuildProducer<FeatureBuildItem> feature,
BuildProducer<DefaultRouteBuildItem> routeProducer,
BuildProducer<DefaultRouteBuildItem> defaultRoutes,
BuildProducer<RouteBuildItem> routes,
InternalWebVertxBuildItem vertx,
BeanContainerBuildItem beanContainer,
ResteasyStandaloneBuildItem standalone,
Optional<RequireVirtualHttpBuildItem> requireVirtual) throws Exception {
Optional<RequireVirtualHttpBuildItem> requireVirtual,
HttpBuildTimeConfig httpConfig) throws Exception {

if (standalone == null) {
return;
}
feature.produce(new FeatureBuildItem(FeatureBuildItem.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());
// Exact match for resources matched to the root path
routes.produce(new RouteBuildItem(standalone.deploymentRootPath, handler));
String matchPath = standalone.deploymentRootPath;
if (matchPath.endsWith("/")) {
matchPath += "*";
} else {
matchPath += "/*";
}
// Match paths that begin with the deployment path
routes.produce(new RouteBuildItem(matchPath, handler));
}

boolean isVirtual = requireVirtual.isPresent();
Consumer<Route> ut = recorder.start(vertx.getVertx(),
shutdown,
beanContainer.getValue(),
isVirtual);
isVirtual, isDefaultOrNullDeploymentPath);

routeProducer.produce(new DefaultRouteBuildItem(ut));
defaultRoutes.produce(new DefaultRouteBuildItem(ut));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.quarkus.resteasy.test.root;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

/**
* Test a combination of application path and http root path.
*/
public class ApplicationPathHttpRootTest {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(HelloResource.class, HelloApp.class)
.addAsResource(new StringAsset("quarkus.http.root-path=/foo"), "application.properties"));

@Test
public void testResources() {
// Note that /foo is added automatically by RestAssuredURLManager
RestAssured.when().get("/hello/world").then().body(Matchers.is("hello world"));
RestAssured.when().get("/world").then().statusCode(404);
}

@Path("world")
public static class HelloResource {

@GET
public String hello() {
return "hello world";
}

}

@ApplicationPath("hello")
public static class HelloApp extends Application {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.quarkus.resteasy.test.root;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class ApplicationPathTest {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(HelloResource.class, HelloApp.class));

@Test
public void testResources() {
RestAssured.when().get("/hello/world").then().body(Matchers.is("hello world"));
RestAssured.when().get("/world").then().statusCode(404);
}

@Path("world")
public static class HelloResource {

@GET
public String hello() {
return "hello world";
}

}

@ApplicationPath("hello")
public static class HelloApp extends Application {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.ResteasyDeployment;

import io.netty.buffer.ByteBuf;
Expand All @@ -29,7 +28,6 @@
@Recorder
public class ResteasyStandaloneRecorder {

private static final Logger log = Logger.getLogger("io.quarkus.resteasy");
public static final String META_INF_RESOURCES = "META-INF/resources";

/**
Expand Down Expand Up @@ -90,10 +88,10 @@ public void staticInit(ResteasyDeployment dep, String path, Set<String> known) {
contextPath = path;
}

public Consumer<Route> start(RuntimeValue<Vertx> vertxValue,
public Consumer<Route> start(RuntimeValue<Vertx> vertx,
ShutdownContext shutdown,
BeanContainer beanContainer,
boolean isVirtual) {
boolean isVirtual, boolean isDefaultResourcesPath) {

shutdown.addShutdownTask(new Runnable() {
@Override
Expand All @@ -103,7 +101,6 @@ public void run() {
}
}
});
Vertx vertx = vertxValue.getValue();
useDirect = !isVirtual;
List<Handler<RoutingContext>> handlers = new ArrayList<>();

Expand Down Expand Up @@ -147,10 +144,8 @@ public Handler<RoutingContext> get() {
});
}

if (deployment != null) {
VertxRequestHandler requestHandler = new VertxRequestHandler(vertx, beanContainer, deployment, contextPath,
ALLOCATOR);
handlers.add(requestHandler);
if (deployment != null && isDefaultResourcesPath) {
handlers.add(vertxRequestHandler(vertx, beanContainer));
}
return new Consumer<Route>() {

Expand All @@ -163,4 +158,12 @@ public void accept(Route route) {
};
}

public Handler<RoutingContext> vertxRequestHandler(RuntimeValue<Vertx> vertx,
BeanContainer beanContainer) {
if (deployment != null) {
return new VertxRequestHandler(vertx.getValue(), beanContainer, deployment, contextPath, ALLOCATOR);
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ public class VertxRequestHandler implements Handler<RoutingContext> {

protected final Vertx vertx;
protected final RequestDispatcher dispatcher;
protected final String servletMappingPrefix;
protected final String rootPath;
protected final BufferAllocator allocator;
protected final BeanContainer beanContainer;
protected final CurrentIdentityAssociation association;

public VertxRequestHandler(Vertx vertx,
BeanContainer beanContainer,
ResteasyDeployment deployment,
String servletMappingPrefix,
String rootPath,
BufferAllocator allocator) {
this.vertx = vertx;
this.beanContainer = beanContainer;
this.dispatcher = new RequestDispatcher((SynchronousDispatcher) deployment.getDispatcher(),
deployment.getProviderFactory(), null);
this.servletMappingPrefix = servletMappingPrefix;
this.rootPath = rootPath;
this.allocator = allocator;
Instance<CurrentIdentityAssociation> association = CDI.current().select(CurrentIdentityAssociation.class);
this.association = association.isResolvable() ? association.get() : null;
Expand Down Expand Up @@ -90,7 +90,7 @@ private void dispatch(RoutingContext routingContext, InputStream is, VertxOutput
try {
Context ctx = vertx.getOrCreateContext();
HttpServerRequest request = routingContext.request();
ResteasyUriInfo uriInfo = VertxUtil.extractUriInfo(request, servletMappingPrefix);
ResteasyUriInfo uriInfo = VertxUtil.extractUriInfo(request, rootPath);
ResteasyHttpHeaders headers = VertxUtil.extractHttpHeaders(request);
HttpServerResponse response = request.response();
VertxHttpResponse vertxResponse = new VertxHttpResponse(request, dispatcher.getProviderFactory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public static ResteasyUriInfo extractUriInfo(HttpServerRequest req, String conte
uriString = protocol + "://" + host + uri;
}

// ResteasyUriInfo expects a context path to start with a "/" character
if (!contextPath.startsWith("/")) {
contextPath = "/" + contextPath;
}

return new ResteasyUriInfo(uriString, contextPath);
}

Expand Down

0 comments on commit c170cd8

Please sign in to comment.