All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lotaris.jee.validation.AbstractConstraintValidator Maven / Gradle / Ivy

Go to download

This library offers components that facilitate validation of arbitrary objects and how they are serialized towards a client. It focuses on the wiring of the proposed patterns and does not contain any concrete validator implementations.

There is a newer version: 0.5.2
Show newest version
package com.lotaris.jee.validation;

import java.lang.annotation.Annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * Basic bean validation constraint implementation. When using this class, you will implement the
 * validate method instead of the isValid method from {@link ConstraintValidator}.
 * This allows the abstract validator to hide bean validation implementation details from you,
 * making validations easier to write and test.
 *
 * 

What to implement

* *

You must implement the validate method. A {@link IConstraintValidationContext} will * be passed to your method. This object will allow you to add errors if you determine that the * supplied value/object is invalid.

* *

Minimal implementation

* *

This validator implementation will use the default error message defined by your constraint * annotation.

* *

 *	public class MyAnnotationValidator extends AbstractConstraintValidator<MyAnnotation, String> {
 *
 *		@Override
 *		public void validate(String value, IConstraintValidationContext context) {
 *			if (isInvalid(value)) {
 *				context.addDefaultError();
 *			}
 *		}
 *	}
 * 

* *

Custom errors

* *

Sometimes the static error message defined by your constraint annotation isn't enough. You * might need to build the message dynamically based on the validated value. In this case, use the * addError methods to override the default error message.

* *

 *	public class MyAnnotationValidator extends AbstractConstraintValidator<MyAnnotation, Object> {
 *
 *		@Override
 *		public void validate(Object value, IConstraintValidationContext context) {
 *
 *			if (isInvalid(value)) {
 *
 *				// add the default error message
 *				context.addDefaultError();
 *
 *				// add another error message
 *				context.addErrorAtCurrentLocation("Object is invalid.");
 *
 *				// additional arguments are interpolated
 *				context.addErrorAtCurrentLocation("%s is invalid", value);
 *
 *				// if you are validating a complex object, you can add errors to a specific property
 *				context.addError("name", "Name must be at most 25 characters long, got %d.", value.getName().length());
 *			}
 *		}
 *	}
 * 

* *

What to test

* *

When testing, only test the validate method and not the isValid method. This * allows you to mock the validation context and ignore bean validation implementation details.

* *

 *	public class CheckStringLengthValidatorUnitTests {
 *
 *		@Mock
 *		private IConstraintValidationContext context;
 *		private CheckStringLengthValidator validator;
 *
 *		@Before
 *		public void setUp() {
 *			MockitoAnnotations.initMocks(this);
 *			validator = new CheckStringLengthValidator();
 *		}
 *
 *		@Test
 *		public void checkStringLengthValidatorShouldFailIfStringIsTooLong() {
 *			validator.validate("foooooooooooooo", context);
 *			verify(context).addDefaultError();
 *			verifyNoMoreInteractions(context);
 *		}
 *	}
 * 

* * @author Simon Oulevay ([email protected]) * @param the constraint annotation * @param the type of value to validate (e.g. String) */ public abstract class AbstractConstraintValidator implements ConstraintValidator { @Override public void initialize(A constraintAnnotation) { // do nothing by default } /** * Validates the specified object. * * @param object the object to validate * @param context the context used to add and keep track of errors during validation */ public abstract void validate(T value, IConstraintValidationContext context); @Override public final boolean isValid(T value, ConstraintValidatorContext context) { final IConstraintValidationContext wrapperContext = new ConstraintValidationContext(context); validate(value, wrapperContext); return !wrapperContext.hasErrors(); } /** * Wrapper around {@link ConstraintValidatorContext} to provide an easier-to-use validation API. */ private static class ConstraintValidationContext implements IConstraintValidationContext { private boolean hasErrors; private final ConstraintValidatorContext context; public ConstraintValidationContext(ConstraintValidatorContext context) { this.hasErrors = false; this.context = context; } private ConstraintValidatorContext addErrors() { if (!hasErrors) { context.disableDefaultConstraintViolation(); hasErrors = true; } return context; } @Override public boolean hasErrors() { return hasErrors; } @Override public IConstraintValidationContext addDefaultError() { addErrors().buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addConstraintViolation(); return this; } @Override public IConstraintValidationContext addError(String location, String message, Object... messageArgs) { location = validateLocation(location); addErrors().buildConstraintViolationWithTemplate(String.format(message, messageArgs)).addPropertyNode(location).addConstraintViolation(); return this; } @Override public IConstraintValidationContext addArrayError(String location, int index, String message, Object... messageArgs) { location = validateLocation(location); addErrors().buildConstraintViolationWithTemplate(String.format(message, messageArgs)).addPropertyNode(location).addPropertyNode(String.valueOf(index)).addConstraintViolation(); return this; } @Override public IConstraintValidationContext addErrorAtCurrentLocation(String message, Object... messageArgs) { addErrors().buildConstraintViolationWithTemplate(String.format(message, messageArgs)).addConstraintViolation(); return this; } /** * Validate and sanitize the error location. * @param location The location * @return The sanitized location. * @throws IllegalArgumentException If the location contains dots or slash */ private String validateLocation(String location) throws IllegalArgumentException { if (location.contains(".")) { throw new IllegalArgumentException("Constraint violation error locations cannot contain dots."); } location = location.replaceFirst("^\\/", ""); if (location.contains("/")) { throw new IllegalArgumentException("Constraint violation error locations cannot contain a slash except as the first character."); } return location; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy