I'm trying to include a dynamic message in my annotation that changes the main body of the text based on the values that are found in the other variables that are passed to it. I set a default message, but when a certain indicator is set, I want to display a different message. Is this possible?
Here's my annotation -
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface FieldMatch
{
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String first();
String second();
String third() default "";
String match() default "true";
String message() default "{error.theseValuesDontMatch}";
/**
* Defines several <code>@FieldMatch</code> annotations on the same element
*
* @see FieldMatch
*/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented @interface List
{
FieldMatch[] value();
}
}
Here's the validator class used by the annotation -
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object>
{
private String firstFieldName;
private String secondFieldName;
private String thirdFieldName;
private String match;
private String message;
@Override
public void initialize(FieldMatch constraintAnnotation)
{
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
thirdFieldName = constraintAnnotation.third();
match = constraintAnnotation.match();
if(match != null && !Boolean.getBoolean(match)){
message = "error.theseValuesMustNotMatch";
}
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context)
{
try
{
final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
final Object secondObj = BeanUtils.getProperty(value, secondFieldName);
final Object thirdObj = BeanUtils.getProperty(value, thirdFieldName);
final String same = BeanUtils.getProperty(value, match);
boolean valid = false;
if(same != null && Boolean.getBoolean(same)){
if("".equals(thirdObj)){
valid = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj) ;
}
else{
valid = firstObj != null && firstObj.equals(secondObj) && firstObj.equals(thirdObj) ;
}
}
else{
if("".equals(thirdObj)){
valid = firstObj == null && secondObj == null || firstObj != null && !firstObj.equals(secondObj) ;
}
else{
valid = firstObj != null && !(firstObj.equals(secondObj) && firstObj.equals(thirdObj)) ;
}
}
return valid ;
}
catch (final Exception ignore)
{
// ignore
}
return true;
}
}
The piece I'm most interested in is the code that reads -
if(match != null && !Boolean.getBoolean(match)){
message = "password.error.theseValuesMustNotMatch";
}
extends Annotation>, Annotation> map; We will update this map to alter annotation at runtime. Approach to access this map differs in various JDK implementation. We will discuss it for JDK7 and JDK8.
In Java SE 6, annotations cannot subclass one another, and an annotation is not allowed to extend/implement any interfaces.
Here's how I was able to do this -
@Override public void initialize(FieldMatch constraintAnnotation) { firstFieldName = constraintAnnotation.first(); secondFieldName = constraintAnnotation.second(); thirdFieldName = constraintAnnotation.third(); match = constraintAnnotation.match(); //set a message variable on initialization if("true".equals(match)){ message = constraintAnnotation.message(); } else{ message = "{password.error.threeQuestionsSameAnswer}";} } @Override public boolean isValid(final Object value, final ConstraintValidatorContext context) { Object firstObj = null; Object secondObj = null; Object thirdObj = null; //disable existing violation message context.disableDefaultConstraintViolation(); //build new violation message and add it context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); etc......... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With