Using Play 2.3 (Java), I'd like to use a parameter from application.conf as a value for a constraint. Something like:
public class Some {
@Constraints.MaxLength(Play.application().configuration().getInt("some.text.maxlength", 1000))
public String text;
}
But of course I can't do this because the annotation parameter must be a constant. What would be the approach, in Java, to bypass this? Should I use a custom validator? Or is there another option?
Any help appreciated.
As you discovered you can't use it via annotations, but fortunately you can write own constraints and read the application.conf
within it, fast sample based on MinLength
constraint:
file: app/utils/MyConstraints.java
package utils;
import play.Play;
import play.libs.F;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static play.libs.F.Tuple;
public class MyConstraints {
public static abstract class Validator<T> {
public abstract boolean isValid(T object);
public boolean isValid(T object, ConstraintValidatorContext constraintContext) {
return isValid(object);
}
public abstract Tuple<String, Object[]> getErrorMessageKey();
}
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = SomeTextMinLengthValidator.class)
@play.data.Form.Display(name="constraint.minLength")
public static @interface SomeTextMinLength {
String message() default SomeTextMinLengthValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class SomeTextMinLengthValidator extends Validator<String> implements ConstraintValidator<SomeTextMinLength, String> {
final static public String message = "error.minLength";
private long min;
public SomeTextMinLengthValidator() {}
public SomeTextMinLengthValidator(long value) {
this.min = value;
}
public void initialize(SomeTextMinLength constraintAnnotation) {
this.min = Play.application().configuration().getInt("some.text.maxlength", 1000);
}
public boolean isValid(String object) {
if (object == null || object.length() == 0) {
return true;
}
return object.length() >= min;
}
public F.Tuple<String, Object[]> getErrorMessageKey() {
return Tuple(message, new Object[] { min });
}
}
}
usage:
@utils.MyConstraints.SomeTextMinLength
public String text;
As you can see to add more constraints like that you only need to add the pairs of MyFooConstraint
and MyFooConstraintValidator
into MyConstraints
class, so you can also make it more generic:
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = MinLengthByCfgValidator.class)
@play.data.Form.Display(name="constraint.minLength", attributes={"configKey","defaultValue"})
public static @interface MinLengthByCfg {
String message() default MinLengthByCfgValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String configKey();
long defaultValue();
}
public static class MinLengthByCfgValidator extends Validator<String> implements ConstraintValidator<MinLengthByCfg, String> {
final static public String message = "error.minLength";
private long min;
public MinLengthByCfgValidator() {}
public void initialize(MinLengthByCfg constraintAnnotation) {
this.min = Play.application().configuration().getLong(
constraintAnnotation.configKey(),
constraintAnnotation.defaultValue()
);
}
public boolean isValid(String object) {
if (object == null || object.length() == 0) {
return true;
}
return object.length() >= min;
}
public F.Tuple<String, Object[]> getErrorMessageKey() {
return Tuple(message, new Object[] {min});
}
}
and then use it like:
@utils.MyConstraints.MinLengthByCfg(configKey = "some.text.maxlength", defaultValue = 3)
public String text;