Dropwizard Validation Support - provides enhanced validation capabilities for Dropwizard applications
—
A framework enabling objects to define custom validation logic through annotated methods, providing flexibility for complex business rules that cannot be expressed with standard validation annotations. This framework allows objects to validate themselves using custom logic while integrating seamlessly with Bean Validation.
Marks a class as having self-validation methods that should be executed during validation.
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SelfValidatingValidator.class)
public @interface SelfValidating {
/**
* The validation message for this constraint.
*
* @return the message
*/
String message() default "";
/**
* The groups the constraint belongs to.
*
* @return an array of classes representing the groups
*/
Class<?>[] groups() default {};
/**
* The payloads of this constraint.
*
* @return the array of payload classes
*/
Class<? extends Payload>[] payload() default {};
}Marks a method as a self-validation method that will be executed to check if the object is valid.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SelfValidation {
}Method Signature Requirements:
publicvoidViolationCollectorCollects constraint violations during self-validation, providing methods to add violations at different scopes.
public class ViolationCollector {
/**
* Constructs a new ViolationCollector with the given ConstraintValidatorContext.
*
* @param constraintValidatorContext the wrapped ConstraintValidatorContext
*/
public ViolationCollector(ConstraintValidatorContext constraintValidatorContext);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param message the message of the violation
*/
public void addViolation(String message);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param message the message of the violation
* @param messageParameters a map of message parameters which can be interpolated in the violation message
*/
public void addViolation(String message, Map<String, Object> messageParameters);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property
* @param message the message of the violation
*/
public void addViolation(String propertyName, String message);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property
* @param message the message of the violation
* @param messageParameters a map of message parameters which can be interpolated in the violation message
*/
public void addViolation(String propertyName, String message, Map<String, Object> messageParameters);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property with the violation
* @param index the index of the element with the violation
* @param message the message of the violation
*/
public void addViolation(String propertyName, Integer index, String message);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property with the violation
* @param index the index of the element with the violation
* @param message the message of the violation
* @param messageParameters a map of message parameters which can be interpolated in the violation message
*/
public void addViolation(String propertyName, Integer index, String message, Map<String, Object> messageParameters);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property with the violation
* @param key the key of the element with the violation
* @param message the message of the violation
*/
public void addViolation(String propertyName, String key, String message);
/**
* Adds a new violation to this collector. This also sets violationOccurred to true.
*
* @param propertyName the name of the property with the violation
* @param key the key of the element with the violation
* @param message the message of the violation
* @param messageParameters a map of message parameters which can be interpolated in the violation message
*/
public void addViolation(String propertyName, String key, String message, Map<String, Object> messageParameters);
/**
* Returns, if a violation has previously occurred.
*
* @return if any violation was collected
*/
public boolean hasViolationOccurred();
/**
* Manually sets if a violation occurred. This is automatically set if addViolation is called.
*
* @param violationOccurred if any violation was collected
*/
public void setViolationOccurred(boolean violationOccurred);
/**
* This method returns the wrapped context for raw access to the validation framework.
* If you use the context to add violations make sure to call setViolationOccurred(true).
*
* @return the wrapped Hibernate ConstraintValidatorContext
*/
public ConstraintValidatorContext getContext();
}import io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
import io.dropwizard.validation.selfvalidating.ViolationCollector;
@SelfValidating
public class UserRegistration {
private String username;
private String password;
private String confirmPassword;
private String email;
@SelfValidation
public void validatePasswords(ViolationCollector collector) {
if (password != null && !password.equals(confirmPassword)) {
collector.addViolation("confirmPassword", "Password confirmation does not match");
}
}
@SelfValidation
public void validateBusinessRules(ViolationCollector collector) {
if (username != null && username.length() < 3) {
collector.addViolation("username", "Username must be at least 3 characters long");
}
if (email != null && !email.contains("@")) {
collector.addViolation("email", "Email must be a valid email address");
}
}
// getters and setters...
}import io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
import io.dropwizard.validation.selfvalidating.ViolationCollector;
import java.util.Map;
import java.util.HashMap;
@SelfValidating
public class ConfigurationSettings {
private int maxConnections;
private int maxConnectionsPerHost;
private List<String> allowedHosts;
@SelfValidation
public void validateConnectionSettings(ViolationCollector collector) {
if (maxConnectionsPerHost > maxConnections) {
Map<String, Object> params = new HashMap<>();
params.put("maxConnections", maxConnections);
params.put("maxConnectionsPerHost", maxConnectionsPerHost);
collector.addViolation(
"maxConnectionsPerHost",
"Connections per host ({maxConnectionsPerHost}) cannot exceed total connections ({maxConnections})",
params
);
}
}
@SelfValidation
public void validateHostList(ViolationCollector collector) {
if (allowedHosts != null) {
for (int i = 0; i < allowedHosts.size(); i++) {
String host = allowedHosts.get(i);
if (host == null || host.trim().isEmpty()) {
collector.addViolation("allowedHosts", i, "Host cannot be empty");
}
}
}
}
}import io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
import io.dropwizard.validation.selfvalidating.ViolationCollector;
@SelfValidating
public class OrderConfiguration {
private BigDecimal minOrderAmount;
private BigDecimal maxOrderAmount;
private BigDecimal discountThreshold;
private BigDecimal discountPercentage;
private Map<String, BigDecimal> categoryLimits;
@SelfValidation
public void validateOrderAmounts(ViolationCollector collector) {
if (minOrderAmount != null && maxOrderAmount != null) {
if (minOrderAmount.compareTo(maxOrderAmount) > 0) {
collector.addViolation("minOrderAmount", "Minimum order amount cannot exceed maximum order amount");
}
}
if (discountThreshold != null && maxOrderAmount != null) {
if (discountThreshold.compareTo(maxOrderAmount) > 0) {
collector.addViolation("discountThreshold", "Discount threshold cannot exceed maximum order amount");
}
}
}
@SelfValidation
public void validateDiscountSettings(ViolationCollector collector) {
if (discountPercentage != null) {
if (discountPercentage.compareTo(BigDecimal.ZERO) < 0 ||
discountPercentage.compareTo(new BigDecimal("100")) > 0) {
collector.addViolation("discountPercentage", "Discount percentage must be between 0 and 100");
}
}
}
@SelfValidation
public void validateCategoryLimits(ViolationCollector collector) {
if (categoryLimits != null) {
for (Map.Entry<String, BigDecimal> entry : categoryLimits.entrySet()) {
if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) {
collector.addViolation("categoryLimits", entry.getKey(),
"Category limit must be positive");
}
if (maxOrderAmount != null && entry.getValue().compareTo(maxOrderAmount) > 0) {
collector.addViolation("categoryLimits", entry.getKey(),
"Category limit cannot exceed maximum order amount");
}
}
}
}
}import io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
import io.dropwizard.validation.selfvalidating.ViolationCollector;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Email;
import javax.validation.constraints.Size;
@SelfValidating
public class UserProfile {
@NotNull
@Size(min = 3, max = 50)
private String username;
@NotNull
@Email
private String email;
@NotNull
@Size(min = 8)
private String password;
private String confirmPassword;
private boolean termsAccepted;
@SelfValidation
public void validateCustomRules(ViolationCollector collector) {
// Password confirmation (not covered by standard annotations)
if (password != null && !password.equals(confirmPassword)) {
collector.addViolation("confirmPassword", "Password confirmation must match password");
}
// Business rule validation
if (!termsAccepted) {
collector.addViolation("termsAccepted", "Terms and conditions must be accepted");
}
// Complex username validation
if (username != null && username.contains("admin")) {
collector.addViolation("username", "Username cannot contain 'admin'");
}
}
}
// Usage
Validator validator = BaseValidator.newValidator();
UserProfile profile = new UserProfile();
// ... set properties
Set<ConstraintViolation<UserProfile>> violations = validator.validate(profile);
// Both standard Bean Validation and self-validation will be executedimport io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
public interface CreateUser {}
public interface UpdateUser {}
@SelfValidating(groups = {CreateUser.class, UpdateUser.class})
public class UserData {
private String username;
private String password;
@SelfValidation
public void validateForCreation(ViolationCollector collector) {
// This validation runs for both CreateUser and UpdateUser groups
if (username != null && username.length() < 3) {
collector.addViolation("username", "Username too short");
}
}
}@SelfValidating
public class ComplexConfiguration {
private DatabaseConfig database;
private CacheConfig cache;
private SecurityConfig security;
@SelfValidation
public void validateDatabaseConfig(ViolationCollector collector) {
// Database-specific validation logic
}
@SelfValidation
public void validateCacheConfig(ViolationCollector collector) {
// Cache-specific validation logic
}
@SelfValidation
public void validateSecurityConfig(ViolationCollector collector) {
// Security-specific validation logic
}
@SelfValidation
public void validateCrossComponentCompatibility(ViolationCollector collector) {
// Cross-component validation logic
}
}@SelfValidation methods on a class are executed when validation occursViolationCollector methods to ensure proper violation reporting and message interpolationInstall with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-validation