Skip to content

Commit

Permalink
Allow to exclude JAX-RS classes with annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
essobedo committed Feb 22, 2021
1 parent b042fca commit ba68e3f
Show file tree
Hide file tree
Showing 12 changed files with 648 additions and 22 deletions.
22 changes: 22 additions & 0 deletions docs/src/main/asciidoc/rest-json.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,28 @@ If set to `true` (default) then a *single instance* of a resource class is creat
If set to `false` then a *new instance* of the resource class is created per each request.
An explicit CDI scope annotation (`@RequestScoped`, `@ApplicationScoped`, etc.) always overrides the default behavior and specifies the lifecycle of resource instances.

== Include/Exclude JAX-RS classes with build time conditions

Quarkus allows to include or exclude root resource, provider and feature classes directly thanks to build time conditions like it is possible with CDI beans.
Indeed, the different JAX-RS classes can be annotated with profile conditions (`@io.quarkus.arc.profile.IfBuildProfile` or `@io.quarkus.arc.profile.UnlessBuildProfile`) and/or with property conditions (`io.quarkus.arc.properties.IfBuildProperty` or `io.quarkus.arc.properties.UnlessBuildProperty`) to indicate to Quarkus at build time under which conditions the JAX-RS classes should be included.

In the following example, Quarkus includes the endpoint `sayHello` if and only if the build profile `app1` has been enabled.

[source,java]
----
@IfBuildProfile("app1")
public class ResourceForApp1Only {
@GET
@Path("sayHello")
public String sayHello() {
return "hello";
}
}
----

Please note that if a JAX-RS Application has been detected and the method `getClasses()` and/or `getSingletons()` has/have been overridden, Quarkus will ignore the build time conditions and consider only what has been defined in the JAX-RS Application.

== Conclusion

Creating JSON REST services with Quarkus is easy as it relies on proven and well known technologies.
Expand Down
22 changes: 22 additions & 0 deletions docs/src/main/asciidoc/resteasy-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1790,3 +1790,25 @@ Or plain text:
<
< {"name":"roquefort"}
----

== Include/Exclude JAX-RS classes with build time conditions

Quarkus allows to include or exclude root resource, provider and feature classes directly thanks to build time conditions like it is possible with CDI beans.
Indeed, the different JAX-RS classes can be annotated with profile conditions (`@io.quarkus.arc.profile.IfBuildProfile` or `@io.quarkus.arc.profile.UnlessBuildProfile`) and/or with property conditions (`io.quarkus.arc.properties.IfBuildProperty` or `io.quarkus.arc.properties.UnlessBuildProperty`) to indicate to Quarkus at build time under which conditions the JAX-RS classes should be included.

In the following example, Quarkus includes the endpoint `sayHello` if and only if the build profile `app1` has been enabled.

[source,java]
----
@IfBuildProfile("app1")
public class ResourceForApp1Only {
@GET
@Path("sayHello")
public String sayHello() {
return "hello";
}
}
----

Please note that if a JAX-RS Application has been detected and the method `getClasses()` and/or `getSingletons()` has/have been overridden, Quarkus will ignore the build time conditions and consider only what has been defined in the JAX-RS Application.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void unlessBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeCon
}

@BuildStep
void ifBuildProperty(BeanArchiveIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
void ifBuildProperty(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
buildProperty(IF_BUILD_PROPERTY, new BiFunction<String, String, Boolean>() {
@Override
public Boolean apply(String stringValue, String expectedStringValue) {
Expand All @@ -82,7 +82,7 @@ public Boolean apply(String stringValue, String expectedStringValue) {
}

@BuildStep
void unlessBuildProperty(BeanArchiveIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
void unlessBuildProperty(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
buildProperty(UNLESS_BUILD_PROPERTY, new BiFunction<String, String, Boolean>() {
@Override
public Boolean apply(String stringValue, String expectedStringValue) {
Expand All @@ -91,7 +91,7 @@ public Boolean apply(String stringValue, String expectedStringValue) {
}, index, producer);
}

void buildProperty(DotName annotationName, BiFunction<String, String, Boolean> testFun, BeanArchiveIndexBuildItem index,
void buildProperty(DotName annotationName, BiFunction<String, String, Boolean> testFun, CombinedIndexBuildItem index,
BuildProducer<BuildTimeConditionBuildItem> producer) {
Config config = ConfigProviderResolver.instance().getConfig();
Collection<AnnotationInstance> annotationInstances = index.getIndex().getAnnotations(annotationName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import javax.ws.rs.RuntimeType;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
Expand All @@ -31,12 +33,14 @@
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BuildTimeConditionBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.util.JandexUtil;
import io.quarkus.resteasy.reactive.common.runtime.JaxRsSecurityConfig;
import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveConfig;
import io.quarkus.resteasy.reactive.spi.AbstractInterceptorBuildItem;
import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem;
import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem;
Expand Down Expand Up @@ -71,9 +75,12 @@ void setUpDenyAllJaxRs(CombinedIndexBuildItem index,

@BuildStep
ApplicationResultBuildItem handleApplication(CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
List<BuildTimeConditionBuildItem> buildTimeConditions,
ResteasyReactiveConfig config) {
ApplicationScanningResult result = ResteasyReactiveScanner
.scanForApplicationClass(combinedIndexBuildItem.getComputingIndex());
.scanForApplicationClass(combinedIndexBuildItem.getComputingIndex(),
config.buildTimeConditionAware ? getExcludedClasses(buildTimeConditions) : Collections.emptySet());
if (result.getSelectedAppClass() != null) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, result.getSelectedAppClass().name().toString()));
}
Expand Down Expand Up @@ -268,4 +275,16 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
}
}

/**
* @param buildTimeConditions the build time conditions from which the excluded classes are extracted.
* @return the set of classes that have been annotated with unsuccessful build time conditions.
*/
private static Set<String> getExcludedClasses(List<BuildTimeConditionBuildItem> buildTimeConditions) {
return buildTimeConditions.stream()
.filter(item -> !item.isEnabled())
.map(BuildTimeConditionBuildItem::getTarget)
.filter(target -> target.kind() == AnnotationTarget.Kind.CLASS)
.map(target -> target.asClass().toString())
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@ public class ResteasyReactiveConfig {
@ConfigItem(defaultValue = "true")
@Experimental("This flag has a high probability of going away in the future")
public boolean defaultProduces;

/**
* Allow to rely on build time conditions to enable or not the JAX-RS resource, provider and feature classes.
*/
@ConfigItem(defaultValue = "true")
public boolean buildTimeConditionAware;
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,7 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem
Map<DotName, String> pathInterfaces = result.getPathInterfaces();

ApplicationScanningResult appResult = applicationResultBuildItem.getResult();
Set<String> allowedClasses = appResult.getAllowedClasses();
Set<String> singletonClasses = appResult.getSingletonClasses();
boolean filterClasses = appResult.isFilterClasses();
Application application = appResult.getApplication();

Map<String, String> existingConverters = new HashMap<>();
Expand Down Expand Up @@ -382,7 +380,7 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an
serverEndpointIndexer = serverEndpointIndexerBuilder.build();

for (ClassInfo i : scannedResources.values()) {
if (filterClasses && !allowedClasses.contains(i.name().toString())) {
if (!appResult.keepClass(i.name().toString())) {
continue;
}
ResourceClass endpoints = serverEndpointIndexer.createEndpoints(i);
Expand Down
Loading

0 comments on commit ba68e3f

Please sign in to comment.