From 5e55d527274d3bf874c8c8f687b9b57538af9c72 Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Wed, 15 Apr 2026 14:31:07 +0200 Subject: [PATCH 01/10] Handle all exporters --- extensions/opentelemetry/deployment/pom.xml | 5 +++++ .../deployment/metrics/GaugeCdiTest.java | 3 ++- ...uredOpenTelemetrySdkBuilderCustomizer.java | 21 ++++++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml index d0a7ed263414e..6e4846b4bd0eb 100644 --- a/extensions/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/deployment/pom.xml @@ -59,6 +59,11 @@ + + io.opentelemetry + opentelemetry-exporter-logging + test + io.quarkus quarkus-junit-internal diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java index 99654f7234891..909a79ec76f22 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java @@ -45,10 +45,11 @@ public class GaugeCdiTest { "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") .add(new StringAsset( "quarkus.otel.metrics.enabled=true\n" + + "quarkus.otel.logs.enabled=true\n" + "quarkus.datasource.db-kind=h2\n" + "quarkus.datasource.jdbc.telemetry=true\n" + "quarkus.otel.traces.exporter=test-span-exporter\n" + - "quarkus.otel.metrics.exporter=in-memory\n" + + "quarkus.otel.metrics.exporter=in-memory,logging\n" + "quarkus.otel.metric.export.interval=300ms\n" + "quarkus.otel.bsp.export.timeout=1s\n" + "quarkus.otel.bsp.schedule.delay=50\n"), diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index c795a03106739..e33c6f21404a7 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -96,18 +97,15 @@ public LogRecordProcessor apply(LogRecordProcessor lrp, ConfigProperties cp) { final class ResourceCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { private final ApplicationConfig appConfig; - private final OTelBuildConfig oTelBuildConfig; private final OTelRuntimeConfig oTelRuntimeConfig; private final Instance delayedAttributes; private final List resources; public ResourceCustomizer(ApplicationConfig appConfig, - OTelBuildConfig oTelBuildConfig, OTelRuntimeConfig oTelRuntimeConfig, @Any Instance delayedAttributes, @All List resources) { this.appConfig = appConfig; - this.oTelBuildConfig = oTelBuildConfig; this.oTelRuntimeConfig = oTelRuntimeConfig; this.delayedAttributes = delayedAttributes; this.resources = resources; @@ -257,23 +255,21 @@ final class MetricProviderCustomizer implements AutoConfiguredOpenTelemetrySdkBu private final OTelBuildConfig oTelBuildConfig; private final OTelRuntimeConfig oTelRuntimeConfig; private final Instance clock; - private final Instance metricExporter; private final Instance managedExecutor; public MetricProviderCustomizer(OTelBuildConfig oTelBuildConfig, OTelRuntimeConfig runtimeConfig, final Instance clock, - final Instance metricExporter, final Instance managedExecutor) { this.oTelBuildConfig = oTelBuildConfig; this.clock = clock; - this.metricExporter = metricExporter; this.managedExecutor = managedExecutor; this.oTelRuntimeConfig = runtimeConfig; } @Override public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { + AtomicReference metricExporterRef = new AtomicReference<>(); builder.addMeterProviderCustomizer( new BiFunction() { @Override @@ -303,15 +299,20 @@ public SdkMeterProviderBuilder apply(SdkMeterProviderBuilder meterProviderBuilde return meterProviderBuilder; } }) + .addMetricExporterCustomizer(new BiFunction() { + @Override + public MetricExporter apply(MetricExporter metricExporter, ConfigProperties configProperties) { + metricExporterRef.set(metricExporter); + return metricExporter; + } + }) .addMetricReaderCustomizer(new BiFunction() { @Override public MetricReader apply(MetricReader metricReader, ConfigProperties configProperties) { // Replace the provided metric reader because it uses an unmanaged executor // with a null classloader - if (metricReader instanceof PeriodicMetricReader && - metricExporter.isResolvable() && - managedExecutor.isResolvable()) { - return PeriodicMetricReader.builder(metricExporter.get()) + if (metricReader instanceof PeriodicMetricReader && managedExecutor.isResolvable()) { + return PeriodicMetricReader.builder(metricExporterRef.get()) .setInterval(oTelRuntimeConfig.metric().exportInterval()) .setExecutor(managedExecutor.get()) .build(); From 032385a6049f0b60b0f7bdf3867faf5245fb081d Mon Sep 17 00:00:00 2001 From: Luca Molteni Date: Wed, 22 Apr 2026 16:29:11 +0200 Subject: [PATCH 02/10] Add JpaJandexScavenger unit tests and enum persistence test Two changes: 1. Add JpaJandexScavengerTest with comprehensive unit tests for entity discovery, hierarchy, embeddables, converters, listeners, XML mappings (ORM and HBM), and enum/java type collection. Includes a few new XML test resources (orm-test-discovery.xml for XML-only entity discovery, empty orm.xml for implicit auto-discovery testing, orm-invalid-nonannotated.xml for invalid mapping regression test). Exposes JpaJandexScavenger.Collector as package-private with a getter so tests can verify enumTypes and javaTypes. 2. Add EnumPersistenceTest in the hibernate-orm extension to verify that entities with enum fields are correctly persisted and retrieved. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../orm/deployment/JpaJandexScavenger.java | 9 +- .../deployment/JpaJandexScavengerTest.java | 632 ++++++++++++++++++ .../orm/enums/EnumPersistenceTest.java | 38 ++ .../hibernate/orm/enums/MyEntityWithEnum.java | 47 ++ .../quarkus/hibernate/orm/enums/Status.java | 6 + .../resources/META-INF/orm-test-discovery.xml | 23 + .../src/test/resources/META-INF/orm.xml | 7 + .../resources/orm-invalid-nonannotated.xml | 19 + 8 files changed, 779 insertions(+), 2 deletions(-) create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java create mode 100644 extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm-test-discovery.xml create mode 100644 extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm.xml create mode 100644 extensions/hibernate-orm/deployment/src/test/resources/orm-invalid-nonannotated.xml diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java index 314152e85c395..3ab9f4a36cba7 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java @@ -64,6 +64,11 @@ public final class JpaJandexScavenger { private final List persistenceUnitContributions; private final IndexView index; private final Set ignorableNonIndexedClasses; + private Collector collector; + + Collector getCollector() { + return collector; + } JpaJandexScavenger(BuildProducer reflectiveClass, BuildProducer hotDeploymentWatchedFiles, @@ -78,7 +83,7 @@ public final class JpaJandexScavenger { } public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildException { - Collector collector = new Collector(); + this.collector = new Collector(); for (DotName packageAnnotation : ClassNames.PACKAGE_ANNOTATIONS) { enlistJPAModelAnnotatedPackages(collector, packageAnnotation); @@ -625,7 +630,7 @@ private static boolean isInJavaPackage(DotName classDotName) { return false; } - private static class Collector { + static class Collector { final Set packages = new HashSet<>(); final Set entityTypes = new HashSet<>(); final Set potentialCdiBeanTypes = new HashSet<>(); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java new file mode 100644 index 0000000000000..7e031edc53497 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java @@ -0,0 +1,632 @@ +package io.quarkus.hibernate.orm.deployment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PrePersist; + +import org.hibernate.boot.models.internal.OrmAnnotationHelper; +import org.jboss.jandex.Index; +import org.jboss.jandex.Indexer; +import org.junit.jupiter.api.Test; + +import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.hibernate.orm.packages.ParentEntity; +import io.quarkus.hibernate.orm.xml.hbm.NonAnnotatedComponent; +import io.quarkus.hibernate.orm.xml.hbm.NonAnnotatedComponentUsingEntity; +import io.quarkus.hibernate.orm.xml.hbm.NonAnnotatedEntity; +import io.quarkus.hibernate.orm.xml.orm.AnnotatedEntity; +import io.quarkus.hibernate.orm.xml.orm.OtherNonAnnotatedEntity; +import io.quarkus.runtime.configuration.ConfigurationException; + +public class JpaJandexScavengerTest { + + @MappedSuperclass + static class BaseMappedSuperclass { + @Id + long id; + } + + @Entity + static class SimpleEntity extends BaseMappedSuperclass { + String name; + } + + @Embeddable + static class SimpleEmbeddable { + String street; + } + + enum MyStatus { + ACTIVE, + INACTIVE + } + + @Entity + static class EntityWithEmbedded { + @Id + long id; + @Embedded + SimpleEmbeddable address; + } + + @Entity + static class EntityWithEmbeddedId { + @EmbeddedId + SimpleEmbeddable compositeId; + } + + @Entity + static class EntityWithElementCollection { + @Id + long id; + @ElementCollection + List addresses; + } + + @Entity + static class EntityWithEnum { + @Id + long id; + MyStatus status; + } + + static class MyIdClass { + long part1; + long part2; + } + + @Entity + @IdClass(MyIdClass.class) + static class EntityWithIdClass { + @Id + long part1; + @Id + long part2; + } + + static class MyListener { + @PrePersist + void onPrePersist(Object entity) { + } + } + + @Entity + @EntityListeners(MyListener.class) + static class EntityWithListener { + @Id + long id; + } + + @Entity + static class ChildEntity extends SimpleEntity { + String extra; + } + + // Class used as @Embedded but missing @Embeddable annotation + static class NotAnEmbeddable { + String value; + } + + @Entity + static class EntityWithMissingEmbeddable { + @Id + long id; + @Embedded + NotAnEmbeddable broken; + } + + @Entity + static class SerializableEntity implements Serializable { + @Id + long id; + String name; + } + + // Non-annotated classes — only defined as JPA types via orm.xml + static class XmlOnlyEntity { + long id; + String name; + } + + static class XmlOnlyMappedSuperclass { + String superField; + } + + static class XmlOnlyEmbeddable { + String value; + } + + // Converter and related classes for testing @Converter(autoApply = true) + static class MyData { + String value; + + MyData() { + } + + MyData(String value) { + this.value = value; + } + } + + @Converter(autoApply = true) + static class MyDataConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(MyData attribute) { + return attribute == null ? null : attribute.value; + } + + @Override + public MyData convertToEntityAttribute(String dbData) { + return dbData == null ? null : new MyData(dbData); + } + } + + @Entity + static class EntityWithConvertedField { + @Id + long id; + MyData myData; + } + + @Test + void entityAndHierarchyDiscovery() throws Exception { + Index index = buildIndex(SimpleEntity.class, BaseMappedSuperclass.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(SimpleEntity.class); + r.entityClassesDoNotHave(BaseMappedSuperclass.class); + r.managedClassesHave(SimpleEntity.class, BaseMappedSuperclass.class); + } + + @Test + void embeddedFieldDiscovery() throws Exception { + Index index = buildIndex(EntityWithEmbedded.class, SimpleEmbeddable.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithEmbedded.class); + r.entityClassesDoNotHave(SimpleEmbeddable.class); + r.managedClassesHave(SimpleEmbeddable.class); + } + + @Test + void embeddedIdOnMethodDiscovery() throws Exception { + Index index = buildIndex(EntityWithEmbeddedId.class, SimpleEmbeddable.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithEmbeddedId.class); + r.entityClassesDoNotHave(SimpleEmbeddable.class); + r.managedClassesHave(SimpleEmbeddable.class); + } + + @Test + void elementCollectionDiscovery() throws Exception { + Index index = buildIndex(EntityWithElementCollection.class, SimpleEmbeddable.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithElementCollection.class); + r.entityClassesDoNotHave(SimpleEmbeddable.class); + r.managedClassesHave(SimpleEmbeddable.class); + } + + @Test + void enumFieldDetection() throws Exception { + Index index = buildIndex(EntityWithEnum.class, MyStatus.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithEnum.class); + r.entityClassesDoNotHave(MyStatus.class); + r.managedClassesDoNotHave(MyStatus.class); + } + + @Test + void entityInheritanceDiscovery() throws Exception { + Index index = buildIndex(ChildEntity.class, SimpleEntity.class, BaseMappedSuperclass.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(SimpleEntity.class, ChildEntity.class); + r.entityClassesDoNotHave(BaseMappedSuperclass.class); + r.managedClassesHave(SimpleEntity.class, ChildEntity.class, BaseMappedSuperclass.class); + } + + @Test + void entityListenerDiscovery() throws Exception { + Index index = buildIndex(EntityWithListener.class, MyListener.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithListener.class); + r.potentialCdiBeanClassNamesHave(MyListener.class); + } + + @Test + void idClassDiscovery() throws Exception { + Index index = buildIndex(EntityWithIdClass.class, MyIdClass.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithIdClass.class); + r.entityClassesDoNotHave(MyIdClass.class); + } + + @Test + void idClassIsManagedClass() throws Exception { + Index index = buildIndex(EntityWithIdClass.class, MyIdClass.class); + + ScavengerResult r = runScavenger(index); + r.managedClassesHave(MyIdClass.class); + } + + @Test + void xmlOnlyEntityDiscovery() throws Exception { + Index index = buildIndex(XmlOnlyEntity.class, XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-test-discovery.xml"); + r.entityClassesHave(XmlOnlyEntity.class); + r.entityClassesDoNotHave(XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); + r.managedClassesHave(XmlOnlyEntity.class, XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); + } + + @Test + void parseHbmTest() throws Exception { + Index index = buildIndex(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml"); + r.entityClassesHave(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); + } + + @Test + void unindexedSuperclassThrowsException() throws Exception { + Index index = buildIndex(SimpleEntity.class); + + assertThatThrownBy(() -> runScavenger(index)) + .isInstanceOf(ConfigurationException.class) + .hasMessage(String.format(Locale.ROOT, """ + Unable to properly register the hierarchy of the following JPA classes \ + as they are not in the Jandex index: + \t- %s + Consider adding them to the index either by creating a Jandex index \ + for your dependency via the Maven plugin, an empty META-INF/beans.xml \ + or quarkus.index-dependency properties.""", BaseMappedSuperclass.class.getName())); + } + + @Test + void unindexedSuperclassIgnoredWhenInIgnorableSet() throws Exception { + Index index = buildIndex(SimpleEntity.class); + Set ignorable = Set.of(BaseMappedSuperclass.class.getName()); + + ScavengerResult r = runScavenger(index, ignorable); + r.entityClassesHave(SimpleEntity.class); + } + + @Test + void embeddedFieldWithoutEmbeddableAnnotationThrowsException() throws Exception { + Index index = buildIndex(EntityWithMissingEmbeddable.class, NotAnEmbeddable.class); + + assertThatThrownBy(() -> runScavenger(index)) + .hasMessageContaining("must be annotated with @Embeddable"); + } + + @Test + void hbmSimpleEntityDiscovery() throws Exception { + Index index = buildIndex(NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml"); + r.entityClassesHave(NonAnnotatedEntity.class); + r.managedClassesHave(NonAnnotatedEntity.class); + } + + @Test + void hbmFilterDefEntityDiscovery() throws Exception { + Index index = buildIndex(NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml"); + r.entityClassesHave(NonAnnotatedEntity.class); + r.managedClassesHave(NonAnnotatedEntity.class); + } + + @Test + void ormSimpleEntityDiscovery() throws Exception { + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); + r.entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + r.managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + } + + @Test + void ormPackageEntityDiscovery() throws Exception { + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml"); + r.entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class); + r.managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class); + } + + @Test + void converterAutoApplyDiscovery() throws Exception { + Index index = buildIndex(EntityWithConvertedField.class, MyDataConverter.class, MyData.class); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(EntityWithConvertedField.class); + r.potentialCdiBeanClassNamesHave(MyDataConverter.class); + } + + @Test + void converterInAllModelClasses() throws Exception { + Index index = buildIndex(EntityWithConvertedField.class, MyDataConverter.class, MyData.class); + + ScavengerResult r = runScavenger(index); + r.allModelClassNamesHave(MyDataConverter.class); + } + + @Test + void packageLevelAnnotationDiscovery() throws Exception { + Index index = buildIndex(ParentEntity.class, + Class.forName("io.quarkus.hibernate.orm.packages.package-info")); + + ScavengerResult r = runScavenger(index); + r.entityClassesHave(ParentEntity.class); + r.allModelPackageNamesHave("io.quarkus.hibernate.orm.packages"); + } + + @Test + void hbmSimpleParseXmlPu() throws Exception { + Index index = buildIndex(NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void hbmFilterDefParseXmlPu() throws Exception { + Index index = buildIndex(NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void hbmComponentParseXmlPu() throws Exception { + Index index = buildIndex(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void ormOverrideParseXmlPu() throws Exception { + Index index = buildIndex(AnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-override.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void ormSimpleParseXmlPu() throws Exception { + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void ormPackageParseXmlPu() throws Exception { + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml"); + r.xmlMappingsHavePU("default"); + } + + @Test + void xmlMappingInvalidSilentlyAccepted() throws Exception { + // JpaJandexScavenger only uses XML to extract class names and never validates + // attribute members, so an orm.xml referencing non-existent properties + // (e.g. propertythatdoesnotexist1) is silently accepted. + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + + runScavenger(index, Collections.emptySet(), "orm-invalid-nonannotated.xml"); + } + + @Test + void enumFieldInEnumTypes() throws Exception { + Index index = buildIndex(EntityWithEnum.class, MyStatus.class); + + ScavengerResult r = runScavenger(index); + r.enumTypesHave(MyStatus.class); + } + + @Test + void javaPackageInterfaceInJavaTypes() throws Exception { + Index index = buildIndex(SerializableEntity.class); + + ScavengerResult r = runScavenger(index); + r.javaTypesHave(Serializable.class); + } + + @Test + void noFileMappingShouldNotDiscoverEntitiesOrEnums() throws Exception { + Index index = buildIndex(AnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "no-file"); + r.entityClassesHave(AnnotatedEntity.class); + } + + @Test + void noFileMappingShouldNotDiscoverEntities() throws Exception { + Index index = buildIndex(NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "no-file"); + r.entityClassesDoNotHave(NonAnnotatedEntity.class); + r.managedClassesDoNotHave(NonAnnotatedEntity.class); + } + + @Test + void xmlMappingFilesRegisteredForHotDeployment() throws Exception { + Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + + ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); + r.hotDeploymentWatchedFilesHave("META-INF/orm-simple.xml"); + } + + @Test + void implicitOrmXmlDiscovery() throws Exception { + Index index = buildIndex(SimpleEntity.class, BaseMappedSuperclass.class); + + ScavengerResult r = runScavenger(index); + r.xmlMappingsHavePU("default"); + } + + private ScavengerResult runScavenger(Index index) throws Exception { + return runScavenger(index, Collections.emptySet()); + } + + private ScavengerResult runScavenger(Index index, Set ignorableNonIndexedClasses, + String... explicitlyListedMappingFiles) throws Exception { + List contributions = new ArrayList<>(); + if (explicitlyListedMappingFiles.length > 0) { + contributions.add(new JpaModelPersistenceUnitContributionBuildItem( + "default", null, Collections.emptySet(), Arrays.asList(explicitlyListedMappingFiles))); + } else { + contributions.add(new JpaModelPersistenceUnitContributionBuildItem( + "default", null, Collections.emptySet(), Collections.emptyList())); + } + + List watchedFiles = new ArrayList<>(); + JpaJandexScavenger scavenger = new JpaJandexScavenger( + new ArrayList()::add, + watchedFiles::add, + contributions, + index, + ignorableNonIndexedClasses); + + JpaModelBuildItem result = scavenger.discoverModelAndRegisterForReflection(); + JpaJandexScavenger.Collector collector = scavenger.getCollector(); + return new ScavengerResult(result, collector.enumTypes, collector.javaTypes, + watchedFiles.stream().map(HotDeploymentWatchedFileBuildItem::getLocation).toList()); + } + + private Index buildIndex(Class... classes) throws Exception { + Indexer indexer = new Indexer(); + OrmAnnotationHelper.forEachOrmAnnotation(descriptor -> { + try { + indexer.indexClass(descriptor.getAnnotationType()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + indexer.indexClass(Deprecated.class); + for (Class clazz : classes) { + indexer.indexClass(clazz); + } + return indexer.complete(); + } + + private record ScavengerResult(JpaModelBuildItem result, Set enumTypes, Set javaTypes, + List hotDeploymentWatchedFiles) { + + private ScavengerResult assertHas(Set actual, Class... classes) { + for (Class clazz : classes) { + assertThat(actual).contains(clazz.getName()); + } + return this; + } + + private ScavengerResult assertDoesNotHave(Set actual, Class... classes) { + for (Class clazz : classes) { + assertThat(actual).doesNotContain(clazz.getName()); + } + return this; + } + + ScavengerResult entityClassesHave(Class... classes) { + return assertHas(result.getEntityClassNames(), classes); + } + + ScavengerResult entityClassesDoNotHave(Class... classes) { + return assertDoesNotHave(result.getEntityClassNames(), classes); + } + + ScavengerResult managedClassesHave(Class... classes) { + return assertHas(result.getManagedClassNames(), classes); + } + + ScavengerResult managedClassesDoNotHave(Class... classes) { + return assertDoesNotHave(result.getManagedClassNames(), classes); + } + + ScavengerResult potentialCdiBeanClassNamesHave(Class... classes) { + Set beanClassNames = result.getPotentialCdiBeanClassNames().stream() + .map(dn -> dn.toString()) + .collect(Collectors.toSet()); + for (Class clazz : classes) { + assertThat(beanClassNames).contains(clazz.getName()); + } + return this; + } + + ScavengerResult allModelPackageNamesHave(String... packageNames) { + for (String pkg : packageNames) { + assertThat(result.getAllModelPackageNames()).contains(pkg); + } + return this; + } + + ScavengerResult allModelClassNamesHave(Class... classes) { + for (Class clazz : classes) { + assertThat(result.getAllModelClassNames()).contains(clazz.getName()); + } + return this; + } + + ScavengerResult xmlMappingsHavePU(String puName) { + assertThat(result.getXmlMappings(puName)).isNotEmpty(); + return this; + } + + ScavengerResult enumTypesHave(Class... classes) { + for (Class clazz : classes) { + assertThat(enumTypes).contains(clazz.getName()); + } + return this; + } + + ScavengerResult javaTypesHave(Class... classes) { + for (Class clazz : classes) { + assertThat(javaTypes).contains(clazz.getName()); + } + return this; + } + + ScavengerResult hotDeploymentWatchedFilesHave(String... fileNames) { + for (String fileName : fileNames) { + assertThat(hotDeploymentWatchedFiles).contains(fileName); + } + return this; + } + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java new file mode 100644 index 0000000000000..3211aa6a9db4c --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java @@ -0,0 +1,38 @@ +package io.quarkus.hibernate.orm.enums; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusExtensionTest; + +public class EnumPersistenceTest { + + @RegisterExtension + static QuarkusExtensionTest runner = new QuarkusExtensionTest() + .withApplicationRoot((jar) -> jar + .addClass(MyEntityWithEnum.class) + .addClass(Status.class)) + .withConfigurationResource("application.properties"); + + @Inject + EntityManager entityManager; + + @Test + @Transactional + public void enumFieldPersistedAndRetrieved() { + MyEntityWithEnum entity = new MyEntityWithEnum("Gizmo", Status.LIVING); + entityManager.persist(entity); + entityManager.flush(); + entityManager.clear(); + + MyEntityWithEnum retrieved = entityManager.find(MyEntityWithEnum.class, entity.getId()); + assertThat(retrieved.getStatus()).isEqualTo(Status.LIVING); + assertThat(retrieved.getName()).isEqualTo("Gizmo"); + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java new file mode 100644 index 0000000000000..f7f0e73694f5d --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java @@ -0,0 +1,47 @@ +package io.quarkus.hibernate.orm.enums; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class MyEntityWithEnum { + + private long id; + private String name; + private Status status; + + public MyEntityWithEnum() { + } + + public MyEntityWithEnum(String name, Status status) { + this.name = name; + this.status = status; + } + + @Id + @GeneratedValue + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java new file mode 100644 index 0000000000000..f3a06b6237866 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java @@ -0,0 +1,6 @@ +package io.quarkus.hibernate.orm.enums; + +public enum Status { + LIVING, + DECEASED +} diff --git a/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm-test-discovery.xml b/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm-test-discovery.xml new file mode 100644 index 0000000000000..18e3240b753f0 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm-test-discovery.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm.xml b/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm.xml new file mode 100644 index 0000000000000..8e5ca6054b8d5 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/resources/META-INF/orm.xml @@ -0,0 +1,7 @@ + + + + diff --git a/extensions/hibernate-orm/deployment/src/test/resources/orm-invalid-nonannotated.xml b/extensions/hibernate-orm/deployment/src/test/resources/orm-invalid-nonannotated.xml new file mode 100644 index 0000000000000..ee60cf6357ebc --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/resources/orm-invalid-nonannotated.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + From 9f49f337c7e66b4f0086497115fe3de28e79093e Mon Sep 17 00:00:00 2001 From: brunobat Date: Mon, 23 Mar 2026 17:38:31 +0000 Subject: [PATCH 03/10] Fix the build --- tcks/microprofile-opentelemetry/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcks/microprofile-opentelemetry/pom.xml b/tcks/microprofile-opentelemetry/pom.xml index 10179b7a5e925..40246e8af5f1c 100644 --- a/tcks/microprofile-opentelemetry/pom.xml +++ b/tcks/microprofile-opentelemetry/pom.xml @@ -77,7 +77,7 @@ none true logging - 1000 + PT1S true ${project.build.directory}/out.log ${project.build.directory}/out.log From 3f37805ce4e763fc59d4a8dbb2511b517cf924bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mal=C3=A9=C5=99?= Date: Wed, 22 Apr 2026 15:13:39 +0200 Subject: [PATCH 04/10] =?UTF-8?q?Review=20for=20the=20OCP=20guide=20Removi?= =?UTF-8?q?ng=20obsolete=20section=20about=20backwards=20compatibility=20a?= =?UTF-8?q?dded=20in=202020=20Signed-off-by:=20Michal=20Mal=C3=A9=C5=99=20?= =?UTF-8?q??= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deploying-to-openshift-docker-howto.adoc | 77 ++++++++++--------- .../deploying-to-openshift-native-howto.adoc | 16 ++-- .../deploying-to-openshift-s2i-howto.adoc | 53 +++++++------ .../main/asciidoc/deploying-to-openshift.adoc | 34 +------- 4 files changed, 81 insertions(+), 99 deletions(-) diff --git a/docs/src/main/asciidoc/deploying-to-openshift-docker-howto.adoc b/docs/src/main/asciidoc/deploying-to-openshift-docker-howto.adoc index 9ae93d7f38131..9ca893c85f584 100644 --- a/docs/src/main/asciidoc/deploying-to-openshift-docker-howto.adoc +++ b/docs/src/main/asciidoc/deploying-to-openshift-docker-howto.adoc @@ -8,19 +8,19 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::_attributes.adoc[] :diataxis-type: howto :categories: cloud, native -:summary: This guide describes how to build and deploy a Quarkus application on {openshift} by using the Docker build strategy. +:summary: This guide describes how to build and deploy a {project-name} application on {openshift} by using the Docker build strategy. :topics: devops,kubernetes,openshift,cloud,deployment :extensions: io.quarkus:quarkus-openshift -As an application developer, you can deploy your applications to {openshift} by using the Docker build strategy as a deployment option. +As an application developer, you can deploy your applications to {openshift-long} by using the Docker build strategy as a deployment option. -This stategy builds the artifacts outside the {openshift} cluster, locally or in a CI environment, and provides them to the {openshift} build system together with a Dockerfile. +This strategy builds the artifacts outside the {openshift} cluster, either locally or in a CI environment, and provides them to the {openshift} build system together with a Dockerfile. The artifacts include JAR files or a native executable. -The {openshift} cluster builds the container and provides it as an image stream. +The {openshift} cluster builds the container image and provides it as an image stream. This functionality is provided by the `quarkus-openshift` extension. -If you want to use a custom Dockerfile, add the file to the `src/main/docker` directory or any location inside the module. -Additionally, set the path to your Dockerfile by using the `quarkus.openshift.jvm-dockerfile` property. +If you want to use a custom Dockerfile, add the file to the `src/main/docker` directory or another location inside the module. +Set the path to your Dockerfile with the `quarkus.openshift.jvm-dockerfile` property. == Prerequisites @@ -33,42 +33,42 @@ Additionally, set the path to your Dockerfile by using the `quarkus.openshift.jv == Procedure -. Set the Docker build strategy in your `application.properties` configuration file: +. Set the Docker build strategy in your `application.properties` file: + -[source, properties] +[source,properties] ---- quarkus.openshift.build-strategy=docker ---- -. Optional: Set the following properties in the `application.properties` file, based on your environment: -** If you are using an untrusted certificate, enable certificate trust for the `KubernetesClient`: +. Optional: Complete one or more of the following configuration steps in the `application.properties` file: ++ +* If you are using an untrusted certificate, enable certificate trust for the `KubernetesClient`: + [source,properties] ---- quarkus.kubernetes-client.trust-certs=true ---- -** To expose the service and create an {openshift} route, set the following property: +* To expose the service and create an {openshift} route, set the following property: + [source,properties] ---- quarkus.openshift.route.expose=true ---- -** To use a custom Dockerfile instead of the pregenerated Dockerfiles, set the path to your Dockerfile: +* To use a custom Dockerfile instead of the pre-generated Dockerfiles, set the path to your Dockerfile: + -[source,properties,subs="attributes+,+quotes"] +[source,properties] ---- quarkus.openshift.jvm-dockerfile= ---- + -For example, to specify a custom Dockerfile named `Dockerfile.custom-jvm`: +For example, to specify a custom Dockerfile named `Dockerfile.custom-jvm`, set the following property: + [source,properties] ---- quarkus.openshift.jvm-dockerfile=src/main/resources/Dockerfile.custom-jvm ---- - . Package and deploy your application to the current {openshift} project: + -[source,shell,subs="attributes+,+quotes"] +[source,shell] ---- ./mvnw clean package -Dquarkus.openshift.deploy=true ---- @@ -79,28 +79,27 @@ The following verification steps use the `openshift-helloworld` example applicat . Display the list of pods associated with your current OpenShift project: + -[source,shell,subs="+quotes",options="nowrap"] +[source,shell,options="nowrap"] ---- oc get pods ---- + -[source,shell,subs="+quotes",options="nowrap"] +[source,text,options="nowrap"] ---- NAME READY STATUS RESTARTS AGE openshift-helloworld-1-build 0/1 Completed 0 11m openshift-helloworld-1-deploy 0/1 Completed 0 10m openshift-helloworld-1-gzzrx 1/1 Running 0 10m ---- - -. To get the log output for your application's pod, use the `oc logs -f` command with its name. -The following example uses the `openshift-helloworld-1-gzzrx` pod name, which corresponds to the latest pod prefixed with the name of your application: +. To get the log output from your application's pod, run the `oc logs -f` command with the pod name. +The following example uses the `openshift-helloworld-1-gzzrx` pod name, which corresponds to the latest pod prefixed with the application name: + -[source,shell,subs="+quotes",options="nowrap"] +[source,shell,options="nowrap"] ---- -oc logs -f _openshift-helloworld-1-gzzrx_ +oc logs -f openshift-helloworld-1-gzzrx ---- + -[source,shell,subs=attributes+] +[source,text,subs="attributes+"] ---- Starting the Java application using /opt/jboss/container/java/run/run-java.sh ... INFO exec -a "java" java -Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -XX:MaxRAMPercentage=50.0 -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/quarkus-run.jar @@ -112,29 +111,27 @@ __ ____ __ _____ ___ __ ____ ______ 2024-09-17 10:23:25,281 INFO [io.quarkus] (main) Profile prod activated. 2024-09-17 10:23:25,281 INFO [io.quarkus] (main) Installed features: [cdi, kubernetes, rest, smallrye-context-propagation, vertx] ---- - -. Get a list of services: +. Display the list of services: + -[source,shell,subs="+quotes",options="nowrap"] +[source,shell,options="nowrap"] ---- oc get svc ---- + -[source,shell,subs="+quotes",options="nowrap"] +[source,text,options="nowrap"] ---- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE openshift-helloworld ClusterIP 172.30.64.57 80/TCP 14m ---- - . Get a URL to test your application. -To do so, ensure you have exposed an {openshift} route by setting the `quarkus.openshift.route.expose=true` property in the `application.properties` file before building the application. +To do so, ensure that you exposed an {openshift} route by setting the `quarkus.openshift.route.expose=true` property in the `application.properties` file before building the application: + -[source,shell,subs="+quotes",options="nowrap"] +[source,shell,options="nowrap"] ---- oc get routes ---- + -[source,shell,subs="+quotes",options="nowrap"] +[source,text,options="nowrap"] ---- NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD openshift-helloworld openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com openshift-helloworld http None @@ -142,15 +139,19 @@ openshift-helloworld openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1 + [NOTE] ==== -Be aware that the route is now listening on port 80 and is no longer on port 8080. +Be aware that the route listens on port 80 and no longer listens on port 8080. ==== + -You can test the application demonstrated in this example with a web browser or a terminal by using `curl` and the complete URL output from `oc get routes`, that is, `\http://openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com`. +You can test the application in this example with a web browser or from a terminal. +Use the complete URL from `oc get routes`, for example `http://openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com`. ++ +For example: + -For example: `curl \http://openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com`. +[source,shell,options="nowrap"] +---- +curl http://openshift-helloworld-username-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com +---- == References -* xref:deploying-to-openshift.adoc[Deploying {project-name} applications to {openshift}] - - +* xref:deploying-to-openshift.adoc[Deploying {project-name} applications to {openshift}] \ No newline at end of file diff --git a/docs/src/main/asciidoc/deploying-to-openshift-native-howto.adoc b/docs/src/main/asciidoc/deploying-to-openshift-native-howto.adoc index b9d25b3bf4006..8ac306bb586b6 100644 --- a/docs/src/main/asciidoc/deploying-to-openshift-native-howto.adoc +++ b/docs/src/main/asciidoc/deploying-to-openshift-native-howto.adoc @@ -12,14 +12,15 @@ include::_attributes.adoc[] :topics: devops,kubernetes,openshift,cloud,deployment :extensions: io.quarkus:quarkus-openshift -You can deploy your native {project-name} applications to {openshift} compiled to native executables by using the Docker build strategy. +You can deploy your {project-name} applications compiled to native executables to {openshift-long} by using the Docker build strategy. -You must create a native executable for your application that targets a supported operating system and match the architecture. +You must create a native executable for your application that targets a supported operating system and matches the architecture. This means, if you are building on Windows, you create a native Linux executable by using a container runtime, for example, Docker or Podman. Your Quarkus project includes pregenerated Dockerfiles with instructions. If you want to use a custom Dockerfile, add the file to the `src/main/docker` directory or any location inside the module. -Additionally, if you want to have multiple Docker files and switch between them, set the path to your preferred Dockerfile by using the `quarkus.openshift.native-dockerfile` property. + +Additionally, if you want to have multiple Dockerfiles and switch between them, set the path to your preferred Dockerfile by using the `quarkus.openshift.native-dockerfile` property. [NOTE] ==== @@ -37,7 +38,7 @@ This guide describes this strategy by using a Quarkus project with Maven as the . Set the Docker build strategy in your `application.properties` configuration file: + -[source, properties] +[source,properties] ---- quarkus.openshift.build-strategy=docker ---- @@ -48,6 +49,7 @@ quarkus.openshift.build-strategy=docker quarkus.native.container-build=true ---- . Optional: Set the following properties in the `application.properties` file based on your environment: ++ ** If you are using an untrusted certificate, enable certificate trust for the `KubernetesClient`: + [source,properties] @@ -66,6 +68,7 @@ quarkus.openshift.route.expose=true ---- quarkus.openshift.native-dockerfile= ---- ++ For example, to specify a custom Dockerfile named `Dockerfile.custom-native`: + [source,properties] @@ -74,7 +77,7 @@ quarkus.openshift.native-dockerfile=src/main/docker/Dockerfile.custom-native ---- ** Specify the container engine: -*** To build a native executable with Podman: +*** To build a native executable with Podman: + [source,properties] ---- @@ -89,7 +92,7 @@ quarkus.native.container-runtime=docker . Finally, build the native executable, package, and deploy your application to {openshift}: + -[source,shell,subs="attributes+,+quotes"] +[source,shell] ---- ./mvnw clean package -Pnative -Dquarkus.openshift.deploy=true ---- @@ -120,4 +123,3 @@ oc logs -f ____ == References * xref:deploying-to-openshift.adoc[Deploying {project-name} applications to {openshift}] - diff --git a/docs/src/main/asciidoc/deploying-to-openshift-s2i-howto.adoc b/docs/src/main/asciidoc/deploying-to-openshift-s2i-howto.adoc index 347562ebf0c2b..ead11551e969e 100644 --- a/docs/src/main/asciidoc/deploying-to-openshift-s2i-howto.adoc +++ b/docs/src/main/asciidoc/deploying-to-openshift-s2i-howto.adoc @@ -35,15 +35,13 @@ You can deploy {project-name} applications to {openshift} with Java {jdk-version == Procedure -. Open the `pom.xml` file, and set the Java version. +. Open the `pom.xml` file, and set the Java version, where `${java.version}` is {jdk-version-all}. + [source,xml,subs=attributes+] ---- -${java.version} -${java.version} +${java.version} ---- -where ${java.version} is {jdk-version-all}. -+ + . Package your application, by entering the following command: + [source,shell] @@ -66,22 +64,22 @@ JAVA_APP_JAR=/deployments/quarkus-run.jar . Import the supported {openshift} image. + -Java {jdk-version-earliest}: +* Java {jdk-version-earliest}: + -[source,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc import-image {name-image-ubi9-open-jdk-17-short} --from={name-image-ubi9-open-jdk-17} --confirm ---- -Java {jdk-version-other}: +* Java {jdk-version-other}: + -[source,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc import-image {name-image-ubi9-open-jdk-21-short} --from={name-image-ubi9-open-jdk-21} --confirm ---- + -Java {jdk-version-latest}: +* Java {jdk-version-latest}: + -[source,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc import-image {name-image-ubi9-open-jdk-25-short} --from={name-image-ubi9-open-jdk-25} --confirm ---- @@ -100,32 +98,39 @@ For information about this image, see link:https://catalog.redhat.com/en/softwar . Build the project, create the application, and deploy the {openshift} service. + -Java {jdk-version-earliest}: +* Java {jdk-version-earliest}: + -[source,xml,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc new-app registry.access.redhat.com/ubi9/openjdk-17~ --name= ---- -Java {jdk-version-other}: +* Java {jdk-version-other}: + -[source,xml,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc new-app registry.access.redhat.com/ubi9/openjdk-21~ --name= ---- + -Java {jdk-version-latest}: +* Java {jdk-version-latest}: + -[source,xml,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc new-app registry.access.redhat.com/ubi9/openjdk-25~ --name= ---- + -* Replace `` with the path of the Git repository that hosts your Quarkus project. -For example, for Java {jdk-version-other}: `oc new-app registry.access.redhat.com/ubi9/openjdk-21~https://github.com/johndoe/code-with-quarkus.git --name=code-with-quarkus`. +.. Replace `` with the path of the Git repository that hosts your Quarkus project. ++ +For example, for Java {jdk-version-other}: ++ +[source,terminal,subs="attributes+",options="nowrap"] +---- +oc new-app registry.access.redhat.com/ubi9/openjdk-21~https://github.com/johndoe/code-with-quarkus.git --name=code-with-quarkus +---- +{nbsp} + If you do not have SSH keys configured for the Git repository, when specifying the Git path, use the HTTPS URL instead of the SSH URL. -* Replace `` with the name of your application. +.. Replace `` with the name of your application. + [NOTE] ==== @@ -134,14 +139,14 @@ If you are deploying on IBM Z infrastructure, enter `oc new-app ubi9/openjdk-21~ . To deploy an updated version of the project, push changes to the Git repository, and then run: + -[source,xml,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+",options="nowrap"] ---- oc start-build ---- + . To expose a route to the application, run the following command: + -[source,shell,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+"] ---- oc expose svc ---- @@ -150,13 +155,13 @@ oc expose svc . List the pods associated with your current {openshift} project: + -[source,shell,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+"] ---- oc get pods ---- . To get the log output for your application's pod, run the following command, replacing `` with the name of the latest pod prefixed by your application name: + -[source,shell,subs="attributes+,+quotes"] +[source,terminal,subs="attributes+"] ---- oc logs -f ---- diff --git a/docs/src/main/asciidoc/deploying-to-openshift.adoc b/docs/src/main/asciidoc/deploying-to-openshift.adoc index 84465274c121e..5b583531c557a 100644 --- a/docs/src/main/asciidoc/deploying-to-openshift.adoc +++ b/docs/src/main/asciidoc/deploying-to-openshift.adoc @@ -85,7 +85,6 @@ However, if you want to continue to use `DeploymentConfig`, it is still possible . To add the `quarkus-openshift` extension to your project, use one of the following methods: * Configure the `pom.xml` file: + -.pom.xml [source,xml,subs="+quotes,attributes+"] ---- @@ -160,14 +159,14 @@ oc login oc project -q ---- . Use one of the following steps to go to the required {openshift} project: -.. If the project already exists, switch to the project: +* If the project already exists, switch to the project: + [source,shell] ---- oc project ---- + -.. If the project does not exist, create a new project: +* If the project does not exist, create a new project: + [source,shell] ---- @@ -180,7 +179,7 @@ You can build and deploy by using any of the following deployment options: * xref:deploying-to-openshift-howto.adoc[With a single step] * xref:deploying-to-openshift-docker-howto.adoc[By using a Docker build strategy] -* xref:deploying-to-openshift-S2I-howto.adoc[By using a Source-to-Image (S2I) strategy] +* xref:deploying-to-openshift-s2i-howto.adoc[By using a Source-to-Image (S2I) strategy] * xref:deploying-to-openshift-native-howto.adoc[Compiled to native executables] ifndef::no-configuring-application-manually[] @@ -203,7 +202,7 @@ For example: [source,properties] ---- -quarkus.openshift.base-jvm-image=image-registry.openshift-image-registry.svc:5000/some-project/openjdk-11:17.1.16. +quarkus.openshift.base-jvm-image=image-registry.openshift-image-registry.svc:5000/some-project/openjdk-17:17.1.16 ---- [NOTE] @@ -494,31 +493,6 @@ You can fix the issue before you deploy your application to your cluster, where Similarly, two redundant definitions, for example, defining an injection from the same secret twice, does not cause an issue but reports a warning to inform you that you might not have intended to duplicate that definition. -[[env-vars-backwards]] -===== Backwards compatibility - -Previous versions of the OpenShift extension supported a different syntax to add environment variables. -The older syntax is still supported but is deprecated, and it is advised that you migrate to the new syntax. - -.Old syntax versus new syntax -|==== -| |Old | New | -| Plain variable |`quarkus.openshift.env-vars.my-env-var.value=foobar` | `quarkus.openshift.env.vars.my-env-var=foobar` | -| From field |`quarkus.openshift.env-vars.my-env-var.field=foobar` | `quarkus.openshift.env.fields.my-env-var=foobar` | -| All from `ConfigMap` |`quarkus.openshift.env-vars.xxx.configmap=foobar` | `quarkus.openshift.env.configmaps=foobar` | -| All from `Secret` |`quarkus.openshift.env-vars.xxx.secret=foobar` | `quarkus.openshift.env.secrets=foobar` | -| From one `Secret` field |`quarkus.openshift.env-vars.foo.secret=foobar` | `quarkus.openshift.env.mapping.foo.from-secret=foobar` | -| |`quarkus.openshift.env-vars.foo.value=field` | `quarkus.openshift.env.mapping.foo.with-key=field` | -| From one `ConfigMap` field |`quarkus.openshift.env-vars.foo.configmap=foobar` | `quarkus.openshift.env.mapping.foo.from-configmap=foobar` | -| |`quarkus.openshift.env-vars.foo.value=field` | `quarkus.openshift.env.mapping.foo.with-key=field` | -|==== - -[NOTE] -==== - If you redefine the same variable by using the new syntax while keeping the old syntax, only the new version is kept, and a warning will be issued to alert you of the problem. - For example, if you define both `quarkus.openshift.env-vars.my-env-var.value=foobar` and `quarkus.openshift.env.vars.my-env-var=newValue`, the extension generates an environment variable `MY_ENV_VAR=newValue` and issues a warning. -==== - === Mounting volumes The OpenShift extension allows you to configure both volumes and mounts for the application. From 5390769c04a0ee98444493021f2327cb4d8a14fe Mon Sep 17 00:00:00 2001 From: Teymur Babayev Date: Tue, 21 Apr 2026 15:21:23 +0200 Subject: [PATCH 05/10] Handle edge-case with kapt plugin missing generated sources Since the generated sources are no longer registered as input dirs for main source set, annotation processing (in this case, mapstruct via kapt) couldn't see those sources. This led to compilation failures. This is not a legitimate fix, rather a band-aid. Addresses #50486. --- .../java/io/quarkus/gradle/QuarkusPlugin.java | 31 +++++++++---- .../build.gradle.kts | 46 +++++++++++++++++++ .../gradle.properties | 5 ++ .../settings.gradle.kts | 22 +++++++++ .../src/main/kotlin/org/acme/DemoMapper.kt | 9 ++++ .../main/kotlin/org/acme/HelloRequestModel.kt | 3 ++ .../src/main/proto/helloworld.proto | 16 +++++++ .../quarkus/gradle/KaptGrpcMapStructTest.java | 19 ++++++++ 8 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/build.gradle.kts create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/gradle.properties create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/settings.gradle.kts create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/DemoMapper.kt create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/HelloRequestModel.kt create mode 100644 integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/proto/helloworld.proto create mode 100644 integration-tests/gradle/src/test/java/io/quarkus/gradle/KaptGrpcMapStructTest.java diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java index 59292e7c29c40..0d4e44ed45096 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java @@ -533,9 +533,10 @@ public boolean isSatisfiedBy(Task t) { project.getPlugins().withId("org.jetbrains.kotlin.jvm", plugin -> { quarkusDev.configure(task -> task.shouldPropagateJavaCompilerArgs(false)); + SourceSetContainer ssc = project.getExtensions().getByType(SourceSetContainer.class); + final SourceSet generatedSourceSet = ssc.getByName(QuarkusGenerateCode.QUARKUS_GENERATED_SOURCES); + final SourceSet generatedTestSourceSet = ssc.getByName(QuarkusGenerateCode.QUARKUS_TEST_GENERATED_SOURCES); tasks.named("compileKotlin", task -> { - final SourceSet generatedSourceSet = project.getExtensions().getByType(SourceSetContainer.class) - .getByName(QuarkusGenerateCode.QUARKUS_GENERATED_SOURCES); addCodeGenSourceDirs(task, generatedSourceSet); task.dependsOn(quarkusGenerateCode); task.mustRunAfter(quarkusGenerateCodeDev); @@ -548,18 +549,32 @@ public boolean isSatisfiedBy(Task t) { } }); tasks.named("compileTestKotlin", task -> { - final SourceSet generatedSourceSet = project.getExtensions().getByType(SourceSetContainer.class) - .getByName(QuarkusGenerateCode.QUARKUS_TEST_GENERATED_SOURCES); - addCodeGenSourceDirs(task, generatedSourceSet); + addCodeGenSourceDirs(task, generatedTestSourceSet); task.dependsOn(quarkusGenerateCodeTests); - if (tasks.contains(new NamedImpl(generatedSourceSet.getCompileJavaTaskName()))) { - task.mustRunAfter(tasks.named(generatedSourceSet.getCompileJavaTaskName())); + if (tasks.contains(new NamedImpl(generatedTestSourceSet.getCompileJavaTaskName()))) { + task.mustRunAfter(tasks.named(generatedTestSourceSet.getCompileJavaTaskName())); } - final String generatedSourcesCompileTaskName = generatedSourceSet.getCompileTaskName("kotlin"); + final String generatedSourcesCompileTaskName = generatedTestSourceSet.getCompileTaskName("kotlin"); if (tasks.contains(new NamedImpl(generatedSourcesCompileTaskName))) { task.mustRunAfter(tasks.named(generatedSourcesCompileTaskName)); } }); + // kapt stub tasks don't inherit the source dirs injected into compileKotlin, so wire them explicitly. + // TODO: perhaps we should prioritize IDE devX and proper wiring into the main SS, and invert + // the handling here: kapt should be the base case, and KSP should be the edge-case. + // See issues [1] and [2] for full context. + // * [1] https://github.com/quarkusio/quarkus/issues/29698 + // * [2] https://github.com/quarkusio/quarkus/issues/50486 + project.getPlugins().withId("org.jetbrains.kotlin.kapt", kaptPlugin -> { + tasks.matching(t -> t.getName().equals("kaptGenerateStubsKotlin")).configureEach(task -> { + addCodeGenSourceDirs(task, generatedSourceSet); + task.dependsOn(quarkusGenerateCode); + }); + tasks.matching(t -> t.getName().equals("kaptGenerateStubsTestKotlin")).configureEach(task -> { + addCodeGenSourceDirs(task, generatedTestSourceSet); + task.dependsOn(quarkusGenerateCodeTests); + }); + }); }); } diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/build.gradle.kts b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/build.gradle.kts new file mode 100644 index 0000000000000..5317920cd7665 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + kotlin("jvm") + kotlin("plugin.allopen") + id("io.quarkus") + kotlin("kapt") +} + +repositories { + mavenLocal { + content { + includeGroupByRegex("io.quarkus.*") + includeGroup("org.hibernate.orm") + } + } + mavenCentral() +} + +val quarkusPlatformGroupId: String by project +val quarkusPlatformArtifactId: String by project +val quarkusPlatformVersion: String by project +val mapstructVersion: String by project + +dependencies { + implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) + implementation("io.quarkus:quarkus-kotlin") + implementation("io.quarkus:quarkus-arc") + implementation("io.quarkus:quarkus-grpc") + implementation("com.google.protobuf:protobuf-kotlin") + implementation("org.mapstruct:mapstruct:${mapstructVersion}") + kapt("org.mapstruct:mapstruct-processor:${mapstructVersion}") +} + +group = "org.acme" +version = "1.0.0-SNAPSHOT" + +allOpen { + annotation("jakarta.ws.rs.Path") + annotation("jakarta.enterprise.context.ApplicationScoped") + annotation("io.quarkus.test.junit.QuarkusTest") +} + +kotlin { + compilerOptions { + javaParameters = true + } +} diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/gradle.properties b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/gradle.properties new file mode 100644 index 0000000000000..68a178fcbeb35 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/gradle.properties @@ -0,0 +1,5 @@ +quarkusPluginId=io.quarkus +quarkusPlatformGroupId=io.quarkus +quarkusPlatformArtifactId=quarkus-bom +kotlinVersion=${kotlin.version} +mapstructVersion=${mapstruct.version} \ No newline at end of file diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/settings.gradle.kts b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/settings.gradle.kts new file mode 100644 index 0000000000000..b95135d1f6bf8 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/settings.gradle.kts @@ -0,0 +1,22 @@ +pluginManagement { + val quarkusPluginVersion: String by settings + val quarkusPluginId: String by settings + val kotlinVersion: String by settings + repositories { + mavenLocal { + content { + includeGroupByRegex("io.quarkus.*") + includeGroup("org.hibernate.orm") + } + } + mavenCentral() + gradlePluginPortal() + } + plugins { + id(quarkusPluginId) version quarkusPluginVersion + kotlin("jvm") version kotlinVersion + kotlin("plugin.allopen") version kotlinVersion + kotlin("kapt") version kotlinVersion + } +} +rootProject.name = "kotlin-kapt-grpc-mapstruct" diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/DemoMapper.kt b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/DemoMapper.kt new file mode 100644 index 0000000000000..80066583951a0 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/DemoMapper.kt @@ -0,0 +1,9 @@ +package org.acme + +import org.acme.HelloWorldProto +import org.mapstruct.Mapper + +@Mapper +interface DemoMapper { + fun map(request: HelloWorldProto.HelloRequest): HelloRequestModel +} diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/HelloRequestModel.kt b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/HelloRequestModel.kt new file mode 100644 index 0000000000000..2de6e6e596b87 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/kotlin/org/acme/HelloRequestModel.kt @@ -0,0 +1,3 @@ +package org.acme + +data class HelloRequestModel(val name: String) diff --git a/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/proto/helloworld.proto b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/proto/helloworld.proto new file mode 100644 index 0000000000000..9e374ea238c95 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/kotlin-kapt-grpc-mapstruct/src/main/proto/helloworld.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +option java_package = "org.acme"; +option java_outer_classname = "HelloWorldProto"; + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/KaptGrpcMapStructTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/KaptGrpcMapStructTest.java new file mode 100644 index 0000000000000..fe5ac4d42c76f --- /dev/null +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/KaptGrpcMapStructTest.java @@ -0,0 +1,19 @@ +package io.quarkus.gradle; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +/** + * Verifies that kapt annotation processors (e.g. MapStruct) can resolve types from Quarkus-generated + * sources (e.g. gRPC stubs). Previously, kaptGenerateStubsKotlin did not inherit the codegen source + * dirs injected into compileKotlin, causing MapStruct to fail with an unresolved type error. + */ +public class KaptGrpcMapStructTest extends QuarkusGradleWrapperTestBase { + + @Test + public void testKaptWithGrpcAndMapStruct() throws Exception { + final File projectDir = getProjectDir("kotlin-kapt-grpc-mapstruct"); + runGradleWrapper(projectDir, "clean", "build"); + } +} From 83ab2e02dae6b8d3029bb47cc285bb6f41cafe7d Mon Sep 17 00:00:00 2001 From: Luca Molteni Date: Thu, 23 Apr 2026 10:02:17 +0200 Subject: [PATCH 06/10] Use AssertJ bulk collection assertions for better error messages Replace loop-based single-element assertions with AssertJ's containsAll/doesNotContainAnyElementsOf so that failures report all mismatches at once instead of only the first one. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../deployment/JpaJandexScavengerTest.java | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java index 7e031edc53497..00ebde42d23ad 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java @@ -549,17 +549,17 @@ private Index buildIndex(Class... classes) throws Exception { private record ScavengerResult(JpaModelBuildItem result, Set enumTypes, Set javaTypes, List hotDeploymentWatchedFiles) { + private static List toNames(Class... classes) { + return Arrays.stream(classes).map(Class::getName).toList(); + } + private ScavengerResult assertHas(Set actual, Class... classes) { - for (Class clazz : classes) { - assertThat(actual).contains(clazz.getName()); - } + assertThat(actual).containsAll(toNames(classes)); return this; } private ScavengerResult assertDoesNotHave(Set actual, Class... classes) { - for (Class clazz : classes) { - assertThat(actual).doesNotContain(clazz.getName()); - } + assertThat(actual).doesNotContainAnyElementsOf(toNames(classes)); return this; } @@ -583,23 +583,17 @@ ScavengerResult potentialCdiBeanClassNamesHave(Class... classes) { Set beanClassNames = result.getPotentialCdiBeanClassNames().stream() .map(dn -> dn.toString()) .collect(Collectors.toSet()); - for (Class clazz : classes) { - assertThat(beanClassNames).contains(clazz.getName()); - } + assertThat(beanClassNames).containsAll(toNames(classes)); return this; } ScavengerResult allModelPackageNamesHave(String... packageNames) { - for (String pkg : packageNames) { - assertThat(result.getAllModelPackageNames()).contains(pkg); - } + assertThat(result.getAllModelPackageNames()).containsAll(Arrays.asList(packageNames)); return this; } ScavengerResult allModelClassNamesHave(Class... classes) { - for (Class clazz : classes) { - assertThat(result.getAllModelClassNames()).contains(clazz.getName()); - } + assertThat(result.getAllModelClassNames()).containsAll(toNames(classes)); return this; } @@ -609,23 +603,17 @@ ScavengerResult xmlMappingsHavePU(String puName) { } ScavengerResult enumTypesHave(Class... classes) { - for (Class clazz : classes) { - assertThat(enumTypes).contains(clazz.getName()); - } + assertThat(enumTypes).containsAll(toNames(classes)); return this; } ScavengerResult javaTypesHave(Class... classes) { - for (Class clazz : classes) { - assertThat(javaTypes).contains(clazz.getName()); - } + assertThat(javaTypes).containsAll(toNames(classes)); return this; } ScavengerResult hotDeploymentWatchedFilesHave(String... fileNames) { - for (String fileName : fileNames) { - assertThat(hotDeploymentWatchedFiles).contains(fileName); - } + assertThat(hotDeploymentWatchedFiles).containsAll(Arrays.asList(fileNames)); return this; } } From 07dd585d2348ea75d5ebc9c53036a6d9315e599c Mon Sep 17 00:00:00 2001 From: Luca Molteni Date: Thu, 23 Apr 2026 10:10:13 +0200 Subject: [PATCH 07/10] Split discoverModelAndRegisterForReflection into collectModel + buildModelFromCollector Remove mutable Collector field and getCollector() accessor by splitting the method into two: collectModel() returns the Collector, and buildModelFromCollector() converts it to a JpaModelBuildItem. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../orm/deployment/JpaJandexScavenger.java | 17 ++++++++++------- .../orm/deployment/JpaJandexScavengerTest.java | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java index 3ab9f4a36cba7..0ff19112f5dbc 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java @@ -64,11 +64,6 @@ public final class JpaJandexScavenger { private final List persistenceUnitContributions; private final IndexView index; private final Set ignorableNonIndexedClasses; - private Collector collector; - - Collector getCollector() { - return collector; - } JpaJandexScavenger(BuildProducer reflectiveClass, BuildProducer hotDeploymentWatchedFiles, @@ -82,8 +77,8 @@ Collector getCollector() { this.ignorableNonIndexedClasses = ignorableNonIndexedClasses; } - public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildException { - this.collector = new Collector(); + Collector collectModel() throws BuildException { + Collector collector = new Collector(); for (DotName packageAnnotation : ClassNames.PACKAGE_ANNOTATIONS) { enlistJPAModelAnnotatedPackages(collector, packageAnnotation); @@ -107,6 +102,10 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildExc enlistExplicitMappings(collector, persistenceUnitContribution); } + return collector; + } + + JpaModelBuildItem buildModelFromCollector(Collector collector) { Set managedClassNames = new HashSet<>(collector.entityTypes); managedClassNames.addAll(collector.modelTypes); reflectiveClass.produce(ReflectiveClassBuildItem.builder(managedClassNames).methods().fields().build()); @@ -157,6 +156,10 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildExc collector.potentialCdiBeanTypes, collector.xmlMappingsByPU); } + public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildException { + return buildModelFromCollector(collectModel()); + } + private void enlistExplicitMappings(Collector collector, JpaModelPersistenceUnitContributionBuildItem persistenceUnitContribution) { // Classes explicitly mentioned in persistence.xml diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java index 00ebde42d23ad..198192659965b 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java @@ -524,8 +524,8 @@ private ScavengerResult runScavenger(Index index, Set ignorableNonIndexe index, ignorableNonIndexedClasses); - JpaModelBuildItem result = scavenger.discoverModelAndRegisterForReflection(); - JpaJandexScavenger.Collector collector = scavenger.getCollector(); + JpaJandexScavenger.Collector collector = scavenger.collectModel(); + JpaModelBuildItem result = scavenger.buildModelFromCollector(collector); return new ScavengerResult(result, collector.enumTypes, collector.javaTypes, watchedFiles.stream().map(HotDeploymentWatchedFileBuildItem::getLocation).toList()); } From e5ab40e5578877ee1b8886d2ff0a5e09bee6bedb Mon Sep 17 00:00:00 2001 From: Luca Molteni Date: Thu, 23 Apr 2026 11:12:59 +0200 Subject: [PATCH 08/10] Move enum persistence test to jpa-h2 integration test for native coverage The deployment-level test doesn't verify enum reflection works in native mode. Move it to the jpa-h2 integration test which runs in both JVM and native mode via JPAFunctionalityInGraalITCase. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../orm/enums/EnumPersistenceTest.java | 38 --------------- .../hibernate/orm/enums/MyEntityWithEnum.java | 47 ------------------- .../quarkus/hibernate/orm/enums/Status.java | 6 --- .../jpa/h2/JPAFunctionalityTestEndpoint.java | 18 ++++++- .../java/io/quarkus/it/jpa/h2/Person.java | 14 ++++++ 5 files changed, 31 insertions(+), 92 deletions(-) delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java deleted file mode 100644 index 3211aa6a9db4c..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/EnumPersistenceTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.quarkus.hibernate.orm.enums; - -import static org.assertj.core.api.Assertions.assertThat; - -import jakarta.inject.Inject; -import jakarta.persistence.EntityManager; -import jakarta.transaction.Transactional; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusExtensionTest; - -public class EnumPersistenceTest { - - @RegisterExtension - static QuarkusExtensionTest runner = new QuarkusExtensionTest() - .withApplicationRoot((jar) -> jar - .addClass(MyEntityWithEnum.class) - .addClass(Status.class)) - .withConfigurationResource("application.properties"); - - @Inject - EntityManager entityManager; - - @Test - @Transactional - public void enumFieldPersistedAndRetrieved() { - MyEntityWithEnum entity = new MyEntityWithEnum("Gizmo", Status.LIVING); - entityManager.persist(entity); - entityManager.flush(); - entityManager.clear(); - - MyEntityWithEnum retrieved = entityManager.find(MyEntityWithEnum.class, entity.getId()); - assertThat(retrieved.getStatus()).isEqualTo(Status.LIVING); - assertThat(retrieved.getName()).isEqualTo("Gizmo"); - } -} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java deleted file mode 100644 index f7f0e73694f5d..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/MyEntityWithEnum.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.quarkus.hibernate.orm.enums; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; - -@Entity -public class MyEntityWithEnum { - - private long id; - private String name; - private Status status; - - public MyEntityWithEnum() { - } - - public MyEntityWithEnum(String name, Status status) { - this.name = name; - this.status = status; - } - - @Id - @GeneratedValue - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } -} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java deleted file mode 100644 index f3a06b6237866..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/enums/Status.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.quarkus.hibernate.orm.enums; - -public enum Status { - LIVING, - DECEASED -} diff --git a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/JPAFunctionalityTestEndpoint.java b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/JPAFunctionalityTestEndpoint.java index dc9eca79984ae..08b1f8eeed80b 100644 --- a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/JPAFunctionalityTestEndpoint.java +++ b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/JPAFunctionalityTestEndpoint.java @@ -78,6 +78,21 @@ public String test() throws IOException { QuarkusTransaction.requiringNew() .run(() -> em.createQuery("from Person p left join fetch p.address a").getResultList()); + //Verify enum fields are persisted and retrieved correctly: + QuarkusTransaction.requiringNew().run(() -> { + Person person = persistNewPerson("EnumTest"); + person.setStatus(Person.Status.LIVING); + em.flush(); + em.clear(); + + Person retrieved = em + .createQuery("from Person p where p.name = 'EnumTest'", Person.class) + .getSingleResult(); + if (retrieved.getStatus() != Person.Status.LIVING) { + throw new RuntimeException("Enum field not correctly persisted/retrieved: " + retrieved.getStatus()); + } + }); + cleanUpData(); return "OK"; @@ -88,11 +103,12 @@ private void cleanUpData() { .run(() -> em.createNativeQuery("Delete from Person").executeUpdate()); } - private void persistNewPerson(String name) { + private Person persistNewPerson(String name) { Person person = new Person(); person.setName(name); person.setAddress(new SequencedAddress("Street " + randomName())); em.persist(person); + return person; } private static String randomName() { diff --git a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/Person.java b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/Person.java index 4c614be7dfdf8..13cf8d79f1671 100644 --- a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/Person.java +++ b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/Person.java @@ -16,9 +16,15 @@ @NamedQuery(name = "get_person_by_name", query = "select p from Person p where name = :name") public class Person { + public enum Status { + LIVING, + DECEASED + } + private UUID id; private String name; private SequencedAddress address; + private Status status; public Person() { } @@ -57,6 +63,14 @@ public void setAddress(SequencedAddress address) { this.address = address; } + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + public void describeFully(StringBuilder sb) { sb.append("Person with id=").append(id).append(", name='").append(name).append("', address { "); getAddress().describeFully(sb); From 9fe438a3ae6be13f9af9351adc44559d58591752 Mon Sep 17 00:00:00 2001 From: Luca Molteni Date: Thu, 23 Apr 2026 11:18:43 +0200 Subject: [PATCH 09/10] Use builder-style chaining for ScavengerResult assertions Co-Authored-By: Claude Opus 4.6 (1M context) --- .../deployment/JpaJandexScavengerTest.java | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java index 198192659965b..5b66564485dc5 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavengerTest.java @@ -195,104 +195,104 @@ static class EntityWithConvertedField { void entityAndHierarchyDiscovery() throws Exception { Index index = buildIndex(SimpleEntity.class, BaseMappedSuperclass.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(SimpleEntity.class); - r.entityClassesDoNotHave(BaseMappedSuperclass.class); - r.managedClassesHave(SimpleEntity.class, BaseMappedSuperclass.class); + runScavenger(index) + .entityClassesHave(SimpleEntity.class) + .entityClassesDoNotHave(BaseMappedSuperclass.class) + .managedClassesHave(SimpleEntity.class, BaseMappedSuperclass.class); } @Test void embeddedFieldDiscovery() throws Exception { Index index = buildIndex(EntityWithEmbedded.class, SimpleEmbeddable.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithEmbedded.class); - r.entityClassesDoNotHave(SimpleEmbeddable.class); - r.managedClassesHave(SimpleEmbeddable.class); + runScavenger(index) + .entityClassesHave(EntityWithEmbedded.class) + .entityClassesDoNotHave(SimpleEmbeddable.class) + .managedClassesHave(SimpleEmbeddable.class); } @Test void embeddedIdOnMethodDiscovery() throws Exception { Index index = buildIndex(EntityWithEmbeddedId.class, SimpleEmbeddable.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithEmbeddedId.class); - r.entityClassesDoNotHave(SimpleEmbeddable.class); - r.managedClassesHave(SimpleEmbeddable.class); + runScavenger(index) + .entityClassesHave(EntityWithEmbeddedId.class) + .entityClassesDoNotHave(SimpleEmbeddable.class) + .managedClassesHave(SimpleEmbeddable.class); } @Test void elementCollectionDiscovery() throws Exception { Index index = buildIndex(EntityWithElementCollection.class, SimpleEmbeddable.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithElementCollection.class); - r.entityClassesDoNotHave(SimpleEmbeddable.class); - r.managedClassesHave(SimpleEmbeddable.class); + runScavenger(index) + .entityClassesHave(EntityWithElementCollection.class) + .entityClassesDoNotHave(SimpleEmbeddable.class) + .managedClassesHave(SimpleEmbeddable.class); } @Test void enumFieldDetection() throws Exception { Index index = buildIndex(EntityWithEnum.class, MyStatus.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithEnum.class); - r.entityClassesDoNotHave(MyStatus.class); - r.managedClassesDoNotHave(MyStatus.class); + runScavenger(index) + .entityClassesHave(EntityWithEnum.class) + .entityClassesDoNotHave(MyStatus.class) + .managedClassesDoNotHave(MyStatus.class); } @Test void entityInheritanceDiscovery() throws Exception { Index index = buildIndex(ChildEntity.class, SimpleEntity.class, BaseMappedSuperclass.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(SimpleEntity.class, ChildEntity.class); - r.entityClassesDoNotHave(BaseMappedSuperclass.class); - r.managedClassesHave(SimpleEntity.class, ChildEntity.class, BaseMappedSuperclass.class); + runScavenger(index) + .entityClassesHave(SimpleEntity.class, ChildEntity.class) + .entityClassesDoNotHave(BaseMappedSuperclass.class) + .managedClassesHave(SimpleEntity.class, ChildEntity.class, BaseMappedSuperclass.class); } @Test void entityListenerDiscovery() throws Exception { Index index = buildIndex(EntityWithListener.class, MyListener.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithListener.class); - r.potentialCdiBeanClassNamesHave(MyListener.class); + runScavenger(index) + .entityClassesHave(EntityWithListener.class) + .potentialCdiBeanClassNamesHave(MyListener.class); } @Test void idClassDiscovery() throws Exception { Index index = buildIndex(EntityWithIdClass.class, MyIdClass.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithIdClass.class); - r.entityClassesDoNotHave(MyIdClass.class); + runScavenger(index) + .entityClassesHave(EntityWithIdClass.class) + .entityClassesDoNotHave(MyIdClass.class); } @Test void idClassIsManagedClass() throws Exception { Index index = buildIndex(EntityWithIdClass.class, MyIdClass.class); - ScavengerResult r = runScavenger(index); - r.managedClassesHave(MyIdClass.class); + runScavenger(index) + .managedClassesHave(MyIdClass.class); } @Test void xmlOnlyEntityDiscovery() throws Exception { Index index = buildIndex(XmlOnlyEntity.class, XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-test-discovery.xml"); - r.entityClassesHave(XmlOnlyEntity.class); - r.entityClassesDoNotHave(XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); - r.managedClassesHave(XmlOnlyEntity.class, XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); + runScavenger(index, Collections.emptySet(), "META-INF/orm-test-discovery.xml") + .entityClassesHave(XmlOnlyEntity.class) + .entityClassesDoNotHave(XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class) + .managedClassesHave(XmlOnlyEntity.class, XmlOnlyMappedSuperclass.class, XmlOnlyEmbeddable.class); } @Test void parseHbmTest() throws Exception { Index index = buildIndex(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml"); - r.entityClassesHave(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml") + .entityClassesHave(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); } @Test @@ -315,8 +315,8 @@ void unindexedSuperclassIgnoredWhenInIgnorableSet() throws Exception { Index index = buildIndex(SimpleEntity.class); Set ignorable = Set.of(BaseMappedSuperclass.class.getName()); - ScavengerResult r = runScavenger(index, ignorable); - r.entityClassesHave(SimpleEntity.class); + runScavenger(index, ignorable) + .entityClassesHave(SimpleEntity.class); } @Test @@ -331,27 +331,27 @@ void embeddedFieldWithoutEmbeddableAnnotationThrowsException() throws Exception void hbmSimpleEntityDiscovery() throws Exception { Index index = buildIndex(NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml"); - r.entityClassesHave(NonAnnotatedEntity.class); - r.managedClassesHave(NonAnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml") + .entityClassesHave(NonAnnotatedEntity.class) + .managedClassesHave(NonAnnotatedEntity.class); } @Test void hbmFilterDefEntityDiscovery() throws Exception { Index index = buildIndex(NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml"); - r.entityClassesHave(NonAnnotatedEntity.class); - r.managedClassesHave(NonAnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml") + .entityClassesHave(NonAnnotatedEntity.class) + .managedClassesHave(NonAnnotatedEntity.class); } @Test void ormSimpleEntityDiscovery() throws Exception { Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); - r.entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); - r.managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml") + .entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class) + .managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); } @Test @@ -359,28 +359,28 @@ void ormPackageEntityDiscovery() throws Exception { Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, OtherNonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml"); - r.entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, - OtherNonAnnotatedEntity.class); - r.managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, - OtherNonAnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml") + .entityClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class) + .managedClassesHave(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, + OtherNonAnnotatedEntity.class); } @Test void converterAutoApplyDiscovery() throws Exception { Index index = buildIndex(EntityWithConvertedField.class, MyDataConverter.class, MyData.class); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(EntityWithConvertedField.class); - r.potentialCdiBeanClassNamesHave(MyDataConverter.class); + runScavenger(index) + .entityClassesHave(EntityWithConvertedField.class) + .potentialCdiBeanClassNamesHave(MyDataConverter.class); } @Test void converterInAllModelClasses() throws Exception { Index index = buildIndex(EntityWithConvertedField.class, MyDataConverter.class, MyData.class); - ScavengerResult r = runScavenger(index); - r.allModelClassNamesHave(MyDataConverter.class); + runScavenger(index) + .allModelClassNamesHave(MyDataConverter.class); } @Test @@ -388,49 +388,49 @@ void packageLevelAnnotationDiscovery() throws Exception { Index index = buildIndex(ParentEntity.class, Class.forName("io.quarkus.hibernate.orm.packages.package-info")); - ScavengerResult r = runScavenger(index); - r.entityClassesHave(ParentEntity.class); - r.allModelPackageNamesHave("io.quarkus.hibernate.orm.packages"); + runScavenger(index) + .entityClassesHave(ParentEntity.class) + .allModelPackageNamesHave("io.quarkus.hibernate.orm.packages"); } @Test void hbmSimpleParseXmlPu() throws Exception { Index index = buildIndex(NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-simple.xml") + .xmlMappingsHavePU("default"); } @Test void hbmFilterDefParseXmlPu() throws Exception { Index index = buildIndex(NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-filterdef.xml") + .xmlMappingsHavePU("default"); } @Test void hbmComponentParseXmlPu() throws Exception { Index index = buildIndex(NonAnnotatedComponentUsingEntity.class, NonAnnotatedComponent.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/hbm-component.xml") + .xmlMappingsHavePU("default"); } @Test void ormOverrideParseXmlPu() throws Exception { Index index = buildIndex(AnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-override.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/orm-override.xml") + .xmlMappingsHavePU("default"); } @Test void ormSimpleParseXmlPu() throws Exception { Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml") + .xmlMappingsHavePU("default"); } @Test @@ -438,8 +438,8 @@ void ormPackageParseXmlPu() throws Exception { Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class, OtherNonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml"); - r.xmlMappingsHavePU("default"); + runScavenger(index, Collections.emptySet(), "META-INF/orm-package.xml") + .xmlMappingsHavePU("default"); } @Test @@ -456,49 +456,49 @@ void xmlMappingInvalidSilentlyAccepted() throws Exception { void enumFieldInEnumTypes() throws Exception { Index index = buildIndex(EntityWithEnum.class, MyStatus.class); - ScavengerResult r = runScavenger(index); - r.enumTypesHave(MyStatus.class); + runScavenger(index) + .enumTypesHave(MyStatus.class); } @Test void javaPackageInterfaceInJavaTypes() throws Exception { Index index = buildIndex(SerializableEntity.class); - ScavengerResult r = runScavenger(index); - r.javaTypesHave(Serializable.class); + runScavenger(index) + .javaTypesHave(Serializable.class); } @Test void noFileMappingShouldNotDiscoverEntitiesOrEnums() throws Exception { Index index = buildIndex(AnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "no-file"); - r.entityClassesHave(AnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "no-file") + .entityClassesHave(AnnotatedEntity.class); } @Test void noFileMappingShouldNotDiscoverEntities() throws Exception { Index index = buildIndex(NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "no-file"); - r.entityClassesDoNotHave(NonAnnotatedEntity.class); - r.managedClassesDoNotHave(NonAnnotatedEntity.class); + runScavenger(index, Collections.emptySet(), "no-file") + .entityClassesDoNotHave(NonAnnotatedEntity.class) + .managedClassesDoNotHave(NonAnnotatedEntity.class); } @Test void xmlMappingFilesRegisteredForHotDeployment() throws Exception { Index index = buildIndex(io.quarkus.hibernate.orm.xml.orm.NonAnnotatedEntity.class); - ScavengerResult r = runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml"); - r.hotDeploymentWatchedFilesHave("META-INF/orm-simple.xml"); + runScavenger(index, Collections.emptySet(), "META-INF/orm-simple.xml") + .hotDeploymentWatchedFilesHave("META-INF/orm-simple.xml"); } @Test void implicitOrmXmlDiscovery() throws Exception { Index index = buildIndex(SimpleEntity.class, BaseMappedSuperclass.class); - ScavengerResult r = runScavenger(index); - r.xmlMappingsHavePU("default"); + runScavenger(index) + .xmlMappingsHavePU("default"); } private ScavengerResult runScavenger(Index index) throws Exception { From aff2dd7c05f63128df23eec56267bc8b3b7773a7 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 23 Apr 2026 13:41:27 +0200 Subject: [PATCH 10/10] Add metacosm to be notified of kubernetes-related issues --- .github/quarkus-github-bot.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/quarkus-github-bot.yml b/.github/quarkus-github-bot.yml index ba7811a04df91..9134d323c0ae3 100644 --- a/.github/quarkus-github-bot.yml +++ b/.github/quarkus-github-bot.yml @@ -345,7 +345,7 @@ triage: - id: kubernetes labels: [area/kubernetes] titleBody: "kubernetes" - notify: [geoand] + notify: [geoand, metacosm] directories: - extensions/kubernetes/ - extensions/kubernetes-client/ @@ -354,15 +354,15 @@ triage: - id: minikube labels: [area/kubernetes] titleBody: "minikube" - notify: [geoand] + notify: [geoand, metacosm] - id: openshift labels: [area/kubernetes] titleBody: "openshift" - notify: [geoand] + notify: [geoand, metacosm] - id: knative labels: [area/kubernetes] titleBody: "knative" - notify: [geoand] + notify: [geoand, metacosm] - id: container-image labels: [area/container-image] expression: |