Last active
August 30, 2018 15:18
-
-
Save ydemartino/f761bed832f61a827ef505939b22011c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://resourcepool.io/2014/08/21/java-quickies-hibernate-validator-reference-implementation-of-jsr-303/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.model; | |
import javax.validation.constraints.Pattern; | |
import org.hibernate.validator.constraints.NotBlank; | |
public class Address { | |
private String line1; | |
private String line2; | |
private String line3; | |
@Pattern(regexp = "[0-9]{5}", message = "Invalid zip code!") | |
private String zipCode; | |
@NotBlank | |
private String city; | |
@NotBlank | |
private String country; | |
// Getters & Setters | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
List<Address> addresses = new ArrayList<Address>(); | |
Address address = new Address(); | |
address.setZipCode("00000"); | |
address.add(addresse); | |
client.setAddresses(addresses); | |
service.validateClient(client); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.validator.constraint; | |
import static java.lang.annotation.ElementType.TYPE; | |
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.Target; | |
import javax.validation.Constraint; | |
import javax.validation.ConstraintValidator; | |
import javax.validation.ConstraintValidatorContext; | |
import javax.validation.Payload; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
@Target({ TYPE }) | |
@Retention(RUNTIME) | |
@Constraint(validatedBy = AtLeastOneConstraint.AtLeastOneValidator.class) | |
public @interface AtLeastOneConstraint { | |
String message() default "{io.resourcepool.validator.AtLeastOneConstraint.message}"; | |
Class<?>[] groups() default {}; | |
Class<? extends Payload>[] payload() default {}; | |
String[] fieldNames(); | |
public static class AtLeastOneValidator implements ConstraintValidator<AtLeastOneConstraint, Object> { | |
private static final Logger LOGGER = LoggerFactory.getLogger(AtLeastOneValidator.class); | |
private String[] fieldNames; | |
public void initialize(AtLeastOneConstraint constraintAnnotation) { | |
this.fieldNames = constraintAnnotation.fieldNames(); | |
} | |
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) { | |
if (object == null) { | |
return true; | |
} | |
try { | |
for (String field : fieldNames) { | |
Object property = tryGetField(object, field, "get", "is"); | |
if (property != null) { | |
return true; | |
} | |
} | |
} catch (Exception e) { | |
LOGGER.error("Exception during AtLeastOne validation", e); | |
} | |
return false; | |
} | |
private Object tryGetField(Object object, String field, String... prefixes) | |
throws IllegalAccessException, InvocationTargetException { | |
for (String prefix: prefixes) { | |
try { | |
return object.getClass().getMethod(prefix + field.substring(0, 1).toUpperCase() + field.substring(1)) | |
.invoke(object); | |
} catch (NoSuchMethodException ignore) { | |
// Nothing to do | |
} | |
} | |
return null; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@AtLeastOneConstraint(fieldNames = { "line1", "line2", "line3" }) | |
public class Address |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class AtLeastOneValidator implements ConstraintValidator<AtLeastOneConstraint, Object> { | |
private static final Logger LOGGER = LoggerFactory.getLogger(AtLeastOneValidator.class); | |
private String[] fieldNames; | |
public void initialize(AtLeastOneConstraint constraintAnnotation) { | |
this.fieldNames = constraintAnnotation.fieldNames(); | |
} | |
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) { | |
if (subValidation(object)) { | |
return true; | |
} | |
// We need to link the error to the correct fields | |
for (String field : fieldNames) { | |
ValidatorUtil.linkErrorToProperty(field, constraintContext); | |
} | |
return false; | |
} | |
private boolean subValidation(Object object) { | |
if (object == null) { | |
return true; | |
} | |
try { | |
for (String field : fieldNames) { | |
Object property = tryGetField(object, field, "get", "is"); | |
if (property != null) { | |
return true; | |
} | |
} | |
} catch (Exception e) { | |
LOGGER.error("Exception during AtLeastOneValidator validation", e); | |
} | |
return false; | |
} | |
private Object tryGetField(Object object, String field, String... prefixes) | |
throws IllegalAccessException, InvocationTargetException { | |
for (String prefix: prefixes) { | |
try { | |
return object.getClass().getMethod(prefix + field.substring(0, 1).toUpperCase() + field.substring(1)) | |
.invoke(object); | |
} catch (NoSuchMethodException ignore) { | |
// Nothing to do! | |
} | |
} | |
return null; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> | |
<bean id="ValidatorUtil" class="io.resourcepool.validator.ValidatorUtil"> | |
<property name="validator" ref="validator"/> | |
</bean> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.model; | |
import java.util.Date; | |
import javax.validation.constraints.Past; | |
import org.hibernate.validator.constraints.*; | |
public class Client { | |
private Long id; | |
@NotBlank | |
private String firstName; | |
@NotBlank | |
private String lastName; | |
private String email; | |
@Past | |
private Date birthDate; | |
// Getters & Setters | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.model; | |
import java.util.Date; | |
import java.util.List; | |
import javax.validation.Valid; | |
import javax.validation.constraints.*; | |
import org.hibernate.validator.constraints.*; | |
import io.resourcepool.validator.ClientBase; | |
public class Client { | |
private Long id; | |
@NotBlank(groups = ClientBase.class) | |
private String firstName; | |
@NotBlank(groups = ClientBase.class) | |
private String lastName; | |
@Email(groups = ClientBase.class) | |
@NotNull(groups = ClientBase.class) | |
private String email; | |
@Past(groups = ClientBase.class) | |
@NotNull(groups = ClientBase.class, message = "You must set your date of birth") | |
private Date birthDate; | |
@Size(groups = ClientBase.class, min = 2, max = 2, message = "You need at least {min} addresses") | |
@NotNull(groups = ClientBase.class) | |
@Valid | |
private List<Address> addresses; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.validator; | |
public interface ClientBase { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Set<ConstraintViolation<Client>> violations = validator.validate(client, ClientBase.class); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@NotNull | |
private String email; | |
@Past | |
@NotNull | |
private Date birthDate; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ClientService { | |
private Validator validator; | |
public ClientService() { | |
ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); | |
validator = factory.getValidator(); | |
} | |
private void validateClient(Client client) { | |
Set<ConstraintViolation<Client>> violations = validator.validate(client); | |
// Process errors if any | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Client client = new Client(); | |
client.setFirstName("Yoann"); | |
client.setEmail("Yoann"); | |
service.validateClient(client); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd" | |
xmlns="http://jboss.org/xml/ns/javax/validation/mapping"> | |
<constraint-definition annotation="io.resourcepool.validator.constraints.UniqueEmailConstraint"> | |
<validated-by include-existing-validators="false"> | |
<value>io.resourcepool.validator.UniqueEmailValidator</value> | |
</validated-by> | |
</constraint-definition> | |
</constraint-mappings> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-validator</artifactId> | |
<version>5.1.2.Final</version> | |
</dependency> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>javax.el</groupId> | |
<artifactId>javax.el-api</artifactId> | |
<version>2.2.4</version> | |
</dependency> | |
<dependency> | |
<groupId>org.glassfish.web</groupId> | |
<artifactId>javax.el</artifactId> | |
<version>2.2.4</version> | |
</dependency> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Constraint(validatedBy = { }) | |
public @interface NotNull |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Past | |
@NotNull(message = "You must set your date of birth") | |
private Date birthDate; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Size(min = 2, max = 2, message = "You need at least {min} addresses") | |
@NotNull | |
@Valid | |
private List<Address> addresses; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Target({ TYPE }) | |
@Retention(RUNTIME) | |
@Constraint(validatedBy = { UniqueEmailConstraint.UniqueEmailValidator.class }) | |
public @interface UniqueEmailConstraint { | |
String message() default "{io.resourcepool.validator.constraint.UniqueEmailConstraint.message}"; | |
Class<?>[] groups() default {}; | |
Class<? extends Payload>[] payload() default {}; | |
public static class UniqueEmailValidator implements ConstraintValidator<UniqueEmailConstraint, Client> { | |
@Autowired | |
private Client clientDao; | |
@Override | |
public void initialize(UniqueEmailConstraint constraintAnnotation) { | |
// Nothing to do | |
} | |
@Override | |
public boolean isValid(Client client, ConstraintValidatorContext context) { | |
return ValidatorUtil.linkErrorToProperty("email", context, subValidation(client)); | |
} | |
private boolean subValidation(Client client) { | |
if (client == null || client.getEmail() == null) { | |
return true; | |
} | |
Client res = clientDao.getByEmail(client.getEmail()); | |
// Valid if the email does not exist in DB or if it is already used by the current client | |
return res == null || client.getId() != null && client.getId().equals(res.getId()); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>javax.validation</groupId> | |
<artifactId>validation-api</artifactId> | |
<version>1.1.0.Final</version> | |
</dependency> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration"> | |
<constraint-mapping>/constraints-client.xml</constraint-mapping> | |
</validation-config> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
io.resourcepool.validator.AtLeastOneConstraint.message=You need at least one of these fields. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.resourcepool.validator; | |
public class ValidatorUtil { | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context) { | |
return linkErrorToProperty(property, context, false); | |
} | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context, boolean isValid) { | |
if (isValid) { | |
// Nothing to do if it is valid ! | |
return true; | |
} | |
// If it is invalid, we need to link it to the correct field | |
context.disableDefaultConstraintViolation(); | |
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) | |
.addPropertyNode(property).addConstraintViolation(); | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ValidatorUtil { | |
private static Validator validator; | |
static { | |
ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); | |
validator = factory.getValidator(); | |
} | |
public static <T> Map<String, String> validate(T object, Class<?>... groups) { | |
Class<?>[] args = Arrays.copyOf(groups, groups.length + 1); | |
args[groups.length] = Default.class; | |
return extractViolations(validator.validate(object, args)); | |
} | |
private static <T> Map<String, String> extractViolations(Set<ConstraintViolation<T>> violations) { | |
Map<String, String> errors = new HashMap<String, String>(); | |
for (ConstraintViolation<T> v: violations) { | |
errors.put(v.getPropertyPath().toString(), v.getMessage()); | |
} | |
return errors; | |
} | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context) { | |
return linkErrorToProperty(property, context, false); | |
} | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context, boolean isValid) { | |
if (isValid) { | |
// Nothing to do if it is valid ! | |
return true; | |
} | |
// If it is invalid, we need to link it to the correct field | |
context.disableDefaultConstraintViolation(); | |
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) | |
.addPropertyNode(property).addConstraintViolation(); | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ValidatorUtil { | |
private Validator validator; | |
private static ThreadLocal<Map<String, Object>> validationContext = new ThreadLocal<>(); | |
public void setValidator(Validator validator) { | |
this.validator = validator; | |
} | |
public <T> Map<String, String> validate(T object, Class<?>... groups) { | |
Class<?>[] args = Arrays.copyOf(groups, groups.length + 1); | |
args[groups.length] = Default.class; | |
return extractViolations(validator.validate(object, args)); | |
} | |
public static <T> T getValidationVariable(String key) { | |
// We are using containsKey not to throw an exception if the value is null | |
if (validationContext.get() != null && validationContext.get().containsKey(key)) { | |
return (T)validationContext.get().get(key); | |
} | |
throw new CannotValidateException(key + " doest not exist or the context is null!"); | |
} | |
public static void addValidationVariable(String key, Object value) { | |
if (validationContext.get() == null) { | |
validationContext.set(new HashMap<String, Object>()); | |
} | |
validationContext.get().put(key, value); | |
} | |
public static void clearValidationVariables() { | |
validationContext.set(null); | |
} | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context) { | |
return linkErrorToProperty(property, context, false); | |
} | |
public static boolean linkErrorToProperty(String property, ConstraintValidatorContext context, boolean isValid) { | |
if (isValid) { | |
return true; | |
} | |
context.disableDefaultConstraintViolation(); | |
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) | |
.addPropertyNode(property).addConstraintViolation(); | |
return false; | |
} | |
private <T> Map<String, String> extractViolations(Set<ConstraintViolation<T>> violations) { | |
Map<String, String> errors = new HashMap<String, String>(); | |
for (ConstraintViolation<T> v: violations) { | |
errors.put(v.getPropertyPath().toString(), v.getMessage()); | |
} | |
return errors; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment