Dropwizard Validation Support - provides enhanced validation capabilities for Dropwizard applications
—
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.
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();
}
}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...
}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);
}
}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);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 {}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;
}
}@Validated works with JAX-RS resource methods for parameter validation@Valid for comprehensive validation coverage@BeanParam, @QueryParam, @PathParam, and request body parameters@ValidationMethod integrates seamlessly with standard Bean Validation annotations@Validated when you need specific validation groups or ordered validation@ValidationMethod for cross-field validation and complex business rules@ValidationMethod should follow the isSomething() patternConstraintViolations.format() utility methodsInstall with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-validation