CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-dropwizard--dropwizard-validation

Dropwizard Validation Support - provides enhanced validation capabilities for Dropwizard applications

Pending
Overview
Eval results
Files

self-validation.mddocs/

Self-Validation Framework

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.

Capabilities

Self-Validating Annotation

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 {};
}

Self-Validation Method Annotation

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:

  • Must be public
  • Must return void
  • Must accept exactly one parameter of type ViolationCollector
  • Method name can be anything

Violation Collector

Collects 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();
}

Usage Examples

Basic Self-Validation

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...
}

Advanced Self-Validation with Message Parameters

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");
                }
            }
        }
    }
}

Complex Business Logic Validation

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");
                }
            }
        }
    }
}

Integration with Bean Validation

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 executed

Advanced Features

Validation Groups

import 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");
        }
    }
}

Multiple Self-Validation Methods

@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
    }
}

Integration Notes

  • Self-validation methods are executed in addition to standard Bean Validation annotations
  • All @SelfValidation methods on a class are executed when validation occurs
  • Violations are collected and reported alongside standard constraint violations
  • Self-validation integrates seamlessly with Dropwizard's configuration validation
  • Use ViolationCollector methods to ensure proper violation reporting and message interpolation
  • Self-validation is particularly useful for cross-field validation and complex business rules

Install with Tessl CLI

npx tessl i tessl/maven-io-dropwizard--dropwizard-validation

docs

base-validation.md

data-size-validation.md

duration-validation.md

index.md

method-validation.md

self-validation.md

value-validation.md

tile.json