Skip to content

Commit

Permalink
Allow Undertow to work with RESTEasy Reactive
Browse files Browse the repository at this point in the history
Note that this integration is different to the integration provided by
resteasy-reactive-servlet or the RESTEasy classic integration. This
allows RESTEasy Reactive to run before Servlet, and delegate any
unmatched requests to the Servlet container. If RESTEasy reactive
matches then Servlet is not involved in the request handling.
  • Loading branch information
stuartwdouglas committed Oct 20, 2021
1 parent a2c81ef commit ace9127
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ public void run() {
};
}

@Override
public void resumeExternalProcessing() {
throw new UnsupportedOperationException("Resuming not supported for Servlet deployments");
}

@Override
public String getRequestHeader(CharSequence name) {
return request.getHeader(name.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ public void serverSerializers(ResteasyReactiveRecorder recorder,
@Record(value = ExecutionTime.STATIC_INIT, useIdentityComparisonForParameters = false)
public void setupDeployment(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
BeanContainerBuildItem beanContainerBuildItem,
Capabilities capabilities,
ResteasyReactiveConfig config,
Optional<ResourceScanningResultBuildItem> resourceScanningResultBuildItem,
ResteasyReactiveRecorder recorder,
Expand Down Expand Up @@ -742,10 +743,18 @@ public void setupDeployment(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
quarkusRestDeploymentInfoBuildItemBuildProducer
.produce(new ResteasyReactiveDeploymentInfoBuildItem(deploymentInfo));

boolean servletPresent = false;
int orderAdd = 1;
if (capabilities.isPresent("io.quarkus.servlet")) {
//if servlet is present we run RR before the default route
//otherwise we run after it
orderAdd = -1;
servletPresent = true;
}
RuntimeValue<Deployment> deployment = recorder.createDeployment(deploymentInfo,
beanContainerBuildItem.getValue(), shutdownContext, vertxConfig,
requestContextFactoryBuildItem.map(RequestContextFactoryBuildItem::getFactory).orElse(null),
initClassFactory, launchModeBuildItem.getLaunchMode());
initClassFactory, launchModeBuildItem.getLaunchMode(), servletPresent);

quarkusRestDeploymentBuildItemBuildProducer
.produce(new ResteasyReactiveDeploymentBuildItem(deployment, deploymentPath));
Expand All @@ -755,7 +764,7 @@ public void setupDeployment(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,

// Exact match for resources matched to the root path
routes.produce(RouteBuildItem.builder()
.orderedRoute(deploymentPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1).handler(handler).build());
.orderedRoute(deploymentPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + orderAdd).handler(handler).build());
String matchPath = deploymentPath;
if (matchPath.endsWith("/")) {
matchPath += "*";
Expand All @@ -764,7 +773,7 @@ public void setupDeployment(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
}
// Match paths that begin with the deployment path
routes.produce(
RouteBuildItem.builder().orderedRoute(matchPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + 1)
RouteBuildItem.builder().orderedRoute(matchPath, VertxHttpRecorder.DEFAULT_ROUTE_ORDER + orderAdd)
.handler(handler).build());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ public RuntimeValue<Deployment> createDeployment(DeploymentInfo info,
ShutdownContext shutdownContext, HttpBuildTimeConfig vertxConfig,
RequestContextFactory contextFactory,
BeanFactory<ResteasyReactiveInitialiser> initClassFactory,
LaunchMode launchMode) {
LaunchMode launchMode, boolean servletPresent) {

if (servletPresent) {
info.setResumeOn404(true);
}

CurrentRequestManager
.setCurrentRequestInstance(new QuarkusCurrentRequest(beanContainer.instance(CurrentVertxRequest.class)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class Deployment {
private final List<ServerRestHandler> preMatchHandlers;
private final List<RequestMapper.RequestPath<RestInitialHandler.InitialMatch>> classMappers;
private final List<RuntimeConfigurableServerRestHandler> runtimeConfigurableServerRestHandlers;
private final boolean resumeOn404;

public Deployment(ExceptionMapping exceptionMapping, ContextResolvers contextResolvers,
ServerSerialisers serialisers,
Expand All @@ -49,7 +50,7 @@ public Deployment(ExceptionMapping exceptionMapping, ContextResolvers contextRes
ThreadSetupAction threadSetupAction, RequestContextFactory requestContextFactory,
List<ServerRestHandler> preMatchHandlers,
List<RequestMapper.RequestPath<RestInitialHandler.InitialMatch>> classMappers,
List<RuntimeConfigurableServerRestHandler> runtimeConfigurableServerRestHandlers) {
List<RuntimeConfigurableServerRestHandler> runtimeConfigurableServerRestHandlers, boolean resumeOn404) {
this.exceptionMapping = exceptionMapping;
this.contextResolvers = contextResolvers;
this.serialisers = serialisers;
Expand All @@ -64,6 +65,7 @@ public Deployment(ExceptionMapping exceptionMapping, ContextResolvers contextRes
this.preMatchHandlers = preMatchHandlers;
this.classMappers = classMappers;
this.runtimeConfigurableServerRestHandlers = runtimeConfigurableServerRestHandlers;
this.resumeOn404 = resumeOn404;
}

public Supplier<Application> getApplicationSupplier() {
Expand Down Expand Up @@ -94,6 +96,10 @@ public EntityWriter getDynamicEntityWriter() {
return dynamicEntityWriter;
}

public boolean isResumeOn404() {
return resumeOn404;
}

/**
* Application path prefix. Must start with "/" and not end with a "/". Cannot be null.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class DeploymentInfo {
private String applicationPath;
private List<HandlerChainCustomizer> globalHandlerCustomers = new ArrayList<>();
private boolean developmentMode;
private boolean resumeOn404;

public ResourceInterceptors getInterceptors() {
return interceptors;
Expand Down Expand Up @@ -177,4 +178,13 @@ public DeploymentInfo setDevelopmentMode(boolean developmentMode) {
this.developmentMode = developmentMode;
return this;
}

public boolean isResumeOn404() {
return resumeOn404;
}

public DeploymentInfo setResumeOn404(boolean resumeOn404) {
this.resumeOn404 = resumeOn404;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,8 @@ private String getResourceLocatorPathParam(String name, PreviousResource previou
return getResourceLocatorPathParam(name, previousResource.prev);
}

public abstract void resumeExternalProcessing();

static class PreviousResource {

public PreviousResource(RuntimeResource locatorTarget, Object locatorPathParamValues, PreviousResource prev) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public BeanFactory.BeanInstance<?> apply(Class<?> aClass) {
.getValue();
Map<String, RequestMapper<RuntimeResource>> mappersByMethod = RuntimeMappingDeployment
.buildClassMapper(perClassMappers);
ClassRoutingHandler classRoutingHandler = new ClassRoutingHandler(mappersByMethod, classTemplateNameCount);
ClassRoutingHandler classRoutingHandler = new ClassRoutingHandler(mappersByMethod, classTemplateNameCount,
info.isResumeOn404());

int maxMethodTemplateNameCount = 0;
for (TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> i : perClassMappers.values()) {
Expand Down Expand Up @@ -206,7 +207,7 @@ public BeanFactory.BeanInstance<?> apply(Class<?> aClass) {
abortHandlingChain.toArray(EMPTY_REST_HANDLER_ARRAY), dynamicEntityWriter,
prefix, paramConverterProviders, configurationImpl, applicationSupplier,
threadSetupAction, requestContextFactory, preMatchHandlers, classMappers,
runtimeConfigurableServerRestHandlers);
runtimeConfigurableServerRestHandlers, info.isResumeOn404());
}

private void addRuntimeConfigurableHandlers(RuntimeResource runtimeResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ public class ClassRoutingHandler implements ServerRestHandler {

private final Map<String, RequestMapper<RuntimeResource>> mappers;
private final int parameterOffset;
final boolean resumeOn404;

public ClassRoutingHandler(Map<String, RequestMapper<RuntimeResource>> mappers, int parameterOffset) {
public ClassRoutingHandler(Map<String, RequestMapper<RuntimeResource>> mappers, int parameterOffset, boolean resumeOn404) {
this.mappers = mappers;
this.parameterOffset = parameterOffset;
this.resumeOn404 = resumeOn404;
}

@Override
Expand Down Expand Up @@ -201,9 +203,13 @@ private MediaType toMediaType(String mediaTypeStr) {
}

private void throwNotFound(ResteasyReactiveRequestContext requestContext) {
// the exception mapper needs access to request scoped beans, so make sure we have the context
requestContext.requireCDIRequestScope();
throw new NotFoundException("Unable to find matching target resource method");
if (resumeOn404) {
requestContext.resumeExternalProcessing();
} else {
// the exception mapper needs access to request scoped beans, so make sure we have the context
requestContext.requireCDIRequestScope();
throw new NotFoundException("Unable to find matching target resource method");
}
}

private String getRemaining(ResteasyReactiveRequestContext requestContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ public class RestInitialHandler implements ServerRestHandler {

final ThreadSetupAction requestContext;
final RequestContextFactory requestContextFactory;
final boolean resumeOn404;

public RestInitialHandler(Deployment deployment) {
this.mappers = new RequestMapper<>(deployment.getClassMappers());
this.deployment = deployment;
this.providers = new ProvidersImpl(deployment);
this.preMappingHandlers = deployment.getPreMatchHandlers();
this.resumeOn404 = deployment.isResumeOn404();
if (preMappingHandlers.isEmpty()) {
initialChain = new ServerRestHandler[] { new MatrixParamHandler(), this };
} else {
Expand All @@ -51,10 +53,14 @@ public void beginProcessing(Object extenalHttpContext) {
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception {
RequestMapper.RequestMatch<InitialMatch> target = mappers.map(requestContext.getPathWithoutPrefix());
if (target == null) {
// the NotFoundExceptionMapper needs access to the headers so we need to activate the scope
requestContext.requireCDIRequestScope();
// we want to engage the NotFoundExceptionMapper when nothing is found
requestContext.handleException(new NotFoundException());
if (resumeOn404) {
requestContext.resumeExternalProcessing();
} else {
// the NotFoundExceptionMapper needs access to the headers so we need to activate the scope
requestContext.requireCDIRequestScope();
// we want to engage the NotFoundExceptionMapper when nothing is found
requestContext.handleException(new NotFoundException());
}
return;
}
requestContext.restart(target.value.handlers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ public void run() {
};
}

@Override
public void resumeExternalProcessing() {
context.next();
}

@Override
public String getRequestHeader(CharSequence name) {
return request.headers().get(name);
Expand Down
23 changes: 22 additions & 1 deletion integration-tests/hibernate-reactive-panache/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jsonb</artifactId>
</dependency>
<!--
Makes sure that undertow does not interfere with RESTEasy Reactive
Undertow is blocking so if it interfered it would break everything
but the RR endpoints should run first
-->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
Expand Down Expand Up @@ -144,7 +153,19 @@
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
Expand Down

0 comments on commit ace9127

Please sign in to comment.