Skip to content

Commit

Permalink
Merge pull request #27563 from geoand/#27469
Browse files Browse the repository at this point in the history
Make Kubernetes model for native registration more complete
  • Loading branch information
geoand authored Aug 30, 2022
2 parents 986f82e + 59a8918 commit cfba66c
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.jackson.deployment;

import java.util.List;

import org.jboss.jandex.DotName;

import io.quarkus.builder.item.MultiBuildItem;
Expand All @@ -10,13 +12,22 @@
*/
public final class IgnoreJsonDeserializeClassBuildItem extends MultiBuildItem {

private final DotName dotName;
private final List<DotName> dotNames;

public IgnoreJsonDeserializeClassBuildItem(DotName dotName) {
this.dotName = dotName;
this.dotNames = List.of(dotName);
}

public IgnoreJsonDeserializeClassBuildItem(List<DotName> dotNames) {
this.dotNames = dotNames;
}

@Deprecated(forRemoval = true)
public DotName getDotName() {
return dotName;
return dotNames.size() > 0 ? dotNames.get(0) : null;
}

public List<DotName> getDotNames() {
return dotNames;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void register(

Set<DotName> ignoredDotNames = new HashSet<>();
for (IgnoreJsonDeserializeClassBuildItem ignoreJsonDeserializeClassBuildItem : ignoreJsonDeserializeClassBuildItems) {
ignoredDotNames.add(ignoreJsonDeserializeClassBuildItem.getDotName());
ignoredDotNames.addAll(ignoreJsonDeserializeClassBuildItem.getDotNames());
}

// handle the various @JsonDeserialize cases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ 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());

private static final Predicate<DotName> 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<AdditionalBeanBuildItem> additionalBeanBuildItemBuildItem,
Expand Down Expand Up @@ -116,45 +122,41 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui

Collection<ClassInfo> kubernetesResourceImpls = combinedIndexBuildItem.getIndex()
.getAllKnownImplementors(KUBERNETES_RESOURCE);
Collection<ClassInfo> kubernetesResourceListImpls = combinedIndexBuildItem.getIndex()
.getAllKnownImplementors(KUBERNETES_RESOURCE_LIST);
Collection<ClassInfo> 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<String> withoutFieldsRegistration = new ArrayList<>(kubernetesResourceImpls.size());
List<String> withoutFieldsRegistration = new ArrayList<>(
kubernetesResourceImpls.size() + kubernetesResourceListImpls.size());
List<String> 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<AnnotationInstance> 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<DotName> 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(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());
}

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")));
Expand Down Expand Up @@ -203,6 +205,40 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui
sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.KUBERNETES_CLIENT));
}

private static void populateReflectionRegistrationLists(Collection<ClassInfo> kubernetesResourceImpls,
Set<DotName> watchedClasses,
List<DotName> ignoredJsonDeserializationClasses,
List<String> withoutFieldsRegistration,
List<String> 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<AnnotationInstance> 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<DotName> watchedClasses,
final int expectedGenericTypeCardinality, boolean isTargetClassAnInterface) {
Expand Down

0 comments on commit cfba66c

Please sign in to comment.