From 45f875bd1c4b651f7a43617012a4a2c2ae786895 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Thu, 7 Nov 2019 16:44:08 -0500 Subject: [PATCH] hibernate validator: Remember annotated interface methods resolves #1888 --- .../HibernateValidatorProcessor.java | 21 +++++++++++++++++- ...MethodValidatedAnnotationsTransformer.java | 17 +++++++++++++- .../HibernateValidatorTestResource.java | 22 +++++++++++++++++++ .../hibernate/validator/ZipCodeService.java | 10 +++++++++ .../validator/ZipCodeServiceImpl.java | 12 ++++++++++ .../HibernateValidatorFunctionalityTest.java | 12 ++++++++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeService.java create mode 100644 integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeServiceImpl.java diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java index ee36facc0..9e94cbc02 100644 --- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java +++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java @@ -5,8 +5,10 @@ import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -30,6 +32,7 @@ import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -144,6 +147,7 @@ class HibernateValidatorProcessor { consideredAnnotations.add(VALIDATE_ON_EXECUTION); Set classNamesToBeValidated = new HashSet<>(); + Map> inheritedAnnotationsToBeValidated = new HashMap<>(); for (DotName consideredAnnotation : consideredAnnotations) { Collection annotationInstances = indexView.getAnnotations(consideredAnnotation); @@ -160,6 +164,8 @@ class HibernateValidatorProcessor { reflectiveMethods.produce(new ReflectiveMethodBuildItem(annotation.target().asMethod())); contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, consideredAnnotation, annotation.target().asMethod().returnType()); + contributeMethodsWithInheritedValidation(inheritedAnnotationsToBeValidated, indexView, + annotation.target().asMethod()); } else if (annotation.target().kind() == AnnotationTarget.Kind.METHOD_PARAMETER) { contributeClass(classNamesToBeValidated, indexView, annotation.target().asMethodParameter().method().declaringClass().name()); @@ -168,6 +174,8 @@ class HibernateValidatorProcessor { // FIXME this won't work in the case of synthetic parameters annotation.target().asMethodParameter().method().parameters() .get(annotation.target().asMethodParameter().position())); + contributeMethodsWithInheritedValidation(inheritedAnnotationsToBeValidated, indexView, + annotation.target().asMethodParameter().method()); } else if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) { contributeClass(classNamesToBeValidated, indexView, annotation.target().asClass().name()); // no need for reflection in the case of a class level constraint @@ -183,7 +191,8 @@ class HibernateValidatorProcessor { annotationsTransformers .produce(new AnnotationsTransformerBuildItem( new MethodValidatedAnnotationsTransformer(consideredAnnotations, - additionalJaxRsMethodAnnotationsDotNames))); + additionalJaxRsMethodAnnotationsDotNames, + inheritedAnnotationsToBeValidated))); Set> classesToBeValidated = new HashSet<>(); for (DotName className : classNamesToBeValidated) { @@ -245,6 +254,16 @@ class HibernateValidatorProcessor { } } + private static void contributeMethodsWithInheritedValidation(Map> inheritedAnnotationsToBeValidated, + IndexView indexView, MethodInfo method) { + ClassInfo clazz = method.declaringClass(); + if (Modifier.isInterface(clazz.flags())) { + // Remember annotated interface methods that must be validated + inheritedAnnotationsToBeValidated.computeIfAbsent(clazz.name(), k -> new HashSet()) + .add(method.name().toString()); + } + } + private static DotName getClassName(Type type) { switch (type.kind()) { case CLASS: diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/MethodValidatedAnnotationsTransformer.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/MethodValidatedAnnotationsTransformer.java index 00e7fe1b9..98dd70635 100644 --- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/MethodValidatedAnnotationsTransformer.java +++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/MethodValidatedAnnotationsTransformer.java @@ -3,9 +3,11 @@ package io.quarkus.hibernate.validator.deployment; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Map; import java.util.Set; import org.jboss.jandex.AnnotationTarget.Kind; +import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.MethodInfo; @@ -30,10 +32,14 @@ public class MethodValidatedAnnotationsTransformer implements AnnotationsTransfo private final Set consideredAnnotations; private final Collection effectiveJaxRsMethodDefiningAnnotations; + private final Map> inheritedAnnotationsToBeValidated; MethodValidatedAnnotationsTransformer(Set consideredAnnotations, - Collection additionalJaxRsMethodAnnotationsDotNames) { + Collection additionalJaxRsMethodAnnotationsDotNames, + Map> inheritedAnnotationsToBeValidated) { this.consideredAnnotations = consideredAnnotations; + this.inheritedAnnotationsToBeValidated = inheritedAnnotationsToBeValidated; + this.effectiveJaxRsMethodDefiningAnnotations = new ArrayList<>( JAXRS_METHOD_ANNOTATIONS.length + additionalJaxRsMethodAnnotationsDotNames.size()); effectiveJaxRsMethodDefiningAnnotations.addAll(Arrays.asList(JAXRS_METHOD_ANNOTATIONS)); @@ -60,6 +66,15 @@ public class MethodValidatedAnnotationsTransformer implements AnnotationsTransfo private boolean requiresValidation(MethodInfo method) { if (method.annotations().isEmpty()) { + // This method has no annotations of its own: look for inherited annotations + ClassInfo clazz = method.declaringClass(); + String methodName = method.name().toString(); + for (Map.Entry> validatedMethod : inheritedAnnotationsToBeValidated.entrySet()) { + if (clazz.interfaceNames().contains(validatedMethod.getKey()) + && validatedMethod.getValue().contains(methodName)) { + return true; + } + } return false; } diff --git a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java index 568745040..20b6e60e9 100644 --- a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java +++ b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java @@ -38,6 +38,9 @@ public class HibernateValidatorTestResource @Inject GreetingService greetingService; + @Inject + ZipCodeService zipCodeResource; + @GET @Path("/basic-features") @Produces(MediaType.TEXT_PLAIN) @@ -132,6 +135,25 @@ public class HibernateValidatorTestResource return result.build(); } + @GET + @Path("/test-inherited-constraints") + @Produces(MediaType.TEXT_PLAIN) + public String testInheritedConstraints() { + ResultBuilder result = new ResultBuilder(); + + zipCodeResource.echoZipCode("12345"); + + result.append(formatViolations(Collections.emptySet())); + + try { + zipCodeResource.echoZipCode("1234"); + } catch (ConstraintViolationException e) { + result.append(formatViolations(e.getConstraintViolations())); + } + + return result.build(); + } + private String formatViolations(Set> violations) { if (violations.isEmpty()) { return "passed"; diff --git a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeService.java b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeService.java new file mode 100644 index 000000000..6e48a72da --- /dev/null +++ b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeService.java @@ -0,0 +1,10 @@ +package io.quarkus.it.hibernate.validator; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public interface ZipCodeService { + + public String echoZipCode(@NotNull @Size(min = 5, max = 5) String zipCode); + +} diff --git a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeServiceImpl.java b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeServiceImpl.java new file mode 100644 index 000000000..75f93ba0e --- /dev/null +++ b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/ZipCodeServiceImpl.java @@ -0,0 +1,12 @@ +package io.quarkus.it.hibernate.validator; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class ZipCodeServiceImpl implements ZipCodeService { + + @Override + public String echoZipCode(String zipCode) { + return zipCode; + } +} diff --git a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java index 2c388e95a..0a279834e 100644 --- a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java +++ b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java @@ -103,4 +103,16 @@ public class HibernateValidatorFunctionalityTest { .then() .body(is(expected.toString())); } + + @Test + public void testInheritedConstraints() { + StringBuilder expected = new StringBuilder(); + expected.append("passed").append("\n") + .append("failed: echoZipCode.arg0 (size must be between 5 and 5)"); + + RestAssured.when() + .get("/hibernate-validator/test/test-inherited-constraints") + .then() + .body(is(expected.toString())); + } }