-
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 1 commit
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 |
---|---|---|
|
@@ -189,7 +189,7 @@ The `initialize()` method of `HibernateConstraintValidator` takes two parameters | |
* The `ConstraintDescriptor` of the constraint at hand. | ||
You can get access to the annotation using `ConstraintDescriptor#getAnnotation()`. | ||
* The `HibernateConstraintValidatorInitializationContext` which provides useful helpers and contextual | ||
information, such as the clock provider or the temporal validation tolerance. | ||
information, such as the clock provider, the temporal validation tolerance or the constraint validator payload. | ||
|
||
This extension is marked as incubating so it might be subject to change. | ||
The plan is to standardize it and to include it in Bean Validation in the future. | ||
|
@@ -210,6 +210,66 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/MyFutureVa | |
You should only implement one of the `initialize()` methods. Be aware that both are called when initializing the validator. | ||
==== | ||
|
||
[[constraint-validator-payload]] | ||
===== Passing a payload to the constraint validator | ||
|
||
From time to time, you might want to condition the constraint validator behavior on some external parameters. | ||
|
||
For instance, your zip code validator could vary depending on the locale of your application instance if you have one | ||
instance per country. | ||
Another requirement could be to have different behaviors on specific environments: the staging environment may not have | ||
access to some external production resources necessary for the correct functioning of a validator. | ||
|
||
The notion of constraint validator payload was introduced for all these use cases. | ||
It is an object passed from the `Validator` instance to each constraint validator via the `HibernateConstraintValidatorInitializationContext`. | ||
|
||
The example below shows how to set a constraint validator payload during the `ValidatorFactory` initialization. | ||
Unless you override this default value, all the ``Validator``s created by this `ValidatorFactory` will have this | ||
constraint validator payload value set. | ||
|
||
[[example-constraint-validator-payload-definition-validatorfactory]] | ||
.Defining a constraint validator payload during the `ValidatorFactory` initialization | ||
==== | ||
[source, JAVA, indent=0] | ||
---- | ||
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/constraintvalidatorpayload/ConstraintValidatorPayloadTest.java[tags=setConstraintValidatorPayloadDuringValidatorFactoryInitialization] | ||
---- | ||
==== | ||
|
||
Another option is to set the constraint validator payload per `Validator` using a context: | ||
|
||
[[example-constraint-validator-payload-definition-validatorcontext]] | ||
.Defining a constraint validator payload using a `Validator` context | ||
==== | ||
[source, JAVA, indent=0] | ||
---- | ||
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/constraintvalidatorpayload/ConstraintValidatorPayloadTest.java[tags=setConstraintValidatorPayloadInValidatorContext] | ||
---- | ||
==== | ||
|
||
Once you have set the constraint validator payload, it is time to use it in your constrait validators as shown in the example below: | ||
|
||
[[example-constraint-validator-payload-usage]] | ||
.Using the constraint validator payload in a constraint validator | ||
==== | ||
[source, JAVA, indent=0] | ||
---- | ||
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/constraintvalidatorpayload/ZipCodeValidator.java[tags=include] | ||
---- | ||
==== | ||
|
||
`HibernateConstraintValidatorInitializationContext#getConstraintValidatorPayload()` has a type parameter | ||
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. Question on that one, what happens if I have registered two payloads of type 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. If I understand your question right you can't have two payloads, because the last one wins. |
||
and returns the payload only if the payload is of the given type. | ||
|
||
[NOTE] | ||
==== | ||
It is important to note that the constraint validator payload is different from the dynamic payload you can include in | ||
the constraint violation raised. | ||
|
||
The whole purpose of this constraint validator payload is to be used to condition the behavior of your constraint validators. | ||
It is not included in the constraint violations. | ||
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. , unless a specific 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 leave that up to @gsmet if he wants to mention that. |
||
==== | ||
|
||
[[validator-customconstraints-errormessage]] | ||
==== The error message | ||
|
||
|
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[] |
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.
s/constrait/constraint/
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.
Fixed.