From 5e676442176a4011b97595fbf0c6a097c5377260 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 29 Aug 2022 10:41:58 +0300 Subject: [PATCH 1/2] Apply minor optimization to KubernetesClientProcessor --- .../client/deployment/KubernetesClientProcessor.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java b/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java index e65bb205057eb..10bf9e433e7cc 100644 --- a/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java +++ b/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java @@ -52,6 +52,7 @@ public class KubernetesClientProcessor { private static final Predicate IS_OKHTTP_CLASS = d -> d.toString().startsWith("okhttp3"); private static final DotName JSON_FORMAT = DotName.createSimple(JsonFormat.class.getName()); + private static final String[] EMPTY_STRINGS_ARRAY = new String[0]; @BuildStep public void registerBeanProducers(BuildProducer additionalBeanBuildItemBuildItem, @@ -148,11 +149,13 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui }); if (!withFieldsRegistration.isEmpty()) { reflectiveClasses.produce(ReflectiveClassBuildItem - .builder(withFieldsRegistration.toArray(new String[0])).weak(true).methods(true).fields(true).build()); + .builder(withFieldsRegistration.toArray(EMPTY_STRINGS_ARRAY)).weak(true).methods(true).fields(true) + .build()); } if (!withoutFieldsRegistration.isEmpty()) { reflectiveClasses.produce(ReflectiveClassBuildItem - .builder(withoutFieldsRegistration.toArray(new String[0])).weak(true).methods(true).fields(false).build()); + .builder(withoutFieldsRegistration.toArray(EMPTY_STRINGS_ARRAY)).weak(true).methods(true).fields(false) + .build()); } // we also ignore some classes that are annotated with @JsonDeserialize that would force the registration of the entire model From 59a89186beedd96dc32a4bfd171fa935be100f75 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 29 Aug 2022 10:59:13 +0300 Subject: [PATCH 2/2] Make Kubernetes model for native registration more complete Fixes: #27469 --- .../IgnoreJsonDeserializeClassBuildItem.java | 17 +++- .../jackson/deployment/JacksonProcessor.java | 2 +- .../deployment/KubernetesClientProcessor.java | 89 +++++++++++++------ 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/IgnoreJsonDeserializeClassBuildItem.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/IgnoreJsonDeserializeClassBuildItem.java index 170df9aca7221..9a62d5bfd17b4 100644 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/IgnoreJsonDeserializeClassBuildItem.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/IgnoreJsonDeserializeClassBuildItem.java @@ -1,5 +1,7 @@ package io.quarkus.jackson.deployment; +import java.util.List; + import org.jboss.jandex.DotName; import io.quarkus.builder.item.MultiBuildItem; @@ -10,13 +12,22 @@ */ public final class IgnoreJsonDeserializeClassBuildItem extends MultiBuildItem { - private final DotName dotName; + private final List dotNames; public IgnoreJsonDeserializeClassBuildItem(DotName dotName) { - this.dotName = dotName; + this.dotNames = List.of(dotName); + } + + public IgnoreJsonDeserializeClassBuildItem(List dotNames) { + this.dotNames = dotNames; } + @Deprecated(forRemoval = true) public DotName getDotName() { - return dotName; + return dotNames.size() > 0 ? dotNames.get(0) : null; + } + + public List getDotNames() { + return dotNames; } } diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index a89809f5ed8b1..5b3c3f5f11570 100644 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -123,7 +123,7 @@ void register( Set ignoredDotNames = new HashSet<>(); for (IgnoreJsonDeserializeClassBuildItem ignoreJsonDeserializeClassBuildItem : ignoreJsonDeserializeClassBuildItems) { - ignoredDotNames.add(ignoreJsonDeserializeClassBuildItem.getDotName()); + ignoredDotNames.addAll(ignoreJsonDeserializeClassBuildItem.getDotNames()); } // handle the various @JsonDeserialize cases diff --git a/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java b/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java index 10bf9e433e7cc..85a765326f28f 100644 --- a/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java +++ b/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientProcessor.java @@ -46,6 +46,11 @@ public class KubernetesClientProcessor { .createSimple("io.fabric8.kubernetes.client.informers.ResourceEventHandler"); private static final DotName KUBERNETES_RESOURCE = DotName .createSimple("io.fabric8.kubernetes.api.model.KubernetesResource"); + private static final DotName KUBERNETES_RESOURCE_LIST = DotName + .createSimple("io.fabric8.kubernetes.api.model.KubernetesResourceList"); + + private static final DotName VISITABLE_BUILDER = DotName + .createSimple("io.fabric8.kubernetes.api.builder.VisitableBuilder"); private static final DotName CUSTOM_RESOURCE = DotName.createSimple("io.fabric8.kubernetes.client.CustomResource"); private static final Logger log = Logger.getLogger(KubernetesClientProcessor.class.getName()); @@ -117,36 +122,28 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui Collection kubernetesResourceImpls = combinedIndexBuildItem.getIndex() .getAllKnownImplementors(KUBERNETES_RESOURCE); + Collection kubernetesResourceListImpls = combinedIndexBuildItem.getIndex() + .getAllKnownImplementors(KUBERNETES_RESOURCE_LIST); + Collection visitableBuilderImpls = combinedIndexBuildItem.getIndex() + .getAllKnownImplementors(VISITABLE_BUILDER); + // default sizes determined experimentally - these are only set in order to prevent continuous expansion of the array list - List withoutFieldsRegistration = new ArrayList<>(kubernetesResourceImpls.size()); + List withoutFieldsRegistration = new ArrayList<>( + kubernetesResourceImpls.size() + kubernetesResourceListImpls.size()); List withFieldsRegistration = new ArrayList<>(2); - kubernetesResourceImpls - .stream() - .peek(c -> { - // we need to make sure that the Jackson extension does not try to fully register the model classes - // since we are going to register them weakly - ignoredJsonDeserializationClasses.produce(new IgnoreJsonDeserializeClassBuildItem(c.name())); - }) - .filter(c -> !watchedClasses.contains(c.name())) - .map(c -> { - boolean registerFields = false; - List jsonFormatInstances = c.annotationsMap().get(JSON_FORMAT); - if (jsonFormatInstances != null) { - for (AnnotationInstance jsonFormatInstance : jsonFormatInstances) { - if (jsonFormatInstance.target().kind() == AnnotationTarget.Kind.FIELD) { - registerFields = true; - break; - } - } - } - return new AbstractMap.SimpleEntry<>(c.name(), registerFields); - }).forEach(e -> { - if (e.getValue()) { - withFieldsRegistration.add(e.getKey().toString()); - } else { - withoutFieldsRegistration.add(e.getKey().toString()); - } - }); + List ignoreJsonDeserialization = new ArrayList<>( + kubernetesResourceImpls.size() + kubernetesResourceListImpls.size()); + + populateReflectionRegistrationLists(kubernetesResourceImpls, watchedClasses, ignoreJsonDeserialization, + withoutFieldsRegistration, + withFieldsRegistration); + populateReflectionRegistrationLists(kubernetesResourceListImpls, watchedClasses, ignoreJsonDeserialization, + withoutFieldsRegistration, + withFieldsRegistration); + populateReflectionRegistrationLists(visitableBuilderImpls, watchedClasses, ignoreJsonDeserialization, + withoutFieldsRegistration, + withFieldsRegistration); + if (!withFieldsRegistration.isEmpty()) { reflectiveClasses.produce(ReflectiveClassBuildItem .builder(withFieldsRegistration.toArray(EMPTY_STRINGS_ARRAY)).weak(true).methods(true).fields(true) @@ -158,6 +155,8 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui .build()); } + ignoredJsonDeserializationClasses.produce(new IgnoreJsonDeserializeClassBuildItem(ignoreJsonDeserialization)); + // we also ignore some classes that are annotated with @JsonDeserialize that would force the registration of the entire model ignoredJsonDeserializationClasses.produce( new IgnoreJsonDeserializeClassBuildItem(DotName.createSimple("io.fabric8.kubernetes.api.model.KubeSchema"))); @@ -206,6 +205,40 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.KUBERNETES_CLIENT)); } + private static void populateReflectionRegistrationLists(Collection kubernetesResourceImpls, + Set watchedClasses, + List ignoredJsonDeserializationClasses, + List withoutFieldsRegistration, + List withFieldsRegistration) { + kubernetesResourceImpls + .stream() + .peek(c -> { + // we need to make sure that the Jackson extension does not try to fully register the model classes + // since we are going to register them weakly + ignoredJsonDeserializationClasses.add(c.name()); + }) + .filter(c -> !watchedClasses.contains(c.name())) + .map(c -> { + boolean registerFields = false; + List jsonFormatInstances = c.annotationsMap().get(JSON_FORMAT); + if (jsonFormatInstances != null) { + for (AnnotationInstance jsonFormatInstance : jsonFormatInstances) { + if (jsonFormatInstance.target().kind() == AnnotationTarget.Kind.FIELD) { + registerFields = true; + break; + } + } + } + return new AbstractMap.SimpleEntry<>(c.name(), registerFields); + }).forEach(e -> { + if (e.getValue()) { + withFieldsRegistration.add(e.getKey().toString()); + } else { + withoutFieldsRegistration.add(e.getKey().toString()); + } + }); + } + private void findWatchedClasses(final DotName implementedOrExtendedClass, final ApplicationIndexBuildItem applicationIndex, final CombinedIndexBuildItem combinedIndexBuildItem, final Set watchedClasses, final int expectedGenericTypeCardinality, boolean isTargetClassAnInterface) {