Hi
I want to have my password component validated:
Code:
public class PasswordView {
private String password;
private String passwordRepeat;
}
My requirements are:
if password is blank ( passwordRepeat does not matter) - return single error that password is blank
if password is less than 6 characters( passwordRepeat does not matter) - return single errror that password length is less than 6
if (password.length >=6 and !password.equals(passwordRepeat)) - return two errors that password and passwordRepeat do not match.
My initial implementation is:
create @PasswordFieldConstraint like:
Code:
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@NotBlank
@Size(min = 6, max = 25)
public @interface PasswordContraint {
String message() default "{InvalidPassword.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and apply it on field:
Code:
public class PasswordView {
@PasswordContraint
private String password;
private String passwordRepeat;
}
then create class level constraint:
Code:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Constraint( validatedBy = { PasswordViewValidator.class } )
public @interface PasswordViewConstraint {
String message() default "{PasswordNotMatch.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
PasswordViewValidator:
Code:
PasswordViewValidator{
private String messageTemplate;
@Override
public void initialize(PasswordViewConstraint constraintAnnotation) {
messageTemplate = constraintAnnotation.message();
}
@Override
public boolean isValid(PasswordView value,
ConstraintValidatorContext context) {
boolean result = isValid(value);
if(!result){
setConstraintViolations(context);
}
return result;
}
private void setConstraintViolations(ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(messageTemplate)
.addNode("password").addConstraintViolation();
context.buildConstraintViolationWithTemplate(messageTemplate).addNode("passwordRepeat")
.addConstraintViolation();
}
private boolean isValid(PasswordView value) {
if(value == null){
return true;
}
return password.equals(value.getPasswordRepeat());
}
}
the problem i faced is that PasswordViewValidator is executed before @PasswordContraint constraints (@Size and @NotBlank). It means that
i got 2 errors when PasswordView = {password ='a' and passwordRepeat = 'b'} - the first error from @PasswordViewConstraint and the sec from @PasswordContraint.
I can force PasswordViewValidator to return true when password is null/blank or password length is less than 6 but i would like to avoid this kind of checking, since it seems like a tight coupling between @PasswordConstraint internals (attribute value of composing annotation @Size) and PasswordViewValidator implementation.
I can workaround by using GroupSequence to specy order between @PasswordConstraint and @PasswordViewConstraint but maybe someone has a better idea.
Thanks in advance.