Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom annotation on method argument is not being executed

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!

like image 759
robguinness Avatar asked Aug 25 '15 17:08

robguinness


1 Answers

I think you are missing this declaration in your Password annotation:

ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;

From the docs:

validationAppliesTo is used at constraint declaration time to clarify what the constraint targets (i.e. the annotated element, the method return value or the method parameters).

The element validationAppliesTo must only be present for constraints that are both generic and cross-parameter, it is mandatory in this situation. A ConstraintDefinitionException is raised if these rules are violated.

The type of the validationAppliesTo parameter is ConstraintTarget. The default value must be ConstraintTarget.IMPLICIT.

like image 63
Danail Alexiev Avatar answered Oct 06 '22 07:10

Danail Alexiev