From 9276555c4828fb1f9366c868158d83d8efc6bb6b Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Wed, 9 Aug 2023 20:22:53 -0300 Subject: [PATCH 1/7] Output build graph using `quarkus.builder.graph-output` property Add `BuilderConfig` to eliminate build warnings (cherry picked from commit cd72dd7cd585cdcadbb3be764608abe372b7031c) --- .../io/quarkus/builder/BuildChainBuilder.java | 2 +- .../io/quarkus/runtime/BuilderConfig.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java diff --git a/core/builder/src/main/java/io/quarkus/builder/BuildChainBuilder.java b/core/builder/src/main/java/io/quarkus/builder/BuildChainBuilder.java index 820e66dc3e81a..0578ba0c4bc34 100644 --- a/core/builder/src/main/java/io/quarkus/builder/BuildChainBuilder.java +++ b/core/builder/src/main/java/io/quarkus/builder/BuildChainBuilder.java @@ -30,7 +30,7 @@ */ public final class BuildChainBuilder { - private static final String GRAPH_OUTPUT = System.getProperty("jboss.builder.graph-output"); + private static final String GRAPH_OUTPUT = System.getProperty("quarkus.builder.graph-output"); static final boolean LOG_CONFLICT_CAUSING = Boolean.getBoolean("quarkus.builder.log-conflict-cause"); private final BuildStepBuilder finalStep; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java new file mode 100644 index 0000000000000..1af640d9ed936 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java @@ -0,0 +1,29 @@ +package io.quarkus.runtime; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +/** + * This configuration class is here to avoid warnings when using {@code -Dquarkus.builder.=...}. + * + * @see io.quarkus.builder.BuildChainBuilder + */ +@ConfigRoot(name = "builder", phase = ConfigPhase.RUN_TIME) +public class BuilderConfig { + + /** + * Dump the graph output to a file. This is useful for debugging. + */ + @ConfigItem + public Optional graphOutput; + + /** + * Whether to log the cause of a conflict. + */ + @ConfigItem + public Optional logConflictCause; + +} From bb22e8c6ff49e2152457dceeb2eeee1b557160ee Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 11 Aug 2023 14:05:34 +0200 Subject: [PATCH 2/7] Convert newly added config to @ConfigMapping (cherry picked from commit b7f641cd74f9d7b42bcff44ab9a96676c4b037e7) --- .../main/java/io/quarkus/runtime/BuilderConfig.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java index 1af640d9ed936..b23b2cbe9ebde 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/BuilderConfig.java @@ -2,28 +2,27 @@ import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; /** * This configuration class is here to avoid warnings when using {@code -Dquarkus.builder.=...}. * * @see io.quarkus.builder.BuildChainBuilder */ -@ConfigRoot(name = "builder", phase = ConfigPhase.RUN_TIME) -public class BuilderConfig { +@ConfigMapping(prefix = "quarkus.builder") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface BuilderConfig { /** * Dump the graph output to a file. This is useful for debugging. */ - @ConfigItem - public Optional graphOutput; + Optional graphOutput(); /** * Whether to log the cause of a conflict. */ - @ConfigItem - public Optional logConflictCause; + Optional logConflictCause(); } From 3f847064fcba262d2de4ebbac4f4d75fcd612c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 11 Aug 2023 13:11:46 +0200 Subject: [PATCH 3/7] Register arrays of Hibernate ORM's JDBC basic types for reflection Works around https://hibernate.atlassian.net/browse/HHH-16809 (cherry picked from commit 58a4770cd15ab6e9bc8e265dff603cba47657971) --- .../orm/deployment/GraalVMFeatures.java | 9 ++++ .../orm/deployment/HibernateOrmProcessor.java | 6 +-- ...nnotations.java => HibernateOrmTypes.java} | 41 ++++++++++++++++- .../orm/deployment/JpaJandexScavenger.java | 4 +- ...nsTest.java => HibernateOrmTypesTest.java} | 45 +++++++++++++++---- 5 files changed, 90 insertions(+), 15 deletions(-) rename extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/{HibernateOrmAnnotations.java => HibernateOrmTypes.java} (91%) rename extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/{HibernateOrmAnnotationsTest.java => HibernateOrmTypesTest.java} (78%) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java index d0d2e124b8f80..b4f4ae6caa1bf 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java @@ -28,4 +28,13 @@ ReflectiveClassBuildItem registerGeneratorClassesForReflections() { .build(); } + // Workaround for https://hibernate.atlassian.net/browse/HHH-16809 + // See https://github.com/hibernate/hibernate-orm/pull/6815#issuecomment-1662197545 + @BuildStep + ReflectiveClassBuildItem registerJdbcArrayTypesForReflection() { + return ReflectiveClassBuildItem + .builder(HibernateOrmTypes.JDBC_JAVA_TYPES.stream().map(d -> d.toString() + "[]").toArray(String[]::new)) + .build(); + } + } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index ed82729ff903b..a2117baab9210 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -461,10 +461,10 @@ public void preGenAnnotationProxies(List per // but there are plans to make deep changes to XML mapping in ORM (to rely on Jandex directly), // so let's not waste our time on optimizations that won't be relevant in a few months. List annotationClassNames = new ArrayList<>(); - for (DotName name : HibernateOrmAnnotations.JPA_MAPPING_ANNOTATIONS) { + for (DotName name : HibernateOrmTypes.JPA_MAPPING_ANNOTATIONS) { annotationClassNames.add(name.toString()); } - for (DotName name : HibernateOrmAnnotations.HIBERNATE_MAPPING_ANNOTATIONS) { + for (DotName name : HibernateOrmTypes.HIBERNATE_MAPPING_ANNOTATIONS) { annotationClassNames.add(name.toString()); } reflective.produce(ReflectiveClassBuildItem.builder(annotationClassNames.toArray(new String[0])) @@ -766,7 +766,7 @@ public void registerInjectServiceMethodsForReflection(CombinedIndexBuildItem ind Set classes = new HashSet<>(); // Built-in service classes; can't rely on Jandex as Hibernate ORM is not indexed by default. - HibernateOrmAnnotations.ANNOTATED_WITH_INJECT_SERVICE.stream() + HibernateOrmTypes.ANNOTATED_WITH_INJECT_SERVICE.stream() .map(DotName::toString) .forEach(classes::add); diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmTypes.java similarity index 91% rename from extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java rename to extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmTypes.java index 384b8239753c3..3b8a7f8e9e0ab 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmTypes.java @@ -4,9 +4,9 @@ import org.jboss.jandex.DotName; -public final class HibernateOrmAnnotations { +public final class HibernateOrmTypes { - private HibernateOrmAnnotations() { + private HibernateOrmTypes() { } public static final List PACKAGE_ANNOTATIONS = List.of( @@ -316,4 +316,41 @@ private HibernateOrmAnnotations() { DotName.createSimple("jakarta.persistence.PreRemove"), DotName.createSimple("jakarta.persistence.PreUpdate")); + public static final List JDBC_JAVA_TYPES = List.of( + DotName.createSimple("java.lang.Boolean"), + DotName.createSimple("java.lang.Byte"), + DotName.createSimple("java.lang.Character"), + DotName.createSimple("java.lang.Class"), + DotName.createSimple("java.lang.Double"), + DotName.createSimple("java.lang.Float"), + DotName.createSimple("java.lang.Integer"), + DotName.createSimple("java.lang.Long"), + DotName.createSimple("java.lang.Object"), + DotName.createSimple("java.lang.Short"), + DotName.createSimple("java.lang.String"), + DotName.createSimple("java.math.BigDecimal"), + DotName.createSimple("java.math.BigInteger"), + DotName.createSimple("java.net.InetAddress"), + DotName.createSimple("java.net.URL"), + DotName.createSimple("java.sql.Blob"), + DotName.createSimple("java.sql.Clob"), + DotName.createSimple("java.sql.NClob"), + DotName.createSimple("java.time.Duration"), + DotName.createSimple("java.time.Instant"), + DotName.createSimple("java.time.LocalDate"), + DotName.createSimple("java.time.LocalDateTime"), + DotName.createSimple("java.time.LocalTime"), + DotName.createSimple("java.time.OffsetDateTime"), + DotName.createSimple("java.time.OffsetTime"), + DotName.createSimple("java.time.Year"), + DotName.createSimple("java.time.ZoneId"), + DotName.createSimple("java.time.ZoneOffset"), + DotName.createSimple("java.time.ZonedDateTime"), + DotName.createSimple("java.util.Calendar"), + DotName.createSimple("java.util.Currency"), + DotName.createSimple("java.util.Date"), + DotName.createSimple("java.util.Locale"), + DotName.createSimple("java.util.Map$Entry"), + DotName.createSimple("java.util.TimeZone"), + DotName.createSimple("java.util.UUID")); } 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 729da9756567e..84edc7d837e66 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 @@ -85,7 +85,7 @@ public final class JpaJandexScavenger { public JpaModelBuildItem discoverModelAndRegisterForReflection() { Collector collector = new Collector(); - for (DotName packageAnnotation : HibernateOrmAnnotations.PACKAGE_ANNOTATIONS) { + for (DotName packageAnnotation : HibernateOrmTypes.PACKAGE_ANNOTATIONS) { enlistJPAModelAnnotatedPackages(collector, packageAnnotation); } enlistJPAModelClasses(collector, ClassNames.JPA_ENTITY); @@ -95,7 +95,7 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() { enlistEmbeddedsAndElementCollections(collector); enlistPotentialCdiBeanClasses(collector, ClassNames.CONVERTER); - for (DotName annotation : HibernateOrmAnnotations.JPA_LISTENER_ANNOTATIONS) { + for (DotName annotation : HibernateOrmTypes.JPA_LISTENER_ANNOTATIONS) { enlistPotentialCdiBeanClasses(collector, annotation); } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmTypesTest.java similarity index 78% rename from extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java rename to extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmTypesTest.java index 397b417c66da6..992cb3d7b33a2 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmTypesTest.java @@ -8,6 +8,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Modifier; import java.net.URL; import java.util.Arrays; import java.util.HashSet; @@ -24,17 +25,22 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.Index; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Type; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import io.quarkus.deployment.index.IndexWrapper; import io.quarkus.deployment.index.IndexingUtil; +import io.quarkus.deployment.index.PersistentClassIndex; +import io.quarkus.deployment.util.JandexUtil; import io.quarkus.hibernate.orm.deployment.ClassNames; -import io.quarkus.hibernate.orm.deployment.HibernateOrmAnnotations; +import io.quarkus.hibernate.orm.deployment.HibernateOrmTypes; /** - * Test that hardcoded lists of Hibernate ORM annotations stay up-to-date. + * Test that hardcoded lists of Hibernate ORM types stay up-to-date. */ -public class HibernateOrmAnnotationsTest { +public class HibernateOrmTypesTest { private static final DotName RETENTION = DotName.createSimple(Retention.class.getName()); private static final DotName TARGET = DotName.createSimple(Target.class.getName()); @@ -53,7 +59,7 @@ public void testNoMissingJpaAnnotation() { Set jpaMappingAnnotations = findRuntimeAnnotations(jpaIndex); jpaMappingAnnotations.removeIf(name -> name.toString().startsWith("jakarta.persistence.metamodel.")); - assertThat(HibernateOrmAnnotations.JPA_MAPPING_ANNOTATIONS) + assertThat(HibernateOrmTypes.JPA_MAPPING_ANNOTATIONS) .containsExactlyInAnyOrderElementsOf(jpaMappingAnnotations); } @@ -65,7 +71,7 @@ public void testNoMissingJpaListenerAnnotation() { .filter(name -> listenerAnnotationNamePattern.matcher(name.toString()).matches()) .collect(Collectors.toSet()); - assertThat(HibernateOrmAnnotations.JPA_LISTENER_ANNOTATIONS) + assertThat(HibernateOrmTypes.JPA_LISTENER_ANNOTATIONS) .containsExactlyInAnyOrderElementsOf(jpaMappingAnnotations); } @@ -76,7 +82,7 @@ public void testNoMissingHibernateAnnotation() { hibernateMappingAnnotations.removeIf(name -> name.toString().contains(".spi.")); ignoreInternalAnnotations(hibernateMappingAnnotations); - assertThat(HibernateOrmAnnotations.HIBERNATE_MAPPING_ANNOTATIONS) + assertThat(HibernateOrmTypes.HIBERNATE_MAPPING_ANNOTATIONS) .containsExactlyInAnyOrderElementsOf(hibernateMappingAnnotations); } @@ -93,7 +99,7 @@ public void testNoMissingPackageLevelAnnotation() { packageLevelHibernateAnnotations.removeIf(name -> name.toString().contains(".internal.")); ignoreInternalAnnotations(packageLevelHibernateAnnotations); - assertThat(HibernateOrmAnnotations.PACKAGE_ANNOTATIONS) + assertThat(HibernateOrmTypes.PACKAGE_ANNOTATIONS) .containsExactlyInAnyOrderElementsOf(packageLevelHibernateAnnotations); } @@ -102,10 +108,33 @@ public void testNoMissingInjectServiceAnnotatedClass() { Set injectServiceAnnotatedClasses = findClassesWithMethodsAnnotatedWith(hibernateIndex, ClassNames.INJECT_SERVICE); - assertThat(HibernateOrmAnnotations.ANNOTATED_WITH_INJECT_SERVICE) + assertThat(HibernateOrmTypes.ANNOTATED_WITH_INJECT_SERVICE) .containsExactlyInAnyOrderElementsOf(injectServiceAnnotatedClasses); } + @Test + public void testNoMissingJdbcJavaTypeClass() { + Set jdbcJavaTypeNames = new TreeSet<>(); + DotName basicJavaTypeName = DotName.createSimple("org.hibernate.type.descriptor.java.BasicJavaType"); + IndexView hibernateAndJdkIndex = new IndexWrapper(hibernateIndex, Thread.currentThread().getContextClassLoader(), + new PersistentClassIndex()); + + for (ClassInfo basicJavaTypeImplInfo : hibernateIndex.getAllKnownImplementors(basicJavaTypeName)) { + if (Modifier.isAbstract(basicJavaTypeImplInfo.flags())) { + continue; + } + List typeParams = JandexUtil.resolveTypeParameters(basicJavaTypeImplInfo.name(), basicJavaTypeName, + hibernateAndJdkIndex); + Type jdbcJavaType = typeParams.get(0); + if (jdbcJavaType.kind() == Type.Kind.CLASS) { + jdbcJavaTypeNames.add(jdbcJavaType.name()); + } + } + + assertThat(HibernateOrmTypes.JDBC_JAVA_TYPES) + .containsExactlyInAnyOrderElementsOf(jdbcJavaTypeNames); + } + private Set findRuntimeAnnotations(Index index) { Set annotations = new HashSet<>(); for (AnnotationInstance retentionAnnotation : index.getAnnotations(RETENTION)) { From d75a07c1f773327a452a21b567498dba76eb0fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 11 Aug 2023 09:47:30 +0200 Subject: [PATCH 4/7] Reproducer for native build failing due to UUID[] not being registered for reflection This seems to only affect PostgreSQL, so I didn't add tests for other DBs. (cherry picked from commit 70d7a6ba04cdcc4460afcd9fa9d0441c8db3436d) --- .../JPAFunctionalityTestEndpoint.java | 23 +++++++++++ .../it/jpa/postgresql/MyUUIDEntity.java | 41 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/MyUUIDEntity.java diff --git a/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/JPAFunctionalityTestEndpoint.java b/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/JPAFunctionalityTestEndpoint.java index 0bc9b7d02b6e7..ac23341e763f4 100644 --- a/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/JPAFunctionalityTestEndpoint.java +++ b/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/JPAFunctionalityTestEndpoint.java @@ -57,6 +57,8 @@ private static void doStuffWithHibernate(EntityManagerFactory entityManagerFacto deleteAllPerson(entityManagerFactory); + // Try an entity using a UUID + verifyUUIDEntity(entityManagerFactory); } private static void verifyJPANamedQuery(final EntityManagerFactory emf) { @@ -144,6 +146,27 @@ private static String randomName() { return UUID.randomUUID().toString(); } + private static void verifyUUIDEntity(final EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + MyUUIDEntity myEntity = new MyUUIDEntity(); + myEntity.setName("George"); + em.persist(myEntity); + transaction.commit(); + em.close(); + + em = emf.createEntityManager(); + transaction = em.getTransaction(); + transaction.begin(); + myEntity = em.find(MyUUIDEntity.class, myEntity.getId()); + if (myEntity == null || !"George".equals(myEntity.getName())) { + throw new RuntimeException("Incorrect loaded MyUUIDEntity " + myEntity); + } + transaction.commit(); + em.close(); + } + private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException { final PrintWriter writer = resp.getWriter(); if (errorMessage != null) { diff --git a/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/MyUUIDEntity.java b/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/MyUUIDEntity.java new file mode 100644 index 0000000000000..23997f53e5f09 --- /dev/null +++ b/integration-tests/jpa-postgresql/src/main/java/io/quarkus/it/jpa/postgresql/MyUUIDEntity.java @@ -0,0 +1,41 @@ +package io.quarkus.it.jpa.postgresql; + +import java.util.UUID; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity(name = "uuidentity") +public class MyUUIDEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + private String name; + + public MyUUIDEntity() { + } + + public MyUUIDEntity(UUID id, String name) { + this.id = id; + this.name = name; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} From 3fcbbe44d44eee31acba7b45fc5145ac0c99f551 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Thu, 10 Aug 2023 00:05:57 +0200 Subject: [PATCH 5/7] Lock jib execution to avoid OverlappingFileLockException in parallel builds (cherry picked from commit 1eb8b2d80673abe76b9cced7a1cf491872d22748) --- .../image/jib/deployment/JibProcessor.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index e406fed36f2ea..2d79781a403c1 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -6,7 +6,6 @@ import static io.quarkus.container.image.deployment.util.EnablementUtil.buildContainerImageNeeded; import static io.quarkus.container.image.deployment.util.EnablementUtil.pushContainerImageNeeded; import static io.quarkus.container.util.PathsUtil.findMainSourcesRoot; -import static io.quarkus.deployment.pkg.PackageConfig.MUTABLE_JAR; import java.io.IOException; import java.io.UncheckedIOException; @@ -26,6 +25,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,6 +33,7 @@ import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; +import com.google.cloud.tools.jib.api.CacheDirectoryCreationException; import com.google.cloud.tools.jib.api.Containerizer; import com.google.cloud.tools.jib.api.DockerDaemonImage; import com.google.cloud.tools.jib.api.ImageReference; @@ -42,6 +43,7 @@ import com.google.cloud.tools.jib.api.JibContainer; import com.google.cloud.tools.jib.api.JibContainerBuilder; import com.google.cloud.tools.jib.api.LogEvent; +import com.google.cloud.tools.jib.api.RegistryException; import com.google.cloud.tools.jib.api.RegistryImage; import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; @@ -249,7 +251,7 @@ private JibContainer containerize(ContainerImageConfig containerImageConfig, previousContextStorageSysProp = System.setProperty(OPENTELEMETRY_CONTEXT_CONTEXT_STORAGE_PROVIDER_SYS_PROP, "default"); - JibContainer container = jibContainerBuilder.containerize(containerizer); + JibContainer container = containerizeUnderLock(jibContainerBuilder, containerizer); log.infof("%s container image %s (%s)\n", containerImageConfig.isPushExplicitlyEnabled() ? "Pushed" : "Created", container.getTargetImage(), @@ -300,7 +302,7 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon } containerizer.setToolName("Quarkus"); containerizer.setToolVersion(Version.getVersion()); - containerizer.addEventHandler(LogEvent.class, (e) -> { + containerizer.addEventHandler(LogEvent.class, e -> { if (!e.getMessage().isEmpty()) { log.log(toJBossLoggingLevel(e.getLevel()), e.getMessage()); } @@ -311,6 +313,29 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon return containerizer; } + /** + * Wraps the containerize invocation in a synchronized block to avoid OverlappingFileLockException when running parallel jib + * builds (e.g. mvn -T2 ...). + * Each build thread uses its own augmentation CL (which is why the OverlappingFileLockException prevention in jib doesn't + * work here), so the lock object + * has to be loaded via the parent classloader so that all build threads lock the same object. + * QuarkusAugmentor was chosen semi-randomly (note: quarkus-core-deployment is visible to that parent CL, this jib extension + * is not!). + */ + private JibContainer containerizeUnderLock(JibContainerBuilder jibContainerBuilder, Containerizer containerizer) + throws InterruptedException, RegistryException, IOException, CacheDirectoryCreationException, ExecutionException { + Class lockObj = getClass(); + ClassLoader parentCL = getClass().getClassLoader().getParent(); + try { + lockObj = parentCL.loadClass("io.quarkus.deployment.QuarkusAugmentor"); + } catch (ClassNotFoundException e) { + log.warnf("Could not load io.quarkus.deployment.QuarkusAugmentor with parent classloader: %s", parentCL); + } + synchronized (lockObj) { + return jibContainerBuilder.containerize(containerizer); + } + } + private void writeOutputFiles(JibContainer jibContainer, ContainerImageJibConfig jibConfig, OutputTargetBuildItem outputTarget) { doWriteOutputFile(outputTarget, Paths.get(jibConfig.imageDigestFile), jibContainer.getDigest().toString()); From 08d13500f20a7d824ee292e36c945984b496ac1e Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 14 Aug 2023 16:20:16 +0200 Subject: [PATCH 6/7] ArC: fix StackOverflowError in AutoAddScopeBuildItem - if multiple conditions are used. (cherry picked from commit 1786eff1a3f0c6e7424be03392745ebe2c5d65cf) --- .../arc/deployment/AutoAddScopeBuildItem.java | 15 ++++++-- .../autoscope/AutoScopeBuildItemTest.java | 36 ++++++++++++++----- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java index 805ef0aab7000..3de2e134b35ad 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java @@ -77,7 +77,7 @@ public interface MatchPredicate { /** * @param clazz - * @param annotations + * @param annotations The current set of (possibly transformed) annotations * @param index * @return {@code true} if the input arguments match the predicate, * {@code false} otherwise @@ -85,10 +85,11 @@ public interface MatchPredicate { boolean test(ClassInfo clazz, Collection annotations, IndexView index); default MatchPredicate and(MatchPredicate other) { + MatchPredicate previous = this; return new MatchPredicate() { @Override public boolean test(ClassInfo clazz, Collection annotations, IndexView index) { - return test(clazz, annotations, index) && other.test(clazz, annotations, index); + return previous.test(clazz, annotations, index) && other.test(clazz, annotations, index); } }; } @@ -140,6 +141,8 @@ public Builder unremovable() { /** * Set a custom predicate. + *

+ * The previous predicate (if any) is replaced. * * @param predicate * @return self @@ -286,7 +289,13 @@ public Builder scopeAlreadyAdded(BiConsumer consumer) { return this; } - private Builder and(MatchPredicate other) { + /** + * The final predicate is a short-circuiting logical AND of the previous predicate (if any) and this condition. + * + * @param other + * @return self + */ + public Builder and(MatchPredicate other) { if (matchPredicate == null) { matchPredicate = other; } else { diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/autoscope/AutoScopeBuildItemTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/autoscope/AutoScopeBuildItemTest.java index 9212a428a2224..bf42fdc62815b 100644 --- a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/autoscope/AutoScopeBuildItemTest.java +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/autoscope/AutoScopeBuildItemTest.java @@ -9,7 +9,9 @@ import jakarta.annotation.PostConstruct; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.jboss.jandex.DotName; import org.jboss.logging.Logger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -31,9 +33,11 @@ public class AutoScopeBuildItemTest { b.addBuildStep(new BuildStep() { @Override public void execute(BuildContext context) { - context.produce(AutoAddScopeBuildItem.builder().match((clazz, annotations, index) -> { - return clazz.name().toString().equals(SimpleBean.class.getName()); - }).defaultScope(BuiltinScope.DEPENDENT) + context.produce(AutoAddScopeBuildItem.builder() + .match((clazz, annotations, index) -> { + return clazz.name().toString().equals(SimpleBean.class.getName()); + }) + .defaultScope(BuiltinScope.DEPENDENT) .scopeAlreadyAdded((scope, reason) -> { // We can't pass the state directly to AutoScopeBuildItemTest because it's loaded by a different classloader Logger.getLogger("AutoScopeBuildItemTest").info(scope + ":" + reason); @@ -43,17 +47,30 @@ public void execute(BuildContext context) { b.addBuildStep(new BuildStep() { @Override public void execute(BuildContext context) { - context.produce(AutoAddScopeBuildItem.builder().match((clazz, annotations, index) -> { - return clazz.name().toString().equals(SimpleBean.class.getName()); - }).defaultScope(BuiltinScope.SINGLETON).priority(10).reason("Foo!").build()); + context.produce(AutoAddScopeBuildItem.builder() + .match((clazz, annotations, index) -> { + return clazz.name().toString().equals(SimpleBean.class.getName()); + }) + .anyMethodMatches(m -> m.hasAnnotation(PostConstruct.class)) + .isAnnotatedWith(DotName.createSimple(Named.class)) + .defaultScope(BuiltinScope.SINGLETON) + .priority(10) + .reason("Foo!") + .build()); } }).produces(AutoAddScopeBuildItem.class).build(); b.addBuildStep(new BuildStep() { @Override public void execute(BuildContext context) { - context.produce(AutoAddScopeBuildItem.builder().match((clazz, annotations, index) -> { - return clazz.name().toString().equals(NotABean.class.getName()); - }).defaultScope(BuiltinScope.SINGLETON).unremovable().build()); + context.produce(AutoAddScopeBuildItem.builder() + .match((clazz, annotations, index) -> { + return clazz.name().toString().equals(NotABean.class.getName()); + }) + .containsAnnotations(DotName.createSimple(PostConstruct.class)) + .and((clazz, annotations, index) -> annotations.isEmpty()) + .defaultScope(BuiltinScope.SINGLETON) + .unremovable() + .build()); } }).produces(AutoAddScopeBuildItem.class).build(); }).setLogRecordPredicate(log -> "AutoScopeBuildItemTest".equals(log.getLoggerName())) @@ -76,6 +93,7 @@ public void testBeans() { assertEquals(notABean1.ping(), Arc.container().instance(NotABean.class).get().ping()); } + @Named static class SimpleBean { private String id; From 625855d33e9f808c789856baca206b53e162f2f9 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Tue, 15 Aug 2023 11:51:56 -0300 Subject: [PATCH 7/7] Fix potential NPE in quarkus-csrf-reactive when no MediaType is found - Fixes #35285 (cherry picked from commit 3c6944364aa3936e6353327da3947367e04de850) --- .../CsrfRequestResponseReactiveFilter.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java index 8fcba71e714b4..4c5eaa1517719 100644 --- a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java +++ b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java @@ -97,15 +97,16 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi } else if (config.verifyToken) { // unsafe HTTP method, token is required - if (!isMatchingMediaType(requestContext.getMediaType(), MediaType.APPLICATION_FORM_URLENCODED_TYPE) - && !isMatchingMediaType(requestContext.getMediaType(), MediaType.MULTIPART_FORM_DATA_TYPE)) { + MediaType mediaType = requestContext.getMediaType(); + if (!isMatchingMediaType(mediaType, MediaType.APPLICATION_FORM_URLENCODED_TYPE) + && !isMatchingMediaType(mediaType, MediaType.MULTIPART_FORM_DATA_TYPE)) { if (config.requireFormUrlEncoded) { - LOG.debugf("Request has the wrong media type: %s", requestContext.getMediaType().toString()); + LOG.debugf("Request has the wrong media type: %s", mediaType); requestContext.abortWith(badClientRequest()); return; } else { - LOG.debugf("Request has the media type: %s, skipping the token verification", - requestContext.getMediaType().toString()); + LOG.debugf("Request has the media type: %s, skipping the token verification", + mediaType); return; } } @@ -148,7 +149,16 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi } } + /** + * Compares if {@link MediaType} matches the expected type. + *

+ * Note: isCompatible is taking wildcards, which is why we individually compare types and subtypes, + * so if someone sends a Content-Type: * it will be marked as compatible which is a problem + */ private static boolean isMatchingMediaType(MediaType contentType, MediaType expectedType) { + if (contentType == null) { + return (expectedType == null); + } return contentType.getType().equals(expectedType.getType()) && contentType.getSubtype().equals(expectedType.getSubtype()); }