Skip to content

Commit

Permalink
Work around the fact that ValueExtractor<*> is no longer a bean type …
Browse files Browse the repository at this point in the history
…in most cases
  • Loading branch information
yrodiere committed Jan 19, 2023
1 parent 0c3c7e1 commit f5cf529
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -216,6 +216,7 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC
BuildProducer<FeatureBuildItem> feature,
BuildProducer<BeanContainerListenerBuildItem> beanContainerListener,
BuildProducer<BeanValidationAnnotationsBuildItem> beanValidationAnnotations,
BuildProducer<UnremovableBeanBuildItem> unremovableBeans,
ShutdownContextBuildItem shutdownContext,
List<ConfigClassBuildItem> configClasses,
List<AdditionalJaxRsResourceMethodAnnotationsBuildItem> additionalJaxRsResourceMethodAnnotations,
Expand Down Expand Up @@ -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<DotName> valueExtractorClassNames = new HashSet<>();
for (ClassInfo valueExtractorType : indexView.getAllKnownImplementors(VALUE_EXTRACTOR)) {
valueExtractorClassNames.add(valueExtractorType.name());
}
unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(valueExtractorClassNames));
Set<Class<?>> 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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,11 +37,8 @@
@Recorder
public class HibernateValidatorRecorder {

private static final TypeLiteral<ValueExtractor<?>> TYPE_LITERAL_VALUE_EXTRACTOR_WITH_WILDCARD = new TypeLiteral<ValueExtractor<?>>() {
};

public BeanContainerListener initializeValidatorFactory(Set<Class<?>> classesToBeValidated,
Set<String> detectedBuiltinConstraints,
Set<String> detectedBuiltinConstraints, Set<Class<?>> valueExtractorClasses,
boolean hasXmlConfiguration, boolean jpaInClasspath,
ShutdownContext shutdownContext, LocalesBuildTimeConfig localesBuildTimeConfig,
HibernateValidatorBuildTimeConfig hibernateValidatorBuildTimeConfig) {
Expand Down Expand Up @@ -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<NotAWildcard>,
// 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
.<ValueExtractor<?>> uniqueBeanInstances(valueExtractorClasses)) {
configuration.addValueExtractor(valueExtractor);
}

Expand Down Expand Up @@ -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<Type>)
// but that method does not exist.
// This method acts as a replacement.
private static <T> Iterable<T> uniqueBeanInstances(Set<Class<?>> classes) {
Set<String> 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<T> instances = new ArrayList<>();
for (String beanId : beanIds) {
var arcContainer = Arc.container();
instances.add(arcContainer.instance(arcContainer.<T> bean(beanId)).get());
}
return instances;
}

public Supplier<ResteasyConfigSupport> resteasyConfigSupportSupplier(boolean jsonDefault) {
return new Supplier<ResteasyConfigSupport>() {

Expand Down

0 comments on commit f5cf529

Please sign in to comment.