Skip to content

Commit

Permalink
Register generic type in JAX-RS return type hierarchy for reflection
Browse files Browse the repository at this point in the history
This is currently done only for the (most common) use case of having
as single generic parameter in type hierarchy

Fixes: #8849
  • Loading branch information
geoand committed Mar 1, 2021
1 parent 13c0cba commit efdecdb
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassFinalFieldsWritablePredicateBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyIgnoreWarningBuildItem;
import io.quarkus.deployment.util.JandexUtil;

public class ReflectiveHierarchyStep {

Expand Down Expand Up @@ -123,17 +124,19 @@ private void addReflectiveHierarchy(CombinedIndexBuildItem combinedIndexBuildIte
return;
}

addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, type.name(),
addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, type.name(), type.name(),
processedReflectiveHierarchies, unindexedClasses,
finalFieldsWritable, reflectiveClass);

for (ClassInfo subclass : combinedIndexBuildItem.getIndex().getAllKnownSubclasses(type.name())) {
addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, subclass.name(),
subclass.name(),
processedReflectiveHierarchies,
unindexedClasses, finalFieldsWritable, reflectiveClass);
}
for (ClassInfo subclass : combinedIndexBuildItem.getIndex().getAllKnownImplementors(type.name())) {
addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, subclass.name(),
subclass.name(),
processedReflectiveHierarchies,
unindexedClasses, finalFieldsWritable, reflectiveClass);
}
Expand All @@ -146,6 +149,7 @@ private void addReflectiveHierarchy(CombinedIndexBuildItem combinedIndexBuildIte
ParameterizedType parameterizedType = (ParameterizedType) type;
if (!reflectiveHierarchyBuildItem.getIgnoreTypePredicate().test(parameterizedType.name())) {
addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, parameterizedType.name(),
parameterizedType.name(),
processedReflectiveHierarchies,
unindexedClasses, finalFieldsWritable, reflectiveClass);
}
Expand All @@ -161,6 +165,7 @@ private void addClassTypeHierarchy(CombinedIndexBuildItem combinedIndexBuildItem
ReflectiveHierarchyBuildItem reflectiveHierarchyBuildItem,
String source,
DotName name,
DotName initialName,
Set<DotName> processedReflectiveHierarchies,
Map<DotName, Set<String>> unindexedClasses,
Predicate<ClassInfo> finalFieldsWritable,
Expand Down Expand Up @@ -198,7 +203,7 @@ private void addClassTypeHierarchy(CombinedIndexBuildItem combinedIndexBuildItem
return;
}

addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, info.superName(),
addClassTypeHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, info.superName(), initialName,
processedReflectiveHierarchies,
unindexedClasses, finalFieldsWritable, reflectiveClass);
for (FieldInfo field : info.fields()) {
Expand All @@ -210,7 +215,24 @@ private void addClassTypeHierarchy(CombinedIndexBuildItem combinedIndexBuildItem
// also skip the outer class elements (unfortunately, we don't have a way to test for synthetic fields in Jandex)
continue;
}
addReflectiveHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, field.type(),
Type fieldType = field.type();
if ((field.type().kind() == Kind.TYPE_VARIABLE) && (info.typeParameters().size() == 1)) {
// handle the common case where the super type has a generic type in the class signature which
// is completely resolved by the sub type
// this could be made to handle more complex cases, but it is unlikely we will have to do so
if (field.type().asTypeVariable().identifier().equals(info.typeParameters().get(0).identifier())) {
try {
List<Type> types = JandexUtil.resolveTypeParameters(initialName, info.name(),
combinedIndexBuildItem.getIndex());
if (types.size() == 1) {
fieldType = types.get(0);
}
} catch (IllegalArgumentException ignored) {

}
}
}
addReflectiveHierarchy(combinedIndexBuildItem, reflectiveHierarchyBuildItem, source, fieldType,
processedReflectiveHierarchies,
unindexedClasses, finalFieldsWritable, reflectiveClass);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.it.resteasy.jackson.generics;

public class BaseClass<T> {

private String baseVariable;
private T data;

public String getBaseVariable() {
return this.baseVariable;
}

public void setBaseVariable(String baseVariable) {
this.baseVariable = baseVariable;
}

public T getData() {
return this.data;
}

public void setData(T data) {
this.data = data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.it.resteasy.jackson.generics;

public class ExtendedClass extends BaseClass<MyData> {

private String extendedVariable;

public String getExtendedVariable() {
return this.extendedVariable;
}

public void setExtendedVariable(String extendedVariable) {
this.extendedVariable = extendedVariable;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.quarkus.it.resteasy.jackson.generics;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/generics")
public class GenericsResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public ExtendedClass testExtendedClass() {
final ExtendedClass c = new ExtendedClass();
c.setBaseVariable("myBaseVariable");
c.setExtendedVariable("myExtendedVariable");
final MyData d = new MyData();
d.setDataVariable("myData");
c.setData(d);
return c;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.it.resteasy.jackson.generics;

public class MyData {

private String dataVariable;

public String getDataVariable() {
return this.dataVariable;
}

public void setDataVariable(String dataVariable) {
this.dataVariable = dataVariable;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.it.resteasy.jackson;

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
public class GenericsResourceIT extends GenericsResourceTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.it.resteasy.jackson;

import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

import io.quarkus.it.resteasy.jackson.generics.ExtendedClass;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class GenericsResourceTest {

@Test
public void testExtendedEndpoint() {
ExtendedClass response = given()
.when().get("/generics")
.then()
.statusCode(200)
.extract().body().as(ExtendedClass.class);
assertEquals("myBaseVariable", response.getBaseVariable());
assertEquals("myExtendedVariable", response.getExtendedVariable());
assertEquals("myData", response.getData().getDataVariable());
}
}

0 comments on commit efdecdb

Please sign in to comment.