From 9a71acfdd3497824f8f4538fd128b63821fac99b Mon Sep 17 00:00:00 2001 From: Nicolas Filotto Date: Wed, 14 Dec 2022 19:54:48 +0100 Subject: [PATCH] Add conditions to optional classes to register --- .../ReflectiveClassConditionBuildItem.java | 29 ++++++++++ .../steps/NativeImageReflectConfigStep.java | 53 ++++++++++++------- .../client/deployment/KafkaProcessor.java | 6 +++ .../deployment/SmallRyeGraphQLProcessor.java | 11 +++- 4 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassConditionBuildItem.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassConditionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassConditionBuildItem.java new file mode 100644 index 0000000000000..f9a84929ee471 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ReflectiveClassConditionBuildItem.java @@ -0,0 +1,29 @@ +package io.quarkus.deployment.builditem.nativeimage; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * Used to define a condition to register a class for reflection in native mode only when a specific type is reachable + */ +public final class ReflectiveClassConditionBuildItem extends MultiBuildItem { + + private final String className; + private final String typeReachable; + + public ReflectiveClassConditionBuildItem(Class className, String typeReachable) { + this(className.getName(), typeReachable); + } + + public ReflectiveClassConditionBuildItem(String className, String typeReachable) { + this.className = className; + this.typeReachable = typeReachable; + } + + public String getClassName() { + return className; + } + + public String getTypeReachable() { + return typeReachable; + } +} 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 e4db41b619022..cb243ee5d4315 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 @@ -17,6 +17,7 @@ import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ForceNonWeakReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassConditionBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveFieldBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; @@ -29,7 +30,8 @@ void generateReflectConfig(BuildProducer reflectConf List reflectiveFields, List reflectiveClassBuildItems, List nonWeakReflectiveClassBuildItems, - List serviceProviderBuildItems) { + List serviceProviderBuildItems, + List reflectiveClassConditionBuildItems) { final Map reflectiveClasses = new LinkedHashMap<>(); final Set forcedNonWeakClasses = new HashSet<>(); @@ -52,20 +54,29 @@ void generateReflectConfig(BuildProducer reflectConf i.providers().toArray(new String[] {})); } + // Perform this as last step, since it augments the already added reflective classes + for (ReflectiveClassConditionBuildItem i : reflectiveClassConditionBuildItems) { + reflectiveClasses.computeIfPresent(i.getClassName(), (key, value) -> { + value.typeReachable = i.getTypeReachable(); + return value; + }); + } + JsonArrayBuilder root = Json.array(); for (Map.Entry entry : reflectiveClasses.entrySet()) { JsonObjectBuilder json = Json.object(); json.put("name", entry.getKey()); - if (entry.getValue().weak) { - json.put("condition", Json.object().put("typeReachable", entry.getKey())); + ReflectionInfo info = entry.getValue(); + if (info.typeReachable != null) { + json.put("condition", Json.object().put("typeReachable", info.typeReachable)); } - if (entry.getValue().constructors) { + if (info.constructors) { json.put("allDeclaredConstructors", true); - } else if (!entry.getValue().ctorSet.isEmpty()) { + } else if (!info.ctorSet.isEmpty()) { JsonArrayBuilder methodsArray = Json.array(); - for (ReflectiveMethodBuildItem ctor : entry.getValue().ctorSet) { + for (ReflectiveMethodBuildItem ctor : info.ctorSet) { JsonObjectBuilder methodObject = Json.object(); methodObject.put("name", ctor.getName()); JsonArrayBuilder paramsArray = Json.array(); @@ -77,11 +88,11 @@ void generateReflectConfig(BuildProducer reflectConf } json.put("methods", methodsArray); } - if (entry.getValue().methods) { + if (info.methods) { json.put("allDeclaredMethods", true); - } else if (!entry.getValue().methodSet.isEmpty()) { + } else if (!info.methodSet.isEmpty()) { JsonArrayBuilder methodsArray = Json.array(); - for (ReflectiveMethodBuildItem method : entry.getValue().methodSet) { + for (ReflectiveMethodBuildItem method : info.methodSet) { JsonObjectBuilder methodObject = Json.object(); methodObject.put("name", method.getName()); JsonArrayBuilder paramsArray = Json.array(); @@ -93,11 +104,11 @@ void generateReflectConfig(BuildProducer reflectConf } json.put("methods", methodsArray); } - if (entry.getValue().fields) { + if (info.fields) { json.put("allDeclaredFields", true); - } else if (!entry.getValue().fieldSet.isEmpty()) { + } else if (!info.fieldSet.isEmpty()) { JsonArrayBuilder fieldsArray = Json.array(); - for (String fieldName : entry.getValue().fieldSet) { + for (String fieldName : info.fieldSet) { fieldsArray.add(Json.object().put("name", fieldName)); } json.put("fields", fieldsArray); @@ -119,7 +130,7 @@ public void addReflectiveMethod(Map reflectiveClasses, R String cl = methodInfo.getDeclaringClass(); ReflectionInfo existing = reflectiveClasses.get(cl); if (existing == null) { - reflectiveClasses.put(cl, existing = new ReflectionInfo(false, false, false, false, false)); + reflectiveClasses.put(cl, existing = new ReflectionInfo()); } if (methodInfo.getName().equals("")) { existing.ctorSet.add(methodInfo); @@ -135,8 +146,9 @@ public void addReflectiveClass(Map reflectiveClasses, Se 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, - !forcedNonWeakClasses.contains(cl) && weak, serialization)); + typeReachable, serialization)); } else { if (constructors) { existing.constructors = true; @@ -158,7 +170,7 @@ public void addReflectiveField(Map reflectiveClasses, Re String cl = fieldInfo.getDeclaringClass(); ReflectionInfo existing = reflectiveClasses.get(cl); if (existing == null) { - reflectiveClasses.put(cl, existing = new ReflectionInfo(false, false, false, false, false)); + reflectiveClasses.put(cl, existing = new ReflectionInfo()); } existing.fieldSet.add(fieldInfo.getName()); } @@ -167,17 +179,22 @@ static final class ReflectionInfo { boolean constructors; boolean methods; boolean fields; - boolean weak; boolean serialization; + String typeReachable; Set fieldSet = new HashSet<>(); Set methodSet = new HashSet<>(); Set ctorSet = new HashSet<>(); - private ReflectionInfo(boolean constructors, boolean methods, boolean fields, boolean weak, boolean serialization) { + private ReflectionInfo() { + this(false, false, false, null, false); + } + + private ReflectionInfo(boolean constructors, boolean methods, boolean fields, String typeReachable, + boolean serialization) { this.methods = methods; this.fields = fields; + this.typeReachable = typeReachable; this.constructors = constructors; - this.weak = weak; this.serialization = serialization; } } diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java index 053dfc9a61939..e5216776c7ab3 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java @@ -80,6 +80,7 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassConditionBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; @@ -411,6 +412,7 @@ public AdditionalBeanBuildItem runtimeConfig() { @BuildStep public void withSasl(CombinedIndexBuildItem index, BuildProducer reflectiveClass, + BuildProducer reflectiveClassCondition, BuildProducer sslNativeSupport) { reflectiveClass @@ -432,6 +434,10 @@ public void withSasl(CombinedIndexBuildItem index, for (ClassInfo authenticateCallbackHandler : index.getIndex().getAllKnownImplementors(AUTHENTICATE_CALLBACK_HANDLER)) { reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, authenticateCallbackHandler.name().toString())); } + // Add a condition for the optional authenticate callback handler + reflectiveClassCondition.produce(new ReflectiveClassConditionBuildItem( + "org.apache.kafka.common.security.oauthbearer.secured.OAuthBearerValidatorCallbackHandler", + "org.jose4j.keys.resolvers.VerificationKeyResolver")); } private static void collectImplementors(Set set, CombinedIndexBuildItem indexBuildItem, Class cls) { diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java index e6e11bc9419e8..e00a6a75c4d87 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java @@ -48,6 +48,7 @@ import io.quarkus.deployment.builditem.TransformedClassesBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassConditionBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; @@ -74,6 +75,8 @@ import io.smallrye.graphql.cdi.config.ConfigKey; import io.smallrye.graphql.cdi.config.MicroProfileConfig; import io.smallrye.graphql.cdi.producer.GraphQLProducer; +import io.smallrye.graphql.cdi.tracing.TracingService; +import io.smallrye.graphql.cdi.validation.ValidationService; import io.smallrye.graphql.schema.Annotations; import io.smallrye.graphql.schema.SchemaBuilder; import io.smallrye.graphql.schema.model.Argument; @@ -181,13 +184,19 @@ void addDependencies(BuildProducer indexDependency) { } @BuildStep - void registerNativeImageResources(BuildProducer serviceProvider) throws IOException { + void registerNativeImageResources(BuildProducer serviceProvider, + BuildProducer reflectiveClassCondition) throws IOException { // Lookup Service (We use the one from the CDI Module) serviceProvider.produce(ServiceProviderBuildItem.allProvidersFromClassPath(LookupService.class.getName())); // Eventing Service (We use the one from the CDI Module) serviceProvider.produce(ServiceProviderBuildItem.allProvidersFromClassPath(EventingService.class.getName())); + // Add a condition for the optional eventing services + reflectiveClassCondition.produce(new ReflectiveClassConditionBuildItem(TracingService.class, "io.opentracing.Tracer")); + reflectiveClassCondition + .produce(new ReflectiveClassConditionBuildItem(ValidationService.class, "javax.validation.ValidatorFactory")); + // Use MicroProfile Config (We use the one from the CDI Module) serviceProvider.produce(ServiceProviderBuildItem.allProvidersFromClassPath(MicroProfileConfig.class.getName()));