mcmahonS9
mcmahonS9

Reputation: 43

Jakarta Custom Validation - How to avoid tight coupling of Annotation and ConstraintValidator

I have a project which publishes a service contract, eg endpoints and model classes. I have created a custom Jakarta validation which checks that when a date range is provided, the end date must be later or equal to the start date. Eg:

@Constraint(validatedBy = DateRangeValidator.class)
public @interface DateRangeValid {
   ...
}

public class DateRangeValidator implements ConstraintValidator<DateRangeValid, Object> {
   ...
}

However, this seems like it creates a very tight coupling between the annotation and valid, since both reference each other.

Is there a way to decouple these 2? By, say, having only the Validator reference the annotation, and not the other way round?

When I look at the validations that come as part of Jakarta, eg @Max, I see the annotation does not reference any validator:

@Constraint(
    validatedBy = {}
)
public @interface Max {
   ...
}

But when I try the same on my custom annotation, I get the following error in my tests:

jakarta.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint

Does anyone know an approach to solve this? Ideally I'd like to only publish the Annotation in my service contract, and keep the validation logic internal to my service so that it is easier to change in future if required.

Upvotes: 0

Views: 278

Answers (1)

Rob Spoor
Rob Spoor

Reputation: 9175

There needs to be a mapping from constraint annotation to the validators that are used to enforce the constraint. For out-of-the-box annotations these validators are mapped by the framework implementation (usually Hibernate Validator). For custom annotations that's usually done through the validatedBy annotation attribute. The only other way is to add this mapping programmatically. I don't think that can be done with ConstraintValidatorFactory as suggested by Sharad Paul because that already needs the validator class.

I've found that Hibernate's ConstraintDefinitionContext interface has a method to add a validator to a constraint, but I couldn't find a way to use it without overwriting all of the annotation-based constraint mappings.

I've also found ServiceLoaderBasedConstraintMappingContributor that, according to How to register custom ConstraintMapping for validations defined in validation-constraints.xml, should allow you to provide your validators through the ServiceLoader mechanism. Perhaps this is the most flexible you're going to get.

Upvotes: 0

Related Questions