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 - Allow customization of the ValidatorFactory #26658

Merged
merged 1 commit into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -75,6 +75,7 @@
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.hibernate.validator.HibernateValidatorFactoryCustomizer;
import io.quarkus.hibernate.validator.runtime.DisableLoggingFeature;
import io.quarkus.hibernate.validator.runtime.HibernateValidatorBuildTimeConfig;
import io.quarkus.hibernate.validator.runtime.HibernateValidatorRecorder;
Expand Down Expand Up @@ -108,6 +109,9 @@ class HibernateValidatorProcessor {
private static final DotName PROPERTY_NODE_NAME_PROVIDER = DotName
.createSimple(PropertyNodeNameProvider.class.getName());

private static final DotName CONSTRAINT_MAPPING_PROVIDER = DotName
.createSimple(HibernateValidatorFactoryCustomizer.class.getName());

private static final DotName CONSTRAINT_VALIDATOR = DotName.createSimple(ConstraintValidator.class.getName());
private static final DotName VALUE_EXTRACTOR = DotName.createSimple(ValueExtractor.class.getName());

Expand Down Expand Up @@ -187,7 +191,8 @@ public boolean test(BeanInfo beanInfo) {
|| beanInfo.hasType(VALUE_EXTRACTOR) || beanInfo.hasType(SCRIPT_EVALUATOR_FACTORY)
|| beanInfo.hasType(GETTER_PROPERTY_SELECTION_STRATEGY)
|| beanInfo.hasType(LOCALE_RESOLVER)
|| beanInfo.hasType(PROPERTY_NODE_NAME_PROVIDER);
|| beanInfo.hasType(PROPERTY_NODE_NAME_PROVIDER)
|| beanInfo.hasType(CONSTRAINT_MAPPING_PROVIDER);
tmihalac marked this conversation as resolved.
Show resolved Hide resolved
}
}));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

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

import javax.inject.Inject;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;

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 HibernateValidatorFactoryCustomizerTest {

@Inject
ValidatorFactory validatorFactory;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.addClasses(MyMultipleHibernateValidatorFactoryCustomizer.class, MyEmailValidator.class,
MyNumValidator.class));

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

static class TestBean {
@Email
public String email;

@Min(-1)
public int num;

public TestBean() {
this.email = "[email protected]";
this.num = -1;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

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

import javax.inject.Inject;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;

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 MultipleHibernateFactoryCustomizersTest {

@Inject
ValidatorFactory validatorFactory;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.addClasses(MyEmailHibernateValidatorFactoryCustomizer.class, MyNumberHibernateValidatorFactoryCustomizer.class,
MyEmailValidator.class,
MyNumValidator.class));

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

static class TestBean {
@Email
public String email;

@Min(-1)
public int num;

public TestBean() {
this.email = "[email protected]";
this.num = -1;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

import javax.enterprise.context.ApplicationScoped;
import javax.validation.constraints.Email;

import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;

import io.quarkus.hibernate.validator.HibernateValidatorFactoryCustomizer;

@ApplicationScoped
public class MyEmailHibernateValidatorFactoryCustomizer implements HibernateValidatorFactoryCustomizer {

@Override
public <T extends BaseHibernateValidatorConfiguration<T>> void customize(T configuration) {
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
.constraintDefinition(Email.class)
.includeExistingValidators(false)
.validatedBy(MyEmailValidator.class);

configuration.addMapping(constraintMapping);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.Email;

public class MyEmailValidator implements ConstraintValidator<Email, CharSequence> {
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
return "[email protected]".contentEquals(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

import javax.enterprise.context.ApplicationScoped;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;

import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;

import io.quarkus.hibernate.validator.HibernateValidatorFactoryCustomizer;

@ApplicationScoped
public class MyMultipleHibernateValidatorFactoryCustomizer implements HibernateValidatorFactoryCustomizer {

@Override
public <T extends BaseHibernateValidatorConfiguration<T>> void customize(T configuration) {
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
.constraintDefinition(Email.class)
.includeExistingValidators(false)
.validatedBy(MyEmailValidator.class);

configuration.addMapping(constraintMapping);

constraintMapping = configuration.createConstraintMapping();

constraintMapping
.constraintDefinition(Min.class)
.includeExistingValidators(false)
.validatedBy(MyNumValidator.class);

configuration.addMapping(constraintMapping);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.Min;

public class MyNumValidator implements ConstraintValidator<Min, Integer> {
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value >= 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.validator.test.validatorfactory;

import javax.enterprise.context.ApplicationScoped;
import javax.validation.constraints.Min;

import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;

import io.quarkus.hibernate.validator.HibernateValidatorFactoryCustomizer;

@ApplicationScoped
public class MyNumberHibernateValidatorFactoryCustomizer implements HibernateValidatorFactoryCustomizer {

@Override
public <T extends BaseHibernateValidatorConfiguration<T>> void customize(T configuration) {
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
.constraintDefinition(Min.class)
.includeExistingValidators(false)
.validatedBy(MyNumValidator.class);

configuration.addMapping(constraintMapping);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.hibernate.validator;

import org.hibernate.validator.BaseHibernateValidatorConfiguration;

/**
* Meant to be implemented by a CDI bean that provides arbitrary customization for the default
* {@link javax.validation.ValidatorFactory}.
* <p>
* All implementations that are registered as CDI beans are taken into account when producing the default
* {@link javax.validation.ValidatorFactory}.
* <p>
* Customizers are applied in the order of {@link javax.annotation.Priority}.
*/
public interface HibernateValidatorFactoryCustomizer {

<T extends BaseHibernateValidatorConfiguration<T>> void customize(T configuration);
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit late to the party, but... These generics are not useful at all, as far as I can see you could just do this.

@gsmet Should I send a PR?

Suggested change
<T extends BaseHibernateValidatorConfiguration<T>> void customize(T configuration);
void customize(BaseHibernateValidatorConfiguration<?> configuration);

Copy link
Member

Choose a reason for hiding this comment

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

Yeah sure.

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.hibernate.validator.runtime;

import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

Expand All @@ -25,6 +26,7 @@
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.arc.runtime.BeanContainerListener;
import io.quarkus.hibernate.validator.HibernateValidatorFactoryCustomizer;
import io.quarkus.hibernate.validator.runtime.jaxrs.ResteasyConfigSupport;
import io.quarkus.runtime.LocalesBuildTimeConfig;
import io.quarkus.runtime.ShutdownContext;
Expand Down Expand Up @@ -148,6 +150,15 @@ public void created(BeanContainer container) {
configuration.addValueExtractor(valueExtractor);
}

List<InstanceHandle<HibernateValidatorFactoryCustomizer>> constraintMappingProviderList = Arc.container()
.listAll(HibernateValidatorFactoryCustomizer.class);
for (InstanceHandle<HibernateValidatorFactoryCustomizer> cmpInstanceHandle : constraintMappingProviderList) {
if (cmpInstanceHandle.isAvailable()) {
final HibernateValidatorFactoryCustomizer hibernateValidatorFactoryCustomizer = cmpInstanceHandle.get();
hibernateValidatorFactoryCustomizer.customize(configuration);
}
}

ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
ValidatorHolder.initialize(validatorFactory);

Expand Down