Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Kubernetes model for native registration more complete #27563

Merged
merged 2 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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