Skip to content

Commit

Permalink
Merge pull request #42035 from zakkak/2024-07-22-extend-RedlectiveCla…
Browse files Browse the repository at this point in the history
…ssBuildItem

Extend ReflectiveClassBuildItem to support queryOnly option
  • Loading branch information
zakkak committed Jul 23, 2024
2 parents 2279295 + 082269d commit 52130c8
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@
import java.util.List;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.logging.Log;

/**
* Used to register a class for reflection in native mode
*/
public final class ReflectiveClassBuildItem extends MultiBuildItem {

// The names of the classes that should be registered for reflection
private final List<String> className;
private final boolean methods;
private final boolean queryMethods;
private final boolean fields;
private final boolean constructors;
private final boolean queryConstructors;
private final boolean weak;
private final boolean serialization;
private final boolean unsafeAllocated;
Expand All @@ -38,7 +42,8 @@ public static Builder builder(String... classNames) {
return new Builder().className(classNames);
}

private ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean weak, boolean serialization,
private ReflectiveClassBuildItem(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, boolean weak, boolean serialization,
boolean unsafeAllocated, Class<?>... classes) {
List<String> names = new ArrayList<>();
for (Class<?> i : classes) {
Expand All @@ -49,8 +54,24 @@ private ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean
}
this.className = names;
this.methods = methods;
if (methods && queryMethods) {
Log.warnf(
"Both methods and queryMethods are set to true for classes: %s. queryMethods is redundant and will be ignored",
String.join(", ", names));
this.queryMethods = false;
} else {
this.queryMethods = queryMethods;
}
this.fields = fields;
this.constructors = constructors;
if (methods && queryMethods) {
Log.warnf(
"Both constructors and queryConstructors are set to true for classes: %s. queryConstructors is redundant and will be ignored",
String.join(", ", names));
this.queryConstructors = false;
} else {
this.queryConstructors = queryConstructors;
}
this.weak = weak;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
Expand All @@ -74,7 +95,7 @@ public ReflectiveClassBuildItem(boolean methods, boolean fields, Class<?>... cla
*/
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, Class<?>... classes) {
this(constructors, methods, fields, false, false, false, classes);
this(constructors, false, methods, false, fields, false, false, false, classes);
}

/**
Expand All @@ -92,7 +113,7 @@ public ReflectiveClassBuildItem(boolean methods, boolean fields, String... class
*/
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, String... classNames) {
this(constructors, methods, fields, false, false, false, classNames);
this(constructors, false, methods, false, fields, false, false, false, classNames);
}

/**
Expand All @@ -102,7 +123,7 @@ public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean f
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean serialization,
String... classNames) {
this(constructors, methods, fields, false, serialization, false, classNames);
this(constructors, false, methods, false, fields, false, serialization, false, classNames);
}

public static ReflectiveClassBuildItem weakClass(String... classNames) {
Expand All @@ -123,7 +144,8 @@ public static ReflectiveClassBuildItem serializationClass(String... classNames)
return ReflectiveClassBuildItem.builder(classNames).serialization().build();
}

ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean weak, boolean serialization,
ReflectiveClassBuildItem(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, boolean weak, boolean serialization,
boolean unsafeAllocated, String... className) {
for (String i : className) {
if (i == null) {
Expand All @@ -132,8 +154,24 @@ public static ReflectiveClassBuildItem serializationClass(String... classNames)
}
this.className = Arrays.asList(className);
this.methods = methods;
if (methods && queryMethods) {
Log.warnf(
"Both methods and queryMethods are set to true for classes: %s. queryMethods is redundant and will be ignored",
String.join(", ", className));
this.queryMethods = false;
} else {
this.queryMethods = queryMethods;
}
this.fields = fields;
this.constructors = constructors;
if (methods && queryMethods) {
Log.warnf(
"Both constructors and queryConstructors are set to true for classes: %s. queryConstructors is redundant and will be ignored",
String.join(", ", className));
this.queryConstructors = false;
} else {
this.queryConstructors = queryConstructors;
}
this.weak = weak;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
Expand All @@ -147,6 +185,10 @@ public boolean isMethods() {
return methods;
}

public boolean isQueryMethods() {
return queryMethods;
}

public boolean isFields() {
return fields;
}
Expand All @@ -155,6 +197,10 @@ public boolean isConstructors() {
return constructors;
}

public boolean isQueryConstructors() {
return queryConstructors;
}

/**
* @deprecated As of GraalVM 21.2 finalFieldsWritable is no longer needed when registering fields for reflection. This will
* be removed in a future verion of Quarkus.
Expand All @@ -179,7 +225,9 @@ public boolean isUnsafeAllocated() {
public static class Builder {
private String[] className;
private boolean constructors = true;
private boolean queryConstructors;
private boolean methods;
private boolean queryMethods;
private boolean fields;
private boolean weak;
private boolean serialization;
Expand All @@ -193,6 +241,10 @@ public Builder className(String[] className) {
return this;
}

/**
* Configures whether constructors should be registered for reflection (true by default).
* Setting this enables getting all declared constructors for the class as well as invoking them reflectively.
*/
public Builder constructors(boolean constructors) {
this.constructors = constructors;
return this;
Expand All @@ -202,6 +254,23 @@ public Builder constructors() {
return constructors(true);
}

/**
* Configures whether constructors should be registered for reflection, for query purposes only.
* Setting this enables getting all declared constructors for the class but does not allow invoking them reflectively.
*/
public Builder queryConstructors(boolean queryConstructors) {
this.queryConstructors = queryConstructors;
return this;
}

public Builder queryConstructors() {
return queryConstructors(true);
}

/**
* Configures whether methods should be registered for reflection.
* Setting this enables getting all declared methods for the class as well as invoking them reflectively.
*/
public Builder methods(boolean methods) {
this.methods = methods;
return this;
Expand All @@ -211,6 +280,23 @@ public Builder methods() {
return methods(true);
}

/**
* Configures whether methods should be registered for reflection, for query purposes only.
* Setting this enables getting all declared methods for the class but does not allow invoking them reflectively.
*/
public Builder queryMethods(boolean queryMethods) {
this.queryMethods = queryMethods;
return this;
}

public Builder queryMethods() {
return queryMethods(true);
}

/**
* Configures whether fields should be registered for reflection.
* Setting this enables getting all declared fields for the class as well as accessing them reflectively.
*/
public Builder fields(boolean fields) {
this.fields = fields;
return this;
Expand Down Expand Up @@ -238,6 +324,9 @@ public Builder weak() {
return weak(true);
}

/**
* Configures whether serialization support should be enabled for the class.
*/
public Builder serialization(boolean serialization) {
this.serialization = serialization;
return this;
Expand All @@ -247,6 +336,9 @@ public Builder serialization() {
return serialization(true);
}

/**
* Configures whether the class can be allocated in an unsafe manner (through JNI).
*/
public Builder unsafeAllocated(boolean unsafeAllocated) {
this.unsafeAllocated = unsafeAllocated;
return this;
Expand All @@ -257,7 +349,8 @@ public Builder unsafeAllocated() {
}

public ReflectiveClassBuildItem build() {
return new ReflectiveClassBuildItem(constructors, methods, fields, weak, serialization, unsafeAllocated, className);
return new ReflectiveClassBuildItem(constructors, queryConstructors, methods, queryMethods, fields, weak,
serialization, unsafeAllocated, className);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
forcedNonWeakClasses.add(nonWeakReflectiveClassBuildItem.getClassName());
}
for (ReflectiveClassBuildItem i : reflectiveClassBuildItems) {
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, i.isConstructors(), i.isMethods(), i.isFields(),
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, i.isConstructors(), i.isQueryConstructors(),
i.isMethods(), i.isQueryMethods(), i.isFields(),
i.isWeak(), i.isSerialization(), i.isUnsafeAllocated(), i.getClassNames().toArray(new String[0]));
}
for (ReflectiveFieldBuildItem i : reflectiveFields) {
Expand All @@ -51,7 +52,7 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
}

for (ServiceProviderBuildItem i : serviceProviderBuildItems) {
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, true, false, false, false, false, false,
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, true, false, false, false, false, false, false, false,
i.providers().toArray(new String[] {}));
}

Expand All @@ -76,30 +77,40 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
}
if (info.constructors) {
json.put("allDeclaredConstructors", true);
} else if (!info.ctorSet.isEmpty()) {
for (ReflectiveMethodBuildItem ctor : info.ctorSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", ctor.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < ctor.getParams().length; ++i) {
paramsArray.add(ctor.getParams()[i]);
} else {
if (info.queryConstructors) {
json.put("queryAllDeclaredConstructors", true);
}
if (!info.ctorSet.isEmpty()) {
for (ReflectiveMethodBuildItem ctor : info.ctorSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", ctor.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < ctor.getParams().length; ++i) {
paramsArray.add(ctor.getParams()[i]);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
}
if (info.methods) {
json.put("allDeclaredMethods", true);
} else if (!info.methodSet.isEmpty()) {
for (ReflectiveMethodBuildItem method : info.methodSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", method.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < method.getParams().length; ++i) {
paramsArray.add(method.getParams()[i]);
} else {
if (info.queryMethods) {
json.put("queryAllDeclaredMethods", true);
}
if (!info.methodSet.isEmpty()) {
for (ReflectiveMethodBuildItem method : info.methodSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", method.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < method.getParams().length; ++i) {
paramsArray.add(method.getParams()[i]);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
}
if (!methodsArray.isEmpty()) {
Expand Down Expand Up @@ -145,22 +156,28 @@ public void addReflectiveMethod(Map<String, ReflectionInfo> reflectiveClasses, R
}

public void addReflectiveClass(Map<String, ReflectionInfo> reflectiveClasses, Set<String> forcedNonWeakClasses,
boolean constructors, boolean method,
boolean fields, boolean weak, boolean serialization, boolean unsafeAllocated,
boolean constructors, boolean queryConstructors, boolean method,
boolean queryMethods, boolean fields, boolean weak, boolean serialization, boolean unsafeAllocated,
String... className) {
for (String cl : className) {
ReflectionInfo existing = reflectiveClasses.get(cl);
if (existing == null) {
String typeReachable = (!forcedNonWeakClasses.contains(cl) && weak) ? cl : null;
reflectiveClasses.put(cl, new ReflectionInfo(constructors, method, fields,
reflectiveClasses.put(cl, new ReflectionInfo(constructors, queryConstructors, method, queryMethods, fields,
typeReachable, serialization, unsafeAllocated));
} else {
if (constructors) {
existing.constructors = true;
}
if (queryConstructors) {
existing.queryConstructors = true;
}
if (method) {
existing.methods = true;
}
if (queryMethods) {
existing.queryMethods = true;
}
if (fields) {
existing.fields = true;
}
Expand All @@ -185,7 +202,9 @@ public void addReflectiveField(Map<String, ReflectionInfo> reflectiveClasses, Re

static final class ReflectionInfo {
boolean constructors;
boolean queryConstructors;
boolean methods;
boolean queryMethods;
boolean fields;
boolean serialization;
boolean unsafeAllocated;
Expand All @@ -195,15 +214,18 @@ static final class ReflectionInfo {
Set<ReflectiveMethodBuildItem> ctorSet = new HashSet<>();

private ReflectionInfo() {
this(false, false, false, null, false, false);
this(false, false, false, false, false, null, false, false);
}

private ReflectionInfo(boolean constructors, boolean methods, boolean fields, String typeReachable,
private ReflectionInfo(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, String typeReachable,
boolean serialization, boolean unsafeAllocated) {
this.methods = methods;
this.queryMethods = queryMethods;
this.fields = fields;
this.typeReachable = typeReachable;
this.constructors = constructors;
this.queryConstructors = queryConstructors;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
}
Expand Down

0 comments on commit 52130c8

Please sign in to comment.