diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassBuildItem.java index d71a00b8e0eab..9bdcc9049a814 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassBuildItem.java @@ -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 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; @@ -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 names = new ArrayList<>(); for (Class i : classes) { @@ -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; @@ -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); } /** @@ -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); } /** @@ -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) { @@ -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) { @@ -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; @@ -147,6 +185,10 @@ public boolean isMethods() { return methods; } + public boolean isQueryMethods() { + return queryMethods; + } + public boolean isFields() { return fields; } @@ -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. @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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); } } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageReflectConfigStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageReflectConfigStep.java index cd04106f20330..5800346551893 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageReflectConfigStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageReflectConfigStep.java @@ -40,7 +40,8 @@ void generateReflectConfig(BuildProducer 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) { @@ -51,7 +52,7 @@ void generateReflectConfig(BuildProducer 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[] {})); } @@ -76,30 +77,40 @@ void generateReflectConfig(BuildProducer 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()) { @@ -145,22 +156,28 @@ public void addReflectiveMethod(Map reflectiveClasses, R } public void addReflectiveClass(Map reflectiveClasses, Set 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; } @@ -185,7 +202,9 @@ public void addReflectiveField(Map reflectiveClasses, Re static final class ReflectionInfo { boolean constructors; + boolean queryConstructors; boolean methods; + boolean queryMethods; boolean fields; boolean serialization; boolean unsafeAllocated; @@ -195,15 +214,18 @@ static final class ReflectionInfo { Set 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; }