Conditional Validation through Annotations

Validation through Annotations has become very popular among the web developers. The ease of use and code readability makes it even more promising to use and replicate.

Many libraries provide us with easy to use annotations for easy validation. Though most of the times they fulfill all the requirements, there are some cases where a more specific annotation is required. For one such case, conditional acceptance of one parameter based on the value of another, I have written this small article.


Annotation

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = DependsOnValidator.class)
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface DependsOn {

	public abstract String message() default "Conditional Constraint Unsatisfied";

	public abstract Class[] groups() default {};

	public abstract Class[] payload() default {};

	public abstract String dependsOn();

	public abstract String field();

	public abstract String value();

	@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
    	DependsOn[] value();
    }

}

Validation Logic

import java.lang.reflect.Field;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.commons.lang.StringUtils;

public class DependsOnValidator implements ConstraintValidator {

	private DependsOn annotation;

	@Override
	public void initialize(DependsOn constraint) {
		this.annotation = constraint;
	}

	@Override
	public boolean isValid(Object value, ConstraintValidatorContext cxt) {
		boolean result = true;

		Field dependsOn;
		try {
			dependsOn = value.getClass().getDeclaredField(this.annotation.dependsOn());

			dependsOn.setAccessible(true);
			String dependsOnValue = (String) dependsOn.get(value);

			Field field = value.getClass().getDeclaredField(this.annotation.field());
			field.setAccessible(true);
			String fieldValue = (String) field.get(value);

			result = StringUtils.isNotEmpty(dependsOnValue) ? annotation.value().equals(dependsOnValue) ? StringUtils.isNotEmpty(fieldValue) ? true : false : false : false;
		} catch (NoSuchFieldException | SecurityException e) {
			result = false;
		} catch (IllegalArgumentException e) {
			result = false;
		} catch (IllegalAccessException e) {
			result = false;
		}
		return result;
	}

}

Usage

 

In the below mentioned use case when an object of class ErrorMessage is
validated using annotations a custom validation is performed taking in account
the values passed to the @DependsOn annotation. field(X): parameter (X) to validate for not empty or not blank dependsOn(Y): parameter (Y) on which parameter (X) depends value: value of parameter (Y) which is evaluated for the check to succeed. for example, If X has value “submit” then Y should not be empty Here, X is offendingMethod, Y is offendingField and value is submit
@DependsOn.List(
	value = { 
		@DependsOn(dependsOn = "offendingMethod", field = "offendingField", value = "submit") 
	})
public class ErrorMessage {

	private String message;
	private String offendingMethod;
	private String offendingField;
	private Object invalidValue;

// getters and setters
}

Leave a comment

Create a free website or blog at WordPress.com.

Up ↑