Skip to content
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
Expand Up @@ -6,6 +6,7 @@

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

Expand Down Expand Up @@ -48,7 +49,7 @@ private ConfigMappingUtils() {
public static void processConfigClasses(
ConfigurationBuildItem configItem,
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
Map<String, GeneratedClassBuildItem> generatedConfigClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
BuildProducer<ConfigClassBuildItem> configClasses,
Expand All @@ -74,41 +75,41 @@ public static void processConfigClasses(
continue;
}
Kind configClassKind = getConfigClassType(instance);
processConfigClass(configClass, configClassKind, combinedIndex, generatedClasses, reflectiveClasses,
processConfigClass(configClass, configClassKind, combinedIndex, generatedConfigClasses, reflectiveClasses,
reflectiveMethods, configClasses, additionalConstrainedClasses);
}
}

public static void processConfigMapping(
ConfigurationBuildItem configItem,
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
Map<String, GeneratedClassBuildItem> generatedConfigClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
BuildProducer<ConfigClassBuildItem> configClasses,
BuildProducer<AdditionalConstrainedClassBuildItem> additionalConstrainedClasses) {
processConfigClasses(configItem, combinedIndex, generatedClasses, reflectiveClasses, reflectiveMethods, configClasses,
additionalConstrainedClasses, CONFIG_MAPPING_NAME);
processConfigClasses(configItem, combinedIndex, generatedConfigClasses, reflectiveClasses, reflectiveMethods,
configClasses, additionalConstrainedClasses, CONFIG_MAPPING_NAME);
}

public static void processExtensionConfigMapping(
ConfigClass configClass,
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
Map<String, GeneratedClassBuildItem> generatedConfigClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
BuildProducer<ConfigClassBuildItem> configClasses,
BuildProducer<AdditionalConstrainedClassBuildItem> additionalConstrainedClasses) {

processConfigClass(configClass, Kind.MAPPING, combinedIndex, generatedClasses, reflectiveClasses,
processConfigClass(configClass, Kind.MAPPING, combinedIndex, generatedConfigClasses, reflectiveClasses,
reflectiveMethods, configClasses, additionalConstrainedClasses);
}

private static void processConfigClass(
ConfigClass configClassWithPrefix,
Kind configClassKind,
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
Map<String, GeneratedClassBuildItem> generatedConfigClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
BuildProducer<ConfigClassBuildItem> configClasses,
Expand All @@ -125,9 +126,9 @@ private static void processConfigClass(
generatedClassesNames.add(mappingMetadata.getClassName());
// This is the generated implementation of the mapping by SmallRye Config.
byte[] classBytes = mappingMetadata.getClassBytes();
generatedClasses.produce(new GeneratedClassBuildItem(isApplicationClass(configClass.getName()),
mappingMetadata.getClassName(),
classBytes));
generatedConfigClasses.put(mappingMetadata.getClassName(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if it's sufficient to check for generatedConfigClasses.put() != null instead of doing a Stream.sorted().count() in AbstractJarBuilder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want the full list of problematic classes, otherwise it makes fixing issues very annoying.

new GeneratedClassBuildItem(isApplicationClass(configClass.getName()), mappingMetadata.getClassName(),
classBytes));
additionalConstrainedClasses.produce(AdditionalConstrainedClassBuildItem.of(mappingMetadata.getClassName(),
classBytes));
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Collectors;

import org.jboss.logging.Logger;

Expand Down Expand Up @@ -68,6 +71,26 @@ public AbstractJarBuilder(CurateOutcomeBuildItem curateOutcome,
this.generatedClasses = generatedClasses;
this.generatedResources = generatedResources;
this.removedArtifactKeys = removedArtifactKeys;

checkConsistency(generatedClasses);
}

private static void checkConsistency(List<GeneratedClassBuildItem> generatedClasses) {
Map<String, Long> generatedClassOccurrences = generatedClasses.stream()
.sorted(Comparator.comparing(GeneratedClassBuildItem::binaryName))
.collect(Collectors.groupingBy(GeneratedClassBuildItem::binaryName, Collectors.counting()));
StringBuilder duplicates = new StringBuilder();
for (Entry<String, Long> generatedClassOccurrence : generatedClassOccurrences.entrySet()) {
if (generatedClassOccurrence.getValue() < 2) {
continue;
}
duplicates.append("- ").append(generatedClassOccurrence.getKey()).append(": ")
.append(generatedClassOccurrence.getValue()).append("\n");
}
if (!duplicates.isEmpty()) {
throw new IllegalStateException(
"Multiple GeneratedClassBuildItem were produced for the same classes:\n\n" + duplicates);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,20 +204,27 @@ void generateMappings(
BuildProducer<ConfigClassBuildItem> configClasses,
BuildProducer<AdditionalConstrainedClassBuildItem> additionalConstrainedClasses) {

processConfigMapping(configItem, combinedIndex, generatedClasses, reflectiveClasses, reflectiveMethods, configClasses,
additionalConstrainedClasses);
Map<String, GeneratedClassBuildItem> generatedConfigClasses = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not very familiar with the code-generation code, so ignore my comment, if it doesn't apply)

Should this be a LinkedHashMap for reproducibility?


processConfigMapping(configItem, combinedIndex, generatedConfigClasses, reflectiveClasses, reflectiveMethods,
configClasses, additionalConstrainedClasses);

List<ConfigClass> buildTimeRunTimeMappings = configItem.getReadResult().getBuildTimeRunTimeMappings();
for (ConfigClass buildTimeRunTimeMapping : buildTimeRunTimeMappings) {
processExtensionConfigMapping(buildTimeRunTimeMapping, combinedIndex, generatedClasses, reflectiveClasses,
processExtensionConfigMapping(buildTimeRunTimeMapping, combinedIndex, generatedConfigClasses, reflectiveClasses,
reflectiveMethods, configClasses, additionalConstrainedClasses);
}

List<ConfigClass> runTimeMappings = configItem.getReadResult().getRunTimeMappings();
for (ConfigClass runTimeMapping : runTimeMappings) {
processExtensionConfigMapping(runTimeMapping, combinedIndex, generatedClasses, reflectiveClasses, reflectiveMethods,
processExtensionConfigMapping(runTimeMapping, combinedIndex, generatedConfigClasses, reflectiveClasses,
reflectiveMethods,
configClasses, additionalConstrainedClasses);
}

for (GeneratedClassBuildItem generatedConfigClass : generatedConfigClasses.values()) {
generatedClasses.produce(generatedConfigClass);
}
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,13 @@ void generateConfigProperties(
BuildProducer<ConfigClassBuildItem> configClasses,
BuildProducer<AdditionalConstrainedClassBuildItem> additionalConstrainedClasses) {

processConfigClasses(configItem, combinedIndex, generatedClasses, reflectiveClasses, reflectiveMethods, configClasses,
additionalConstrainedClasses, MP_CONFIG_PROPERTIES_NAME);
Map<String, GeneratedClassBuildItem> generatedConfigClasses = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not very familiar with the code-generation code, so ignore my comment, if it doesn't apply)

Should this be a LinkedHashMap for reproducibility?

Copy link
Member Author

@gsmet gsmet Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The elements are sorted when necessary i.e. later when assembling the jar.

Build steps are generating classes in parallel so the list is not really ordered.

processConfigClasses(configItem, combinedIndex, generatedConfigClasses, reflectiveClasses, reflectiveMethods,
configClasses, additionalConstrainedClasses, MP_CONFIG_PROPERTIES_NAME);

for (GeneratedClassBuildItem generatedConfigClass : generatedConfigClasses.values()) {
generatedClasses.produce(generatedConfigClass);
}
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import jakarta.ws.rs.Priorities;

Expand All @@ -24,7 +25,6 @@
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
Expand Down Expand Up @@ -82,36 +82,36 @@ AdditionalBeanBuildItem registerTransactionalExecutor() {
}

@BuildStep
void registerRepositories(CombinedIndexBuildItem indexBuildItem, Capabilities capabilities,
void registerRepositories(
CombinedIndexBuildItem indexBuildItem,
BuildProducer<GeneratedBeanBuildItem> implementationsProducer,
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer,
BuildProducer<ResourcePropertiesBuildItem> resourcePropertiesProducer,
BuildProducer<UnremovableBeanBuildItem> unremovableBeansProducer) {
IndexView index = indexBuildItem.getIndex();
EntityClassHelper entityClassHelper = new EntityClassHelper(index);
List<ClassInfo> repositoriesToImplement = getRepositoriesToImplement(index, CRUD_REPOSITORY_INTERFACE,
Set<ClassInfo> repositoriesToImplement = getRepositoriesToImplement(index, CRUD_REPOSITORY_INTERFACE,
LIST_CRUD_REPOSITORY_INTERFACE,
PAGING_AND_SORTING_REPOSITORY_INTERFACE, LIST_PAGING_AND_SORTING_REPOSITORY_INTERFACE,
JPA_REPOSITORY_INTERFACE);

implementResources(capabilities, implementationsProducer, restDataResourceProducer, resourcePropertiesProducer,
implementResources(implementationsProducer, restDataResourceProducer, resourcePropertiesProducer,
unremovableBeansProducer, new RepositoryMethodsImplementor(index, entityClassHelper),
index,
repositoriesToImplement);
index, repositoriesToImplement);
}

/**
* Implement the {@link io.quarkus.rest.data.panache.RestDataResource} interface for each given Spring Data
* repository and register its metadata and properties to be later picked up by the `rest-data-panache` extension.
*/
private void implementResources(Capabilities capabilities,
private void implementResources(
BuildProducer<GeneratedBeanBuildItem> implementationsProducer,
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer,
BuildProducer<ResourcePropertiesBuildItem> resourcePropertiesProducer,
BuildProducer<UnremovableBeanBuildItem> unremovableBeansProducer,
ResourceMethodsImplementor methodsImplementor,
IndexView index,
List<ClassInfo> repositoriesToImplement) {
Set<ClassInfo> repositoriesToImplement) {
ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);
ResourceImplementor resourceImplementor = new ResourceImplementor(methodsImplementor);
EntityClassHelper entityClassHelper = new EntityClassHelper(index);
Expand Down Expand Up @@ -141,8 +141,8 @@ private void implementResources(Capabilities capabilities,
}
}

private List<ClassInfo> getRepositoriesToImplement(IndexView indexView, DotName... repositoryInterfaces) {
List<ClassInfo> result = new LinkedList<>();
private Set<ClassInfo> getRepositoriesToImplement(IndexView indexView, DotName... repositoryInterfaces) {
Set<ClassInfo> result = new LinkedHashSet<>();
for (DotName repositoryInterface : repositoryInterfaces) {
for (ClassInfo classInfo : indexView.getKnownDirectImplementors(repositoryInterface)) {
if (!hasImplementors(indexView, classInfo) && !EXCLUDED_INTERFACES.contains(classInfo.name())) {
Expand Down
Loading