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

method-validation.mddocs/

Method Validation

Annotations and utilities for validating method parameters and enabling bean predicate validation, supporting advanced validation scenarios beyond field-level constraints. This provides method-level validation capabilities for Dropwizard applications.

Capabilities

Validated Annotation

Specifies validation groups for method parameters and return values, extending beyond the limitations of standard @Valid annotation.

@Target({PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface Validated {
    /**
     * Specify one or more validation groups to apply to the validation.
     *
     * @return Validation groups
     */
    Class<?>[] value() default {Default.class};
}

Usage Examples:

import io.dropwizard.validation.Validated;
import javax.validation.groups.Default;

public interface CreateUser {}
public interface UpdateUser {}

@Path("/users")
public class UserResource {
    
    @POST
    @Path("/create")
    public Response createUser(@Validated(CreateUser.class) UserData userData) {
        // Method parameter validated with CreateUser group
        return Response.ok().build();
    }
    
    @PUT
    @Path("/{id}")
    public Response updateUser(
        @PathParam("id") Long id,
        @Validated({UpdateUser.class, Default.class}) UserData userData
    ) {
        // Method parameter validated with both UpdateUser and Default groups
        return Response.ok().build();
    }
    
    @POST
    @Path("/batch")
    public Response createUsers(@Validated List<@Validated(CreateUser.class) UserData> users) {
        // Both the list and individual elements are validated
        return Response.ok().build();
    }
}

ValidationMethod Annotation

Validates bean predicate methods as returning true. Bean predicates must be of the form isSomething or they'll be silently ignored.

@Target({TYPE, ANNOTATION_TYPE, METHOD})
@Retention(RUNTIME)
@Constraint(validatedBy = MethodValidator.class)
public @interface ValidationMethod {
    /**
     * The validation message for this constraint.
     *
     * @return the message
     */
    String message() default "is not valid";

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

Usage Examples:

import io.dropwizard.validation.ValidationMethod;

public class UserConfiguration {
    private String username;
    private String password;
    private int age;
    private String email;
    
    @ValidationMethod(message = "Username and password cannot be the same")
    public boolean isUsernameAndPasswordDifferent() {
        return username == null || password == null || !username.equals(password);
    }
    
    @ValidationMethod(message = "User must be at least 18 years old")
    public boolean isAgeValid() {
        return age >= 18;
    }
    
    @ValidationMethod(message = "Email domain must be from approved list")
    public boolean isEmailDomainApproved() {
        if (email == null) return true;
        
        String[] approvedDomains = {"company.com", "partner.org", "trusted.net"};
        String domain = email.substring(email.lastIndexOf("@") + 1);
        
        return Arrays.asList(approvedDomains).contains(domain);
    }
    
    // getters and setters...
}

Advanced Usage

Combining Method Validation with Field Validation

import io.dropwizard.validation.ValidationMethod;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Email;

public class RegistrationData {
    @NotNull
    @Size(min = 3, max = 50)
    private String username;
    
    @NotNull
    @Size(min = 8)
    private String password;
    
    @NotNull
    @Email
    private String email;
    
    private String confirmPassword;
    private boolean termsAccepted;
    
    // Method validation for business rules
    @ValidationMethod(message = "Password and confirmation must match")
    public boolean isPasswordConfirmed() {
        return password != null && password.equals(confirmPassword);
    }
    
    @ValidationMethod(message = "Terms and conditions must be accepted")
    public boolean isTermsAccepted() {
        return termsAccepted;
    }
    
    @ValidationMethod(message = "Username cannot be the same as email prefix")
    public boolean isUsernameUniqueFromEmail() {
        if (username == null || email == null) return true;
        
        String emailPrefix = email.substring(0, email.indexOf("@"));
        return !username.equalsIgnoreCase(emailPrefix);
    }
}

Method Validation with Groups

import io.dropwizard.validation.ValidationMethod;

public interface BasicValidation {}
public interface AdvancedValidation {}

public class SecurityConfiguration {
    private int passwordMinLength;
    private int maxLoginAttempts;
    private boolean requireTwoFactor;
    
    @ValidationMethod(
        message = "Password minimum length must be at least 8 characters",
        groups = BasicValidation.class
    )
    public boolean isPasswordMinLengthSufficient() {
        return passwordMinLength >= 8;
    }
    
    @ValidationMethod(
        message = "Max login attempts must be reasonable (3-10)",
        groups = BasicValidation.class
    )
    public boolean isMaxLoginAttemptsReasonable() {
        return maxLoginAttempts >= 3 && maxLoginAttempts <= 10;
    }
    
    @ValidationMethod(
        message = "Two-factor authentication should be enabled for password lengths less than 12",
        groups = AdvancedValidation.class
    )
    public boolean isTwoFactorAppropriate() {
        return passwordMinLength >= 12 || requireTwoFactor;
    }
}

// Validate with specific groups
Validator validator = BaseValidator.newValidator();
Set<ConstraintViolation<SecurityConfiguration>> basicViolations = 
    validator.validate(config, BasicValidation.class);
Set<ConstraintViolation<SecurityConfiguration>> advancedViolations = 
    validator.validate(config, AdvancedValidation.class);

JAX-RS Resource Method Validation

import io.dropwizard.validation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

@Path("/api/orders")
public class OrderResource {
    
    @POST
    @Path("/create")
    public Response createOrder(
        @Valid @NotNull OrderRequest request,
        @Validated(CreateOrder.class) @NotNull OrderMetadata metadata
    ) {
        // Both request and metadata are validated
        // request uses standard validation, metadata uses CreateOrder group
        return Response.ok().build();
    }
    
    @PUT
    @Path("/{id}/update")
    public Response updateOrder(
        @PathParam("id") @NotNull Long orderId,
        @Validated({UpdateOrder.class, Default.class}) OrderRequest request
    ) {
        // request validated with both UpdateOrder and Default groups
        return Response.ok().build();
    }
    
    @GET
    @Path("/search")
    public Response searchOrders(
        @Validated(SearchCriteria.class) @BeanParam OrderSearchParams params
    ) {
        // Query parameters validated with SearchCriteria group
        return Response.ok().build();
    }
}

// Validation groups
public interface CreateOrder {}
public interface UpdateOrder {}
public interface SearchCriteria {}

Complex Business Rule Validation

import io.dropwizard.validation.ValidationMethod;

public class FinancialConfiguration {
    private BigDecimal interestRate;
    private int loanTermMonths;
    private BigDecimal maxLoanAmount;
    private BigDecimal minDownPayment;
    private String creditScoreRequirement;
    
    @ValidationMethod(message = "Interest rate must be positive and reasonable (0.1% to 50%)")
    public boolean isInterestRateValid() {
        if (interestRate == null) return true;
        
        BigDecimal minRate = new BigDecimal("0.001");  // 0.1%
        BigDecimal maxRate = new BigDecimal("0.50");   // 50%
        
        return interestRate.compareTo(minRate) >= 0 && 
               interestRate.compareTo(maxRate) <= 0;
    }
    
    @ValidationMethod(message = "Loan term must be between 6 months and 30 years")
    public boolean isLoanTermReasonable() {
        return loanTermMonths >= 6 && loanTermMonths <= 360;
    }
    
    @ValidationMethod(message = "Down payment cannot exceed maximum loan amount")
    public boolean isDownPaymentAppropriate() {
        if (minDownPayment == null || maxLoanAmount == null) return true;
        
        return minDownPayment.compareTo(maxLoanAmount) <= 0;
    }
    
    @ValidationMethod(message = "Higher interest rates require lower credit score requirements")
    public boolean isCreditScoreConsistentWithRate() {
        if (interestRate == null || creditScoreRequirement == null) return true;
        
        // Business logic: higher rates should allow lower credit scores
        BigDecimal highRateThreshold = new BigDecimal("0.10"); // 10%
        
        if (interestRate.compareTo(highRateThreshold) > 0) {
            // High rate should allow lower credit requirements
            return !creditScoreRequirement.equals("EXCELLENT");
        }
        
        return true;
    }
}

Integration Notes

JAX-RS Integration

  • @Validated works with JAX-RS resource methods for parameter validation
  • Integrates with Dropwizard's Jersey validation to provide detailed error responses
  • Can be combined with @Valid for comprehensive validation coverage
  • Supports validation of @BeanParam, @QueryParam, @PathParam, and request body parameters

Bean Validation Integration

  • @ValidationMethod integrates seamlessly with standard Bean Validation annotations
  • Method validation occurs in addition to field-level validation
  • Supports validation groups for conditional validation scenarios
  • Works with inheritance - validation methods in parent classes are also executed

Best Practices

  • Use @Validated when you need specific validation groups or ordered validation
  • Use @ValidationMethod for cross-field validation and complex business rules
  • Method names for @ValidationMethod should follow the isSomething() pattern
  • Combine method validation with field validation for comprehensive coverage
  • Consider performance implications of complex validation methods in high-throughput scenarios

Error Handling

  • Method validation violations are reported alongside field validation violations
  • Custom error messages are supported and recommended for business rule clarity
  • Violations can be processed using ConstraintViolations.format() utility methods

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