Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hibernate Validator: Correctly detect custom ValueExtractor beans #20382

Merged
merged 4 commits into from
Sep 27, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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 @@ -32,6 +33,9 @@
@Recorder
public class HibernateValidatorRecorder {

private static final TypeLiteral<ValueExtractor<?>> TYPE_LITERAL_VALUE_EXTRACTOR_WITH_WILDCARD = new TypeLiteral<ValueExtractor<?>>() {
};
Comment on lines +36 to +37
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the only way I found to select all beans implementing ValueExtractor<Something>.

Strangely, .select(ValueExtractor.class) does not match a bean implementing ValueExtractor<List<?>>, for example.

I say strangely, because you can definitely assign ValueExtractor<List<?>> to a variable of type ValueExtractor (the raw type). So if you consider that .select(ValueExtractor.class) should select all beans that can be assigned to ValueExtractor (the raw type), then beans implementing ValueExtractor<List<?>> should definitely be selected.

But no, that's not how it works, apparently.

According to the javadoc on io.quarkus.arc.impl.BeanTypeAssignabilityRules#matches(java.lang.Class<?>, java.lang.reflect.ParameterizedType):

     * A parameterized bean type is considered assignable to a raw required type if the raw types are identical and all type
     * parameters of the bean type are
     * either unbounded type variables or java.lang.Object.

So according to this definition, ValueExtractor<T> is assignable to ValueExtractor, but ValueExtractor<List<?>> is not.

@stuartwdouglas is this on purpose? Or should we adjust the implementation of io.quarkus.arc.impl.BeanTypeAssignabilityRules#matches(java.lang.Class<?>, java.lang.reflect.ParameterizedType)?

Copy link
Member

Choose a reason for hiding this comment

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

This is a question for @mkouba, but this sounds like a bug.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, sorry for the noise. Created #20410 for @mkouba :)

Copy link
Contributor

Choose a reason for hiding this comment

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

This works as required by the spec: "A parameterized bean type is considered assignable to a raw required type if the raw types are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object"; see also https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#assignable_parameters


public BeanContainerListener initializeValidatorFactory(Set<Class<?>> classesToBeValidated,
Set<String> detectedBuiltinConstraints,
boolean hasXmlConfiguration, boolean jpaInClasspath,
Expand Down Expand Up @@ -132,7 +136,10 @@ public void created(BeanContainer container) {

// Automatically add all the values extractors declared as beans
for (ValueExtractor<?> valueExtractor : Arc.container().beanManager().createInstance()
.select(ValueExtractor.class)) {
// 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)) {
configuration.addValueExtractor(valueExtractor);
}

Expand Down