Dropwizard Jersey Support - Jersey integration module for the Dropwizard Java framework
—
Hibernate Validator integration providing comprehensive validation support for JAX-RS resources with custom parameter extractors and constraint violation handling. Enables Bean Validation annotations on resource methods and request objects.
Utility class for creating and configuring Hibernate validators with Dropwizard-specific enhancements.
/**
* Utility class for Hibernate Validator configuration
* Provides factory methods for creating properly configured validators
*/
public class Validators {
/** Creates a new Validator with Dropwizard configuration */
public static Validator newValidator();
/** Creates a new ValidatorFactory with Dropwizard configuration */
public static ValidatorFactory newValidatorFactory();
/** Creates a new HibernateValidatorConfiguration with custom extractors */
public static HibernateValidatorConfiguration newConfiguration();
}Usage Examples:
import io.dropwizard.jersey.validation.Validators;
import jakarta.validation.Validator;
import jakarta.validation.ConstraintViolation;
import java.util.Set;
public class ValidationService {
private final Validator validator = Validators.newValidator();
public <T> void validate(T object) {
Set<ConstraintViolation<T>> violations = validator.validate(object);
if (!violations.isEmpty()) {
throw new ValidationException("Validation failed", violations);
}
}
public <T> void validateProperty(T object, String propertyName) {
Set<ConstraintViolation<T>> violations =
validator.validateProperty(object, propertyName);
if (!violations.isEmpty()) {
throw new ValidationException("Property validation failed", violations);
}
}
}Exception class for Jersey-specific validation violations with detailed constraint violation information.
/**
* Exception for Jersey validation violations
* Wraps constraint violations from Bean Validation
*/
public class JerseyViolationException extends ValidationException {
/** Creates exception with constraint violations */
public JerseyViolationException(Set<ConstraintViolation<?>> violations);
/** Gets the constraint violations that caused this exception */
public Set<ConstraintViolation<?>> getConstraintViolations();
/** Gets formatted error messages from constraint violations */
public List<String> getErrorMessages();
}Exception mapper that converts validation exceptions to appropriate HTTP responses with detailed error information.
/**
* Exception mapper for JerseyViolationException
* Converts validation violations to HTTP 422 responses with error details
*/
public class JerseyViolationExceptionMapper implements ExceptionMapper<JerseyViolationException> {
/** Maps JerseyViolationException to HTTP response with validation errors */
public Response toResponse(JerseyViolationException exception);
/** Creates validation error response with detailed field errors */
protected ValidationErrorMessage createErrorMessage(JerseyViolationException exception);
}Structured error message format for validation failures with field-level error details.
/**
* Structured validation error message with field-level errors
* Provides detailed information about validation failures
*/
public class ValidationErrorMessage {
/** Creates validation error message from error list */
public ValidationErrorMessage(Collection<String> errors);
/** Gets list of validation error messages */
public List<String> getErrors();
/** Gets validation errors grouped by field name */
public Map<String, List<String>> getFieldErrors();
}
/**
* Individual constraint violation message with field and error details
*/
public class ConstraintMessage {
/** Creates constraint message from violation */
public ConstraintMessage(ConstraintViolation<?> violation);
/** Gets the field or property path that failed validation */
public String getPath();
/** Gets the validation error message */
public String getMessage();
/** Gets the invalid value that caused the violation */
public Object getInvalidValue();
/** Gets the constraint annotation type */
public String getConstraintType();
}Custom value extractors that enable validation on Dropwizard parameter types.
/**
* Value extractor for AbstractParam types
* Enables Bean Validation on parameter wrapper classes
* @param <T> the wrapped parameter type
*/
public class ParamValueExtractor<T> implements ValueExtractor<AbstractParam<@ExtractedValue T>> {
/** Extracts the wrapped value for validation */
public void extractValues(AbstractParam<T> originalValue, ValueReceiver receiver);
/** Descriptor for registering the extractor */
public static final ValueExtractorDescriptor DESCRIPTOR;
}
/**
* Value extractor specifically for NonEmptyStringParam
* Provides specialized validation support for non-empty string parameters
*/
public class NonEmptyStringParamValueExtractor implements ValueExtractor<NonEmptyStringParam> {
/** Extracts string value for validation */
public void extractValues(NonEmptyStringParam originalValue, ValueReceiver receiver);
/** Descriptor for registering the extractor */
public static final ValueExtractorDescriptor DESCRIPTOR;
}Parameter name provider that integrates with Jersey to provide meaningful parameter names for validation error messages.
/**
* Parameter name provider for Jersey integration
* Provides parameter names from JAX-RS annotations for validation errors
*/
public class JerseyParameterNameProvider implements ParameterNameProvider {
/** Gets parameter names for method parameters using JAX-RS annotations */
public List<String> getParameterNames(Method method);
/** Gets parameter names for constructor parameters */
public List<String> getParameterNames(Constructor<?> constructor);
}/**
* Hibernate Validation binder for Jersey integration
* Registers validation components with Jersey's dependency injection
*/
public class HibernateValidationBinder extends AbstractBinder {
/** Configures validation bindings */
protected void configure();
}
/**
* Mutable validator factory for constraint validators
* Allows dynamic constraint validator registration
*/
public class MutableValidatorFactory implements ConstraintValidatorFactory {
/** Creates or retrieves constraint validator instance */
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key);
/** Releases constraint validator instance */
public void releaseInstance(ConstraintValidator<?, ?> instance);
}
/**
* Dropwizard-configured validator context resolver
* Provides configured validators for Jersey resources
*/
public class DropwizardConfiguredValidator implements ContextResolver<Validator> {
/** Gets configured validator instance */
public Validator getContext(Class<?> type);
}Parameter converter that provides fuzzy matching for enum values with validation support.
/**
* Parameter converter for enum types with fuzzy matching
* Provides case-insensitive and partial matching for enum parameters
* @param <T> the enum type
*/
public class FuzzyEnumParamConverter<T extends Enum<T>> implements ParamConverter<T> {
/** Converts string to enum with fuzzy matching */
public T fromString(String value);
/** Converts enum to string representation */
public String toString(T value);
/** Gets enum type being converted */
public Class<T> getEnumType();
}
/**
* Provider for fuzzy enum parameter converters
* Automatically provides converters for enum types
*/
public class FuzzyEnumParamConverterProvider implements ParamConverterProvider {
/** Gets parameter converter for enum types */
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations);
}import io.dropwizard.jersey.params.UUIDParam;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import jakarta.ws.rs.*;
@Path("/users")
public class UserResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createUser(@Valid @NotNull CreateUserRequest request) {
// @Valid triggers validation of the request object
// @NotNull ensures request is not null
User user = userService.create(request);
return Response.status(201).entity(user).build();
}
@PUT
@Path("/{id}")
public Response updateUser(@PathParam("id") UUIDParam userId,
@Valid UpdateUserRequest request) {
// Both parameter and request body validation
User user = userService.update(userId.get(), request);
return Response.ok(user).build();
}
@GET
public List<User> getUsers(@QueryParam("page") @Min(1) @Max(1000) Integer page,
@QueryParam("size") @Min(1) @Max(100) Integer size,
@QueryParam("status") UserStatus status) {
// Parameter-level validation constraints
int actualPage = page != null ? page : 1;
int actualSize = size != null ? size : 10;
return userService.getUsers(actualPage, actualSize, status);
}
}import jakarta.validation.constraints.*;
import jakarta.validation.Valid;
import java.time.LocalDate;
import java.util.List;
public class CreateUserRequest {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Email must be a valid email address")
private String email;
@Min(value = 18, message = "Age must be at least 18")
@Max(value = 120, message = "Age must be less than 120")
private Integer age;
@Past(message = "Birth date must be in the past")
private LocalDate birthDate;
@Valid // Cascade validation to nested objects
@NotNull(message = "Address is required")
private Address address;
@Size(max = 5, message = "Maximum 5 phone numbers allowed")
private List<@Pattern(regexp = "\\d{10}", message = "Phone number must be 10 digits") String> phoneNumbers;
// getters and setters
}
public class Address {
@NotBlank(message = "Street is required")
private String street;
@NotBlank(message = "City is required")
private String city;
@Pattern(regexp = "\\d{5}", message = "ZIP code must be 5 digits")
private String zipCode;
// getters and setters
}import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidUserId.Validator.class)
@Documented
public @interface ValidUserId {
String message() default "Invalid user ID format";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class Validator implements ConstraintValidator<ValidUserId, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return true; // Let @NotNull handle null checks
// Custom validation logic
return value.matches("^[A-Z]{2}\\d{6}$");
}
}
}
// Usage
public class UserRequest {
@ValidUserId
@NotNull
private String userId;
}import jakarta.validation.groups.Default;
public interface CreateGroup {}
public interface UpdateGroup {}
public class UserRequest {
@NotNull(groups = {CreateGroup.class, UpdateGroup.class})
private String name;
@NotNull(groups = CreateGroup.class)
@Null(groups = UpdateGroup.class, message = "ID cannot be specified for updates")
private String id;
@Email(groups = {CreateGroup.class, UpdateGroup.class})
private String email;
}
@Path("/users")
public class UserResource {
@POST
public Response createUser(@Valid(CreateGroup.class) UserRequest request) {
// Validates with CreateGroup constraints
return Response.ok().build();
}
@PUT
@Path("/{id}")
public Response updateUser(@PathParam("id") String id,
@Valid(UpdateGroup.class) UserRequest request) {
// Validates with UpdateGroup constraints
return Response.ok().build();
}
}Validation errors are returned as HTTP 422 responses with detailed field information:
{
"code": 422,
"message": "Validation failed",
"errors": [
"name: Name is required",
"email: Email must be a valid email address",
"age: Age must be at least 18"
],
"fieldErrors": {
"name": ["Name is required"],
"email": ["Email must be a valid email address"],
"age": ["Age must be at least 18"]
}
}@Provider
public class CustomValidationExceptionMapper implements ExceptionMapper<JerseyViolationException> {
@Override
public Response toResponse(JerseyViolationException exception) {
Map<String, List<String>> fieldErrors = new HashMap<>();
List<String> globalErrors = new ArrayList<>();
for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
String propertyPath = violation.getPropertyPath().toString();
String message = violation.getMessage();
if (propertyPath.isEmpty()) {
globalErrors.add(message);
} else {
fieldErrors.computeIfAbsent(propertyPath, k -> new ArrayList<>()).add(message);
}
}
ValidationErrorResponse error = new ValidationErrorResponse();
error.setMessage("Validation failed");
error.setFieldErrors(fieldErrors);
error.setGlobalErrors(globalErrors);
return Response.status(422).entity(error).build();
}
}public class ValidationBestPractices {
// Use appropriate validation annotations
@NotNull // For null checks
@NotBlank // For strings that should not be null, empty, or whitespace
@NotEmpty // For collections/arrays that should not be null or empty
@Size(min = 1, max = 100) // For length constraints
@Min(1) @Max(1000) // For numeric ranges
@Email // For email validation
@Pattern // For custom regex validation
@Past // For dates in the past
@Future // For dates in the future
// Cascade validation to nested objects
@Valid
private Address address;
// Validate collection elements
private List<@Valid ContactInfo> contacts;
// Group validation for different scenarios
@NotNull(groups = CreateGroup.class)
@Null(groups = UpdateGroup.class)
private String id;
}@Path("/api")
public class ValidationExamples {
// Validate path parameters
@GET
@Path("/users/{id}")
public User getUser(@PathParam("id") @Pattern(regexp = "\\d+") String id) {
return userService.findById(Long.parseLong(id));
}
// Validate query parameters with defaults
@GET
@Path("/search")
public SearchResults search(
@QueryParam("q") @NotBlank @Size(min = 2, max = 100) String query,
@QueryParam("page") @Min(1) @DefaultValue("1") int page,
@QueryParam("size") @Min(1) @Max(100) @DefaultValue("10") int size) {
return searchService.search(query, page, size);
}
// Validate header parameters
@POST
@Path("/data")
public Response uploadData(
@HeaderParam("Content-Type") @Pattern(regexp = "application/.*") String contentType,
@Valid UploadRequest request) {
return Response.ok().build();
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-jersey