I have implemented a custom annotation @Password
to perform validation on an argument of my method setPassword()
. The annotation is defined like this:
// Password.java
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.validation.*;
@Target({METHOD, FIELD, PARAMETER, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
@Documented
public @interface Password {
String message() default PasswordValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And the current implementation of the validator is this:
// PasswordValidator.java
package utils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordValidator implements ConstraintValidator<Password, String> {
/* Default error message */
final static public String message = "error.invalid.password";
/**
* Validator init
* Can be used to initialize the validation based on parameters
* passed to the annotation.
*/
public void initialize(Password constraintAnnotation) {
System.out.println("in initialize()");
}
public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
System.out.println("in isValid()");
return false;
}
}
Note that in the current implementation isValid()
always returns false. The reason will be apparent shortly.
My usage of the validator is in a class User
. For brevity I won't post the whole source here, but the relevant parts are:
package models;
import utils.Password;
// other imports omitted
@Entity
@Table(name = "users", schema="public")
public class User {
@Required
private String password;
...
public void setPassword(@Password String clearPassword) {
try {
this.password = HashHelper.createPassword(clearPassword);
} catch (AppException e) {
e.printStackTrace();
}
}
...
}
The basic idea is that I use the User class to store a hashed password for a user, but before setting the hashed password, I (would like to) run validation on the unhashed password (i.e. clearPassword
).
The problem I am having is that this validation is not taking place. In the current implementation, it should (according to my understanding) always throw a ConstraintViolationException
because isValid()
always returns false, but this is not the case.
I have checked that the annotation is being attached to the method argument by calling (in another part of the application) something along the lines of:
Method method = user.getClass().getMethod("setPassword", new Class[] { String.class });
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
System.out.println(method.getName() + ": " + parameterAnnotations[0][0]);
which produces the following output:
setPassword:@utils.Password(message=error.invalid.password, payload=[], groups=[])
So this tells me the annotation is being applied to the method argument. But I can't understand why I'm not getting the ConstraintViolationException
when I actually call the method. I also never see the output "in initialize()" or "in isValid()" that I added to these methods as a check to see if they're being fired.
As another test, I also added the @Password
annotation to the member variable password
in User.class
. This causes the ConstraintViolationException
to be thrown as expected, e.g. when I try to persist a User
object.
Can anyone shed light as to why the annotation on the method argument is not working properly? Thanks in advance!