-
Notifications
You must be signed in to change notification settings - Fork 565
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
HV-1529 Add dynamic payload to HibernateConstraintValidator #890
Changes from all commits
10b8aeb
ac2046a
44238e3
c7957ab
e82181e
1bdeac8
274dcea
fe2ee4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload; | ||
|
||
import javax.validation.Validation; | ||
import javax.validation.Validator; | ||
import javax.validation.ValidatorFactory; | ||
|
||
import org.hibernate.validator.HibernateValidator; | ||
import org.hibernate.validator.HibernateValidatorFactory; | ||
import org.junit.Test; | ||
|
||
@SuppressWarnings("unused") | ||
public class ConstraintValidatorPayloadTest { | ||
|
||
@Test | ||
public void setConstraintValidatorPayloadDuringValidatorFactoryInitialization() { | ||
//tag::setConstraintValidatorPayloadDuringValidatorFactoryInitialization[] | ||
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) | ||
.configure() | ||
.constraintValidatorPayload( "US" ) | ||
.buildValidatorFactory(); | ||
|
||
Validator validator = validatorFactory.getValidator(); | ||
//end::setConstraintValidatorPayloadDuringValidatorFactoryInitialization[] | ||
} | ||
|
||
@Test | ||
public void setConstraintValidatorPayloadInValidatorContext() { | ||
//tag::setConstraintValidatorPayloadInValidatorContext[] | ||
HibernateValidatorFactory hibernateValidatorFactory = Validation.byDefaultProvider() | ||
.configure() | ||
.buildValidatorFactory() | ||
.unwrap( HibernateValidatorFactory.class ); | ||
|
||
Validator validator = hibernateValidatorFactory.usingContext() | ||
.constraintValidatorPayload( "US" ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I'm seeing the API used, I'm wondering whether it should be
Btw. what happens if I register multiple payloads of the same type? Last one wins, or is an exception raised? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, no, it seems the implementation actually doesn't allow what I had in mind. Wondering whether that's not a bit too limited? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We had the exact same discussion about the other payloads recently and you answered me the user could use a Map or a bean of some sort. I don't think it's limited and it's consistent with the other payloads in the API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I fully agree. |
||
.getValidator(); | ||
|
||
// [...] US specific validation checks | ||
|
||
validator = hibernateValidatorFactory.usingContext() | ||
.constraintValidatorPayload( "FR" ) | ||
.getValidator(); | ||
|
||
// [...] France specific validation checks | ||
|
||
//end::setConstraintValidatorPayloadInValidatorContext[] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload; | ||
|
||
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.TYPE_USE; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import javax.validation.Constraint; | ||
import javax.validation.Payload; | ||
|
||
@Target({ METHOD, FIELD, ANNOTATION_TYPE, TYPE_USE }) | ||
@Retention(RUNTIME) | ||
@Constraint(validatedBy = ZipCodeValidator.class) | ||
@Documented | ||
public @interface ZipCode { | ||
|
||
String message() default "{org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload.ZipCode.message}"; | ||
|
||
Class<?>[] groups() default { }; | ||
|
||
Class<? extends Payload>[] payload() default { }; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//tag::include[] | ||
package org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload; | ||
|
||
//end::include[] | ||
|
||
import javax.validation.ConstraintValidatorContext; | ||
import javax.validation.metadata.ConstraintDescriptor; | ||
|
||
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; | ||
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; | ||
|
||
//tag::include[] | ||
public class ZipCodeValidator implements HibernateConstraintValidator<ZipCode, String> { | ||
|
||
public String countryCode; | ||
|
||
@Override | ||
public void initialize(ConstraintDescriptor<ZipCode> constraintDescriptor, | ||
HibernateConstraintValidatorInitializationContext initializationContext) { | ||
this.countryCode = initializationContext | ||
.getConstraintValidatorPayload( String.class ); | ||
} | ||
|
||
@Override | ||
public boolean isValid(String object, ConstraintValidatorContext constraintContext) { | ||
if ( object == null ) { | ||
return true; | ||
} | ||
|
||
boolean isValid = false; | ||
|
||
if ( "US".equals( countryCode ) ) { | ||
// checks specific to the United States | ||
} | ||
else if ( "FR".equals( countryCode ) ) { | ||
// checks specific to France | ||
} | ||
else { | ||
// ... | ||
} | ||
|
||
return isValid; | ||
} | ||
} | ||
//end::include[] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -298,4 +298,16 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate | |
*/ | ||
@Incubating | ||
HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance); | ||
|
||
/** | ||
* Allows to set a payload which will be passed to the constraint validators. | ||
* | ||
* @param constraintValidatorPayload the payload passed to constraint validators | ||
* | ||
* @return {@code this} following the chaining method pattern | ||
* | ||
* @since 6.0.8 | ||
*/ | ||
@Incubating | ||
HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How comes this is exposed at the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With my PR you don't have to create a new VF for each request, you can just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was added at my specific request. I don't think it's that absurd to imagine someone needing to pass information at the VF level to the validators (might be info about the environment for instance, staging/production, with validation rules differing depending on the environment). And it was also because I wanted the symmetry with the other features. I think it's better with it than without. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have to agree with @gsmet - it's better with it than without. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My concern is that this this may be people lead to do the wrong thing and provide volatile contextual information to the VF instead of getting a (rather cheap) validator instance with such information. Really that's the purpose of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you supposing that the information is volatile? My environment example is not about volatile information. I would say it makes as much sense as any other configuration knobs we have at the VF level: we want it to be the default value for all Validator creations.
What you're asking is special casing this one, not the other way around. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, let's do it then. I think the confusion stems from the very generic nature of this thing (just passing in and getting out any object). For the originally discussed use case (locale), it doesn't make really sense on the VF, but yes, perhaps for others. On your environment example, can you detail that a bit, i.e. when would validation be different per environment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm... specific to my use case maybe we could run different instances of an application - each instance for a different country. Each country should use it's own locale - no matter what the user locale in the request is. E.g. app instances that server There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gunnarmorling @mkurz Yes, that's basically what I had in mind. Or a different behavior in staging for instance because staging doesn't have access to some component required for validation (external service only available for production for instance). I know you shouldn't do that but I also know from experience it happens. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question on that one, what happens if I have registered two payloads of type
A
andB
,B
extendsA
and I requestA
? Is an order of precedence defined?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand your question right you can't have two payloads, because the last one wins.