I believe you can do what you're asking, but you might need to generalize your approach just a bit.
As others have mentioned, you can not pass two attributes into a validator, but, if you changed your validator to be class level validator instead of a field level validator, it can work.
Here is a validator we created that makes sure that two fields are the same value when submitted. Think of the password and confirm password use case that you often see websites, or email and confirm email use case.
Of course, in your particular case, you'll need to pass in the user's id and the name of the project that they are trying to create.
Annotation:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Taken from:
* http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303
* <p/>
* Validation annotation to validate that 2 fields have the same value.
* An array of fields and their matching confirmation fields can be supplied.
* <p/>
* Example, compare 1 pair of fields:
*
* @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match")
* <p/>
* Example, compare more than 1 pair of fields:
* @FieldMatch.List({
* @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match"),
* @FieldMatch(first = "email", second = "confirmEmail", message = "The email fields must match")})
*/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface FieldMatch {
String message() default "{constraints.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* @return The first field
*/
String first();
/**
* @return The second field
*/
String second();
/**
* Defines several <code>@FieldMatch</code> annotations on the same element
*
* @see FieldMatch
*/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
FieldMatch[] value();
}
}
The Validator:
import org.apache.commons.beanutils.BeanUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* Taken from:
* http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303
*/
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName;
private String secondFieldName;
@Override
public void initialize(FieldMatch constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
Object firstObj = BeanUtils.getProperty(value, firstFieldName);
Object secondObj = BeanUtils.getProperty(value, secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
} catch (Exception ignore) {
// ignore
}
return true;
}
}
Then here our command object:
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.GroupSequence;
@GroupSequence({Required.class, Type.class, Data.class, Persistence.class, ChangePasswordCommand.class})
@FieldMatch(groups = Data.class, first = "password", second = "confirmNewPassword", message = "The New Password and Confirm New Password fields must match.")
public class ChangePasswordCommand {
@NotBlank(groups = Required.class, message = "New Password is required.")
@Length(groups = Data.class, min = 6, message = "New Password must be at least 6 characters in length.")
private String password;
@NotBlank(groups = Required.class, message = "Confirm New Password is required.")
private String confirmNewPassword;
...
}