org.kiwiproject.validation.KiwiValidations Maven / Gradle / Ivy
Show all versions of kiwi Show documentation
package org.kiwiproject.validation;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.kiwiproject.base.KiwiStrings.format;
import static org.kiwiproject.collect.KiwiSets.isNotNullOrEmpty;
import static org.kiwiproject.collect.KiwiSets.isNullOrEmpty;
import com.google.common.base.Preconditions;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.groups.Default;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.kiwiproject.base.KiwiStrings;
import org.kiwiproject.reflect.KiwiReflection;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
/**
* Static utilities related to Jakarta Bean Validation (formerly Java Bean Validation).
* Relies on the Bean Validation API.
*
* Dependency requirements:
*
* The {@code jakarta.validation:jakarta.validation-api} dependency and some implementation such as Hibernate Validator
* ({@code org.hibernate.validator:hibernate-validator} must be available at runtime.
*/
@UtilityClass
@Slf4j
public class KiwiValidations {
/**
* A re-usable {@link Validator} instance built using {@link #newValidator()}.
*/
private static Validator validatorInstance = newValidator();
/**
* Creates a new, default {@link Validator} instance using the default validator factory provided by the
* underlying bean validation implementation, for example, Hibernate Validator.
*
* @return a new {@link Validator} instance
*/
public static Validator newValidator() {
return Validation.buildDefaultValidatorFactory().getValidator();
}
/**
* Return the re-usable (singleton) Validator instance.
*
* @return singleton {@link Validator} instance
*/
public static Validator getValidator() {
return validatorInstance;
}
/**
* Reset the singleton Validator instance.
*
* This is intended primarily for use by unit tests, to permit resetting the Validator.
* Use with caution and remember: with great power, come great responsibility.
*
* @param newValidator the new Validator to use as the singleton instance
* @implNote This method is intentionally not synchronized. Since it is expected only to be used once at
* application startup, and during unit tests, we can skip adding synchronization to this and all
* other methods that use the singleton Validator instance. In other words, because we assume this will
* not be called in real production code, except perhaps one time at application startup, we don't think
* it's worth adding locking or synchronization on every method that uses the singleton Validator instance.
*/
public static void setValidator(Validator newValidator) {
LOG.warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
LOG.warn(" Overriding static Validator instance !!!");
LOG.warn("!!! You should only see this in tests or an application startup !!!");
LOG.warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
validatorInstance = newValidator;
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group.
*
* @param object the object to validate
* @param the object type
* @return validation results
*/
public static Set> validate(T object) {
return validatorInstance.validate(object, Default.class);
}
/**
* Validate the given object using the singleton validator instance against the specified validation groups.
*
* @param object the object to validate
* @param groupClasses zero or more validation group classes
* @param the object type
* @return validation results
*/
public static Set> validate(T object, Class>... groupClasses) {
return validatorInstance.validate(object, groupClasses);
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group, and throw
* a {@link ConstraintViolationException} if validation fails.
*
* @param object the object to validate
* @param the object type
*/
public static void validateThrowing(T object) {
var violations = KiwiValidations.validate(object);
throwConstraintViolationExceptionIfNotEmpty(violations);
}
/**
* Validate the given object using the singleton validator instance against the specified validation groups, and
* throw a {@link ConstraintViolationException} if validation fails.
*
* @param object the object to validate
* @param groupClasses zero or more validation group classes
* @param the object type
*/
public static void validateThrowing(T object, Class>... groupClasses) {
var violations = KiwiValidations.validate(object, groupClasses);
throwConstraintViolationExceptionIfNotEmpty(violations);
}
/**
* If the set of constraint violations is not empty, throw a {@link ConstraintViolationException}.
*
* @param violations the constraint violations
* @param the object type
*/
public static void throwConstraintViolationExceptionIfNotEmpty(Set> violations) {
if (isNotNullOrEmpty(violations)) {
throw new ConstraintViolationException(violations);
}
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group.
* If the argument is not valid, throws an {@link IllegalArgumentException}. The exception message is
* supplied by {@link #checkArgumentNoViolations(Set)}.
*
* @param object the object to validate
* @param the object type
* @see #checkArgumentNoViolations(Set)
*/
public static void checkArgumentValid(T object) {
var violations = KiwiValidations.validate(object);
checkArgumentNoViolations(violations);
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group.
* If the argument is not valid, throws an {@link IllegalArgumentException}.
*
* @param object the object to validate
* @param errorMessage the error message for the exception
* @param the object type
*/
public static void checkArgumentValid(T object, String errorMessage) {
var violations = KiwiValidations.validate(object);
checkArgumentNoViolations(violations, errorMessage);
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group.
* If the argument is not valid, throws an {@link IllegalArgumentException}.
*
* @param object the object to validate
* @param errorMessageTemplate a template for the exception message should the check fail, according to how
* {@link KiwiStrings#format(String, Object...)} handles placeholders
* @param errorMessageArgs the arguments to be substituted into the message template. Arguments
* are converted to Strings using {@link String#valueOf(Object)}.
* @param the object type
*/
public static void checkArgumentValid(T object, String errorMessageTemplate, Object... errorMessageArgs) {
var violations = KiwiValidations.validate(object);
checkArgumentNoViolations(violations, errorMessageTemplate, errorMessageArgs);
}
/**
* Validate the given object using the singleton validator instance against the {@link Default} group.
* If the argument is not valid, throws an {@link IllegalArgumentException}.
*
* @param object the object to validate
* @param errorMessageCreator a Function that transforms constraint violations into an error message for the exception
* @param the object type
*/
public static void checkArgumentValid(T object,
Function>, String> errorMessageCreator) {
var violations = KiwiValidations.validate(object);
checkArgumentNoViolations(violations, errorMessageCreator);
}
/**
* Validate the given object using the singleton validator instance against the specified validation groups.
* If the argument is not valid, throws an {@link IllegalArgumentException}. The exception message is
* supplied by {@link #checkArgumentNoViolations(Set)}.
*
* @param object the object to validate
* @param groupClasses zero or more validation group classes
* @param the object type
* @see #checkArgumentNoViolations(Set)
*/
public static void checkArgumentValid(T object, Class>... groupClasses) {
var violations = KiwiValidations.validate(object, groupClasses);
checkArgumentNoViolations(violations);
}
/**
* Validate the given object using the singleton validator instance against the specified validation groups.
* If the argument is not valid, throws an {@link IllegalArgumentException}.
*
* @param object the object to validate
* @param errorMessage the error message for the exception
* @param groupClasses zero or more validation group classes
* @param the object type
*/
public static void checkArgumentValid(T object, String errorMessage, Class>... groupClasses) {
var violations = KiwiValidations.validate(object, groupClasses);
checkArgumentNoViolations(violations, errorMessage);
}
/**
* Validate the given object using the singleton validator instance against the specified validation groups.
* If the argument is not valid, throws an {@link IllegalArgumentException}.
*
* @param object the object to validate
* @param errorMessageTemplate a template for the exception message should the check fail, according to how
* {@link KiwiStrings#format(String, Object...)} handles placeholders
* @param errorMessageArgs the arguments to be substituted into the message template. Arguments
* are converted to Strings using {@link String#valueOf(Object)}.
* @param groupClasses zero or more validation group classes
* @param the object type
*/
public static void checkArgumentValid(T object,
String errorMessageTemplate,
List