From 780d683f93a71be7a859d9162f7017cea3ec1735 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 22 Feb 2022 14:33:40 +0100 Subject: [PATCH] Hibernate Validator - Allow setting the expression language feature level I only exposed the constraint one and not the custom violation one as I think it is bad practice to globally change the custom violation one. Fix #23838 --- ...intExpressionLanguageFeatureLevelTest.java | 75 +++++++++++++++++++ .../HibernateValidatorBuildTimeConfig.java | 28 +++++++ .../runtime/HibernateValidatorRecorder.java | 5 ++ 3 files changed, 108 insertions(+) create mode 100644 extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ConstraintExpressionLanguageFeatureLevelTest.java diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ConstraintExpressionLanguageFeatureLevelTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ConstraintExpressionLanguageFeatureLevelTest.java new file mode 100644 index 0000000000000..f513348a2bdfb --- /dev/null +++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ConstraintExpressionLanguageFeatureLevelTest.java @@ -0,0 +1,75 @@ +package io.quarkus.hibernate.validator.test; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Set; + +import javax.inject.Inject; +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintViolation; +import javax.validation.Payload; +import javax.validation.Validator; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +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 ConstraintExpressionLanguageFeatureLevelTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(BeanMethodsConstraint.class, BeanMethodsConstraintStringValidator.class, BeanMethodsBean.class) + .add(new StringAsset( + "quarkus.hibernate-validator.expression-language.constraint-expression-feature-level=bean-methods"), + "application.properties")); + + @Inject + Validator validator; + + @Test + public void testConstraintExpressionFeatureLevel() { + Set> violations = validator.validate(new BeanMethodsBean()); + assertEquals("Method execution: a", violations.iterator().next().getMessage()); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { BeanMethodsConstraintStringValidator.class }) + private @interface BeanMethodsConstraint { + String message() default "Method execution: ${'aaaa'.substring(0, 1)}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class BeanMethodsConstraintStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class BeanMethodsBean { + + @BeanMethodsConstraint + public String value; + } +} diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java index d379f2624f645..9c65d18486ebd 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorBuildTimeConfig.java @@ -1,5 +1,9 @@ package io.quarkus.hibernate.validator.runtime; +import java.util.Optional; + +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; + import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigItem; @@ -22,6 +26,12 @@ public class HibernateValidatorBuildTimeConfig { @ConfigDocSection public HibernateValidatorMethodBuildTimeConfig methodValidation; + /** + * Expression Language. + */ + @ConfigDocSection + public HibernateValidatorExpressionLanguageBuildTimeConfig expressionLanguage; + @ConfigGroup public static class HibernateValidatorMethodBuildTimeConfig { @@ -72,4 +82,22 @@ public static class HibernateValidatorMethodBuildTimeConfig { @ConfigItem(defaultValue = "false") public boolean allowMultipleCascadedValidationOnReturnValues; } + + @ConfigGroup + public static class HibernateValidatorExpressionLanguageBuildTimeConfig { + + /** + * Configure the Expression Language feature level for constraints, allowing the selection of + * Expression Language features available for message interpolation. + *

+ * This property only affects the EL feature level of "static" constraint violation messages set through the + * message attribute of constraint annotations. + *

+ * In particular, it doesn't affect the default EL feature level for custom violations + * created programmatically in validator implementations. + * The feature level for those can only be configured directly in the validator implementation. + */ + @ConfigItem(defaultValueDocumentation = "bean-properties") + public Optional constraintExpressionFeatureLevel; + } } 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 be6db637c8c36..95a8691c97538 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 @@ -68,6 +68,11 @@ public void created(BeanContainer container) { .defaultLocale(localesBuildTimeConfig.defaultLocale) .beanMetaDataClassNormalizer(new ArcProxyBeanMetaDataClassNormalizer()); + if (hibernateValidatorBuildTimeConfig.expressionLanguage.constraintExpressionFeatureLevel.isPresent()) { + configuration.constraintExpressionLanguageFeatureLevel( + hibernateValidatorBuildTimeConfig.expressionLanguage.constraintExpressionFeatureLevel.get()); + } + InstanceHandle configuredConstraintValidatorFactory = Arc.container() .instance(ConstraintValidatorFactory.class); if (configuredConstraintValidatorFactory.isAvailable()) {