Skip to content

Commit

Permalink
Merge pull request quarkusio#16793 from essobedo/16754/add-inheritanc…
Browse files Browse the repository at this point in the history
…e-support

Allow to support inheritance in JAX-RS Resource included via Application#getClasses
  • Loading branch information
geoand authored Apr 29, 2021
2 parents 8626b87 + ae36b2e commit 81e8ea3
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -236,7 +237,8 @@ public void build(
final Collection<AnnotationInstance> allPaths;
if (filterClasses) {
allPaths = paths.stream().filter(
annotationInstance -> keepEnclosingClass(allowedClasses, excludedClasses, annotationInstance))
annotationInstance -> keepAnnotation(beanArchiveIndexBuildItem.getIndex(), allowedClasses, excludedClasses,
annotationInstance))
.collect(Collectors.toList());
} else {
allPaths = new ArrayList<>(paths);
Expand Down Expand Up @@ -889,21 +891,32 @@ private static Set<String> getExcludedClasses(List<BuildTimeConditionBuildItem>
}

/**
* @param allowedClasses the classes returned by the methods {@link Application#getClasses()} and
* {@link Application#getSingletons()} to keep.
* @param excludedClasses the classes that have been annotated wih unsuccessful build time conditions and that
* @param index the Jandex index view from which the class information is extracted.
* @param allowedClasses the classes to keep provided by the methods {@link Application#getClasses()} and
* {@link Application#getSingletons()}.
* @param excludedClasses the classes that have been annotated with unsuccessful build time conditions and that
* need to be excluded from the list of paths.
* @param annotationInstance the annotation instance from which the enclosing class will be extracted.
* @return {@code true} if the enclosing class of the annotation is part of the allowed classes if not empty
* or if is not part of the excluded classes, {@code false} otherwise.
* @param annotationInstance the annotation instance to test.
* @return {@code true} if the enclosing class of the annotation is a concrete class and is part of the allowed
* classes, or is an interface and at least one concrete implementation is included, or is an abstract class
* and at least one concrete sub class is included, or is not part of the excluded classes, {@code false} otherwise.
*/
private static boolean keepEnclosingClass(Set<String> allowedClasses, Set<String> excludedClasses,
private static boolean keepAnnotation(IndexView index, Set<String> allowedClasses, Set<String> excludedClasses,
AnnotationInstance annotationInstance) {
final String className = JandexUtil.getEnclosingClass(annotationInstance).toString();
final ClassInfo classInfo = JandexUtil.getEnclosingClass(annotationInstance);
final String className = classInfo.toString();
if (allowedClasses.isEmpty()) {
// No allowed classes have been set, meaning that only excluded classes have been provided.
// Keep the enclosing class only if not excluded
return !excludedClasses.contains(className);
} else if (Modifier.isAbstract(classInfo.flags())) {
// Only keep the annotation if a concrete implementation or a sub class has been included
return (Modifier.isInterface(classInfo.flags()) ? index.getAllKnownImplementors(classInfo.name())
: index.getAllKnownSubclasses(classInfo.name()))
.stream()
.filter(clazz -> !Modifier.isAbstract(clazz.flags()))
.map(Objects::toString)
.anyMatch(allowedClasses::contains);
}
return allowedClasses.contains(className);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ class ApplicationTest {
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(
ResourceTest1.class, ResourceTest2.class, ResponseFilter1.class, ResponseFilter2.class,
IResourceTest.class, ResourceInheritedInterfaceTest.class,
AResourceTest.class, ResourceInheritedClassTest.class,
ResourceTest1.class, ResourceTest2.class,
ResponseFilter1.class, ResponseFilter2.class,
ResponseFilter3.class, ResponseFilter4.class, ResponseFilter5.class, ResponseFilter6.class,
Feature1.class, Feature2.class, DynamicFeature1.class, DynamicFeature2.class,
ExceptionMapper1.class, ExceptionMapper2.class, AppTest.class));
Expand Down Expand Up @@ -76,6 +79,79 @@ void should_not_call_ok_of_resource_2() {
.statusCode(Response.Status.SERVICE_UNAVAILABLE.getStatusCode());
}

@DisplayName("Should access to path inherited from an interface")
@Test
void should_call_inherited_from_interface() {
when()
.get("/rt-i/ok")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.body(Matchers.is("ok-i"));
}

@DisplayName("Should access to path inherited from a class where method is implemented")
@Test
void should_call_inherited_from_class_implemented() {
when()
.get("/rt-a/ok-1")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.body(Matchers.is("ok-a-1"));
}

@DisplayName("Should access to path inherited from a class where method is overridden")
@Test
void should_call_inherited_from_class_overridden() {
when()
.get("/rt-a/ok-2")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.body(Matchers.is("ok-a-2"));
}

@Path("rt-i")
public interface IResourceTest {

@GET
@Path("ok")
String ok();
}

public static class ResourceInheritedInterfaceTest implements IResourceTest {

@Override
public String ok() {
return "ok-i";
}
}

@Path("rt-a")
public abstract static class AResourceTest {

@GET
@Path("ok-1")
public abstract String ok1();

@GET
@Path("ok-2")
public String ok2() {
return "ok-a";
}
}

public static class ResourceInheritedClassTest extends AResourceTest {

@Override
public String ok1() {
return "ok-a-1";
}

@Override
public String ok2() {
return "ok-a-2";
}
}

@Path("rt-1")
public static class ResourceTest1 {

Expand Down Expand Up @@ -224,6 +300,7 @@ public static class AppTest extends Application {
public Set<Class<?>> getClasses() {
return new HashSet<>(
Arrays.asList(
ResourceInheritedInterfaceTest.class, ResourceInheritedClassTest.class,
ResourceTest1.class, Feature1.class, ExceptionMapper1.class));
}

Expand Down

0 comments on commit 81e8ea3

Please sign in to comment.