From f5cf52932da21b8c78d2e73281e9d135cf9666c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 19 Jan 2023 13:49:59 +0100 Subject: [PATCH] Work around the fact that ValueExtractor<*> is no longer a bean type in most cases --- .../HibernateValidatorProcessor.java | 16 +++++- .../runtime/HibernateValidatorRecorder.java | 51 +++++++++++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java index 35dc92ce4a455..1893714d8b3a0 100644 --- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java +++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java @@ -196,7 +196,7 @@ public boolean test(BeanInfo beanInfo) { return beanInfo.hasType(CONSTRAINT_VALIDATOR) || beanInfo.hasType(CONSTRAINT_VALIDATOR_FACTORY) || beanInfo.hasType(MESSAGE_INTERPOLATOR) || beanInfo.hasType(TRAVERSABLE_RESOLVER) || beanInfo.hasType(PARAMETER_NAME_PROVIDER) || beanInfo.hasType(CLOCK_PROVIDER) - || beanInfo.hasType(VALUE_EXTRACTOR) || beanInfo.hasType(SCRIPT_EVALUATOR_FACTORY) + || beanInfo.hasType(SCRIPT_EVALUATOR_FACTORY) || beanInfo.hasType(GETTER_PROPERTY_SELECTION_STRATEGY) || beanInfo.hasType(LOCALE_RESOLVER) || beanInfo.hasType(PROPERTY_NODE_NAME_PROVIDER) @@ -216,6 +216,7 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC BuildProducer feature, BuildProducer beanContainerListener, BuildProducer beanValidationAnnotations, + BuildProducer unremovableBeans, ShutdownContextBuildItem shutdownContext, List configClasses, List additionalJaxRsResourceMethodAnnotations, @@ -359,9 +360,22 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC classesToBeValidated.add(recorderContext.classProxy(className.toString())); } + // Prevent the removal of ValueExtractor beans + // and collect all classes implementing ValueExtractor (for use in HibernateValidatorRecorder) + Set valueExtractorClassNames = new HashSet<>(); + for (ClassInfo valueExtractorType : indexView.getAllKnownImplementors(VALUE_EXTRACTOR)) { + valueExtractorClassNames.add(valueExtractorType.name()); + } + unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(valueExtractorClassNames)); + Set> valueExtractorClassProxies = new HashSet<>(); + for (DotName className : valueExtractorClassNames) { + valueExtractorClassProxies.add(recorderContext.classProxy(className.toString())); + } + beanContainerListener .produce(new BeanContainerListenerBuildItem( recorder.initializeValidatorFactory(classesToBeValidated, detectedBuiltinConstraints, + valueExtractorClassProxies, hasXmlConfiguration(), capabilities.isPresent(Capability.HIBERNATE_ORM), shutdownContext, diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java index d6e6b1725e1e8..635dbaf3be022 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java @@ -1,10 +1,11 @@ package io.quarkus.hibernate.validator.runtime; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Supplier; -import javax.enterprise.util.TypeLiteral; import javax.validation.ClockProvider; import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; @@ -36,11 +37,8 @@ @Recorder public class HibernateValidatorRecorder { - private static final TypeLiteral> TYPE_LITERAL_VALUE_EXTRACTOR_WITH_WILDCARD = new TypeLiteral>() { - }; - public BeanContainerListener initializeValidatorFactory(Set> classesToBeValidated, - Set detectedBuiltinConstraints, + Set detectedBuiltinConstraints, Set> valueExtractorClasses, boolean hasXmlConfiguration, boolean jpaInClasspath, ShutdownContext shutdownContext, LocalesBuildTimeConfig localesBuildTimeConfig, HibernateValidatorBuildTimeConfig hibernateValidatorBuildTimeConfig) { @@ -143,11 +141,15 @@ public void created(BeanContainer container) { } // Automatically add all the values extractors declared as beans - for (ValueExtractor valueExtractor : Arc.container().beanManager().createInstance() - // Apparently passing ValueExtractor.class - // won't match beans implementing ValueExtractor, - // so we need a type literal here. - .select(TYPE_LITERAL_VALUE_EXTRACTOR_WITH_WILDCARD)) { + for (ValueExtractor valueExtractor : HibernateValidatorRecorder + // We cannot do something like `instance(...).select(ValueExtractor.class)`, + // because `ValueExtractor` is usually implemented + // as a parameterized type with wildcards, + // and the CDI spec does not consider such types as bean types. + // We work around that by listing all classes implementing `ValueExtractor` at build time, + // then retrieving all bean instances implementing those types here. + // See https://github.com/quarkusio/quarkus/pull/30447 + .> uniqueBeanInstances(valueExtractorClasses)) { configuration.addValueExtractor(valueExtractor); } @@ -176,6 +178,35 @@ public void run() { return beanContainerListener; } + // Ideally we'd retrieve all instances of a set of bean types + // simply by calling something like ArcContainer#select(Set) + // but that method does not exist. + // This method acts as a replacement. + private static Iterable uniqueBeanInstances(Set> classes) { + Set beanIds = new HashSet<>(); + for (Class clazz : classes) { + for (InstanceHandle handle : Arc.container().select(clazz).handles()) { + if (!handle.isAvailable()) { + continue; + } + // A single bean can have multiple types. + // To avoid returning duplicate instances of the same bean, + // we first retrieve all bean IDs, deduplicate those, + // then retrieve the instance for each bean. + // Note that just retrieving all instances and putting them in a identity-based Set + // would not work, because beans can have the dependent pseudo-scope, + // in which case we'd have two instances of the same bean. + beanIds.add(handle.getBean().getIdentifier()); + } + } + List instances = new ArrayList<>(); + for (String beanId : beanIds) { + var arcContainer = Arc.container(); + instances.add(arcContainer.instance(arcContainer. bean(beanId)).get()); + } + return instances; + } + public Supplier resteasyConfigSupportSupplier(boolean jsonDefault) { return new Supplier() {