Skip to content

Commit

Permalink
Merge pull request quarkusio#20382 from yrodiere/i20347-valueextractor
Browse files Browse the repository at this point in the history
Hibernate Validator: Correctly detect custom ValueExtractor beans
  • Loading branch information
gsmet authored Sep 27, 2021
2 parents c127aa9 + 80f7b0e commit c5b743c
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.quarkus.hibernate.validator.test.valueextractor;

import static org.assertj.core.api.Assertions.assertThat;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotBlank;
import javax.validation.valueextraction.ExtractedValue;
import javax.validation.valueextraction.ValueExtractor;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

@Disabled("Reproduces https://github.com/quarkusio/quarkus/issues/20375, not yet fixed")
public class ApplicationScopedCustomValueExtractorTest {

@Inject
ValidatorFactory validatorFactory;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.addClasses(TestBean.class, Container.class, ApplicationScopedContainerValueExtractor.class));

@Test
public void testApplicationScopedCustomValueExtractor() {
assertThat(validatorFactory.getValidator().validate(new TestBean())).hasSize(1);
}

public static class TestBean {
public Container<@NotBlank String> constrainedContainer;

public TestBean() {
Container<String> invalidContainer = new Container<>();
invalidContainer.value = " ";

this.constrainedContainer = invalidContainer;
}
}

@ApplicationScoped
public static class ApplicationScopedContainerValueExtractor
implements ValueExtractor<Container<@ExtractedValue ?>> {

@Override
public void extractValues(Container<?> originalValue, ValueReceiver receiver) {
receiver.value("someName", originalValue.value);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.quarkus.hibernate.validator.test.valueextractor;

// TODO for some reason, this must be a top-level type, not a nested/inner type,
// in order for annotations on type parameters to be detected.
// See NestedContainerTypeCustomValueExtractorTest
public class Container<T> {

public T value;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.quarkus.hibernate.validator.test.valueextractor;

import static org.assertj.core.api.Assertions.assertThat;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotBlank;
import javax.validation.valueextraction.ExtractedValue;
import javax.validation.valueextraction.ValueExtractor;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

@Disabled("Reproduces https://github.com/quarkusio/quarkus/issues/20377, not yet fixed")
public class NestedContainerTypeCustomValueExtractorTest {

@Inject
ValidatorFactory validatorFactory;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.addClasses(TestBean.class, NestedContainerType.class, NestedContainerClassValueExtractor.class));

@Test
public void testNestedContainerTypeValueExtractor() {
assertThat(validatorFactory.getValidator().validate(new TestBean())).hasSize(1);
}

public static class TestBean {
public NestedContainerType<@NotBlank String> constrainedContainer;

public TestBean() {
NestedContainerType<String> invalidContainer = new NestedContainerType<>();
invalidContainer.value = " ";

this.constrainedContainer = invalidContainer;
}
}

public static class NestedContainerType<T> {

public T value;

}

@Singleton
public static class NestedContainerClassValueExtractor
implements ValueExtractor<NestedContainerType<@ExtractedValue ?>> {

@Override
public void extractValues(NestedContainerType<?> originalValue, ValueReceiver receiver) {
receiver.value("someName", originalValue.value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkus.hibernate.validator.test.valueextractor;

import static org.assertj.core.api.Assertions.assertThat;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotBlank;
import javax.validation.valueextraction.ExtractedValue;
import javax.validation.valueextraction.ValueExtractor;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class SingletonCustomValueExtractorTest {

@Inject
ValidatorFactory validatorFactory;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.addClasses(TestBean.class, Container.class, SingletonContainerValueExtractor.class));

@Test
public void testSingletonCustomValueExtractor() {
assertThat(validatorFactory.getValidator().validate(new TestBean())).hasSize(1);
}

public static class TestBean {
public Container<@NotBlank String> constrainedContainer;

public TestBean() {
Container<String> invalidContainer = new Container<>();
invalidContainer.value = " ";

this.constrainedContainer = invalidContainer;
}
}

@Singleton
public static class SingletonContainerValueExtractor
implements ValueExtractor<Container<@ExtractedValue ?>> {

@Override
public void extractValues(Container<?> originalValue, ValueReceiver receiver) {
receiver.value("someName", originalValue.value);
}
}

}
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<?>>() {
};

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

0 comments on commit c5b743c

Please sign in to comment.