Custom Bean Validation (JSR-303) Annotations

I’ve got some java code that I would like to port to kotlin. It makes heavy use of bean validation annotations. I’ve already run into the limitation of not being able to decorate a field with repeated annotations (see here: Issue with repeated Java 8 annotations), and that is certainly complicating porting the code.

Additionally, the code in question also includes custom annotations to stack validation. For example:

@Documented
@Constraint(validatedBy = {})
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Pattern(regexp = "^(([\\p{Alnum}]([-\\p{Alnum}]*[\\p{Alnum}])?)\\.)*([\\p{Alpha}]([-\\p{Alnum}]*[\\p{Alnum}])?)$")
@ReportAsSingleViolation
public @interface Hostname {

    String message() default "Invalid hostname or hostname component";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

In our java code, we regularly decorate fields or getters with annotations such as this as well as @Pattern to enforce multiple regex-based validations (don’t get me started on that). But I’m having trouble getting a kotlin version of this annotation to be picked up by any of the validation enforcement options like spring.

Help?

Did you try to compare bytecode generated by Java and Kotlin compilers to see if there are any differences that might explain why Kotlin versions of the annotations are not recognized?

Ok, I misspoke. The annotation was being picked up just fine, I was just missing some fields. Custom annotations deriving from javax.validation.Constraint are expected to have the following methods:

  • String message()
  • Class<?> groups
  • Class<? extends Payload> payload()

I was able to get it to work by providing these fields to the kotlin annotation’s constructor:

@MustBeDocumented
@Constraint(validatedBy = [])
@Target(
    AnnotationTarget.FUNCTION, AnnotationTarget.FIELD, AnnotationTarget.ANNOTATION_CLASS,
    AnnotationTarget.PROPERTY_GETTER
)
@Retention(AnnotationRetention.RUNTIME)
@Pattern(regexp = "[012345]")
@ReportAsSingleViolation
annotation class ExampleCustomAnnotation(
    val message: String = "foo",
    val groups: Array<KClass<out Any>> = [],
    val payload: Array<KClass<out Any>> = []
)

1 Like