From 1e071112887a8d2bddd7c8320bcc52a87acd79f6 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 25 Nov 2016 15:37:38 +0100 Subject: [PATCH 1/8] HV-644 Initial Jandex implementation --- engine/pom.xml | 5 + ...stractConstrainedElementJandexBuilder.java | 464 ++++++++++++++++++ .../jandex/ClassConstrainsJandexBuilder.java | 81 +++ .../jandex/ConstrainedFieldJandexBuilder.java | 144 ++++++ .../ConstrainedMethodJandexBuilder.java | 340 +++++++++++++ .../util/GroupSequenceJandexHelper.java | 99 ++++ .../metadata/jandex/util/JandexHelper.java | 167 +++++++ .../provider/JandexMetaDataProvider.java | 132 +++++ .../internal/metadata/provider/package.html | 2 +- .../metadata/raw/ConfigurationSource.java | 6 +- .../validator/internal/util/logging/Log.java | 6 + .../util/privilegedactions/LoadClass.java | 2 +- .../ConstrainedFieldJandexBuilderTest.java | 99 ++++ .../ConstrainedFieldJandexBuilderModel.java | 67 +++ pom.xml | 8 + 15 files changed, 1619 insertions(+), 3 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ClassConstrainsJandexBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java diff --git a/engine/pom.xml b/engine/pom.xml index a52590697f..6ed67aacc0 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -94,6 +94,11 @@ money-api true + + org.jboss + jandex + true + + 2.0.3.Final + 2.0.1.Alpha3 5.0.3 @@ -365,6 +368,11 @@ guava 20.0 + + org.jboss + jandex + ${jandex.version} + From 8e59f61fd0e5a9900b2571e44d94dd8ff80ba290 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 21 Dec 2016 15:57:25 +0100 Subject: [PATCH 2/8] HV-644 Fix the build --- distribution/pom.xml | 4 ++++ ...AbstractConstrainedElementJandexBuilder.java | 2 +- .../jandex/util/GroupSequenceJandexHelper.java | 4 +--- .../metadata/jandex/util/JandexHelper.java | 17 ++++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/distribution/pom.xml b/distribution/pom.xml index 4b99e35ab0..0d0a6c24d2 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -68,6 +68,10 @@ org.jsoup jsoup + + org.jboss + jandex + com.thoughtworks.paranamer paranamer diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java index 5cf753b510..144fa67bdc 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java @@ -215,7 +215,7 @@ protected A instanceToAnnotation(Class annotationClass * * @param allAnnotations collection to look for constraints in * - * @return a {@link Stream} representing constraint annotations + * @return a {@link Stream} of the constraint annotations */ protected Stream findConstrainAnnotations(Collection allAnnotations) { return allAnnotations.stream().filter( this::isConstraintAnnotation ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java index 6d00d41060..691742ee0d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java @@ -7,11 +7,10 @@ package org.hibernate.validator.internal.metadata.jandex.util; import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Optional; import java.util.stream.Stream; + import javax.validation.GroupSequence; import org.hibernate.validator.group.GroupSequenceProvider; @@ -20,7 +19,6 @@ import org.hibernate.validator.internal.util.privilegedactions.GetMethods; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java index 54f3d6f517..252a884bff 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java @@ -46,7 +46,7 @@ private JandexHelper(ClassLoader classLoader) { * @return an instance of {@link JandexHelper} */ public static JandexHelper getInstance(ClassLoader classLoader) { - return new JandexHelper(classLoader); + return new JandexHelper( classLoader ); } public static JandexHelper getInstance() { @@ -108,18 +108,17 @@ public boolean isIterable(Type type) { } /** - * Finds an annotation of a given type inside provided collection. + * Finds an annotation of a given type inside the provided collection of annotations. * * @param annotations a collection of annotation in which to look for a provided annotation type - * @param aClass a type of annotation to look for. - * - * @return an {@link Optional} which will contain a found annotation, an empty {@link Optional} - * if none was found. Also if there are more than one annotation of provided type present in the collection there's - * no guarantee which one will be returned. + * @param clazz a type of annotation to look for + * @return an {@code Optional} which will contain a found annotation, the {@code Optional} being + * empty if none was found. Also if there are more than one annotation of the provided type present in the + * collection there's no guarantee which one will be returned. */ - public Optional findAnnotation(Collection annotations, Class aClass) { + public Optional findAnnotation(Collection annotations, Class clazz) { return annotations.stream() - .filter( annotation -> annotation.name().toString().equals( aClass.getName() ) ) + .filter( annotation -> annotation.name().toString().equals( clazz.getName() ) ) .findAny(); } From 05b4139309c2c0e13f0526e455e67edbd055a77e Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 22 Dec 2016 15:06:25 +0100 Subject: [PATCH 3/8] HV-644 Implement constraint annotations extraction Minor cleanup in passing --- ...stractConstrainedElementJandexBuilder.java | 17 +-- ...ava => ClassConstraintsJandexBuilder.java} | 24 +--- .../jandex/ConstrainedFieldJandexBuilder.java | 23 +--- .../ConstrainedMethodJandexBuilder.java | 22 +--- .../provider/JandexMetaDataProvider.java | 120 +++++++++++------- .../ConstrainedFieldJandexBuilderTest.java | 39 ++---- 6 files changed, 110 insertions(+), 135 deletions(-) rename engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/{ClassConstrainsJandexBuilder.java => ClassConstraintsJandexBuilder.java} (74%) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java index 144fa67bdc..7b0e62fba7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java @@ -37,6 +37,7 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.DotName; import org.jboss.jandex.Type; /** @@ -54,11 +55,14 @@ public abstract class AbstractConstrainedElementJandexBuilder { protected final AnnotationProcessingOptions annotationProcessingOptions; + private final List constraintAnnotations; + protected AbstractConstrainedElementJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - AnnotationProcessingOptions annotationProcessingOptions) { + AnnotationProcessingOptions annotationProcessingOptions, List constraintAnnotations) { this.jandexHelper = jandexHelper; this.constraintHelper = constraintHelper; this.annotationProcessingOptions = annotationProcessingOptions; + this.constraintAnnotations = constraintAnnotations; } /** @@ -229,16 +233,7 @@ protected Stream findConstrainAnnotations(Collection constraintAnnotations) { + super( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ); } /** @@ -53,7 +43,7 @@ public static ClassConstrainsJandexBuilder getInstance(ConstraintHelper constrai * * @return a stream of {@link ConstrainedElement}s that represent class type */ - public Stream getClassConstrains(ClassInfo classInfo, Class beanClass) { + public Stream getClassConstraints(ClassInfo classInfo, Class beanClass) { if ( annotationProcessingOptions.areClassLevelConstraintsIgnoredFor( beanClass ) ) { return Stream.empty(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java index 9387fd8a46..534a747cec 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java @@ -9,6 +9,7 @@ import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -24,6 +25,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedField; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; /** @@ -33,28 +35,15 @@ */ public class ConstrainedFieldJandexBuilder extends AbstractConstrainedElementJandexBuilder { - private ConstrainedFieldJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - AnnotationProcessingOptions annotationProcessingOptions) { - super( constraintHelper, jandexHelper, annotationProcessingOptions ); - } - - /** - * Creates an instance of a {@link ConstrainedFieldJandexBuilder}. - * - * @param constraintHelper an instance of {@link ConstraintHelper} - * @param jandexHelper an instance of {@link JandexHelper} - * - * @return a new instance of {@link ConstrainedFieldJandexBuilder} - */ - public static ConstrainedFieldJandexBuilder getInstance(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - AnnotationProcessingOptions annotationProcessingOptions) { - return new ConstrainedFieldJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions ); + public ConstrainedFieldJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, + AnnotationProcessingOptions annotationProcessingOptions, List constraintAnnotations) { + super( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ); } /** * Gets {@link ConstrainedField}s from a given class. * - * @param classInfo a class in which to look for constrained fileds + * @param classInfo a class in which to look for constrained fields * @param beanClass same class as {@code classInfo} but represented as {@link Class} * * @return a stream of {@link ConstrainedElement}s that represents fields diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java index 00fbb90a3b..a0d8063789 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java @@ -28,9 +28,9 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; @@ -43,25 +43,13 @@ public class ConstrainedMethodJandexBuilder extends AbstractConstrainedElementJa protected final ExecutableParameterNameProvider parameterNameProvider; - private ConstrainedMethodJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider) { - super( constraintHelper, jandexHelper, annotationProcessingOptions ); + public ConstrainedMethodJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, + AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider, + List constraintAnnotations) { + super( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ); this.parameterNameProvider = parameterNameProvider; } - /** - * Creates an instance of a {@link ConstrainedMethodJandexBuilder}. - * - * @param constraintHelper an instance of {@link ConstraintHelper} - * @param jandexHelper an instance of {@link JandexHelper} - * - * @return a new instance of {@link ConstrainedMethodJandexBuilder} - */ - public static ConstrainedMethodJandexBuilder getInstance(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider) { - return new ConstrainedMethodJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions, parameterNameProvider ); - } - /** * Gets {@link ConstrainedExecutable}s from a given class. * diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java index 36b46dbbac..5434402b81 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java @@ -6,15 +6,19 @@ */ package org.hibernate.validator.internal.metadata.provider; -import java.io.IOException; -import java.io.InputStream; +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; + +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.Constraint; + import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.jandex.ClassConstrainsJandexBuilder; +import org.hibernate.validator.internal.metadata.jandex.ClassConstraintsJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.ConstrainedFieldJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.ConstrainedMethodJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.util.GroupSequenceJandexHelper; @@ -22,72 +26,74 @@ import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; - +import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; +import org.jboss.jandex.AnnotationTarget.Kind; import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.Index; -import org.jboss.jandex.IndexReader; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; /** * @author Marko Bekhta + * @author Guillaume Smet */ -public class JandexMetaDataProvider extends MetaDataProviderKeyedByClassName { +public class JandexMetaDataProvider implements MetaDataProvider { - private static final Log log = LoggerFactory.make(); + private static final DotName CONSTRAINT_ANNOTATION = DotName.createSimple( Constraint.class.getName() ); - private AnnotationProcessingOptions annotationProcessingOptions; - protected final ExecutableParameterNameProvider parameterNameProvider; + private final Map> configuredBeans; + + private final AnnotationProcessingOptions annotationProcessingOptions; public JandexMetaDataProvider( ConstraintHelper constraintHelper, JandexHelper jandexHelper, - InputStream jandexIndexStreamResource, + IndexView indexView, AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider) { - super( constraintHelper, - readJandexIndex( constraintHelper, jandexHelper, jandexIndexStreamResource, annotationProcessingOptions, parameterNameProvider ) - ); this.annotationProcessingOptions = annotationProcessingOptions; - this.parameterNameProvider = parameterNameProvider; + + List constraintAnnotations = Collections.unmodifiableList( extractConstraintAnnotations( indexView ) ); + + this.configuredBeans = Collections.unmodifiableMap( extractConfiguredBeans( indexView, + constraintHelper, jandexHelper, + annotationProcessingOptions, parameterNameProvider, + constraintAnnotations ) ); } - private static Map> readJandexIndex(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - InputStream jandexIndexStreamResource, AnnotationProcessingOptions annotationProcessingOptions, - ExecutableParameterNameProvider parameterNameProvider) { - IndexReader jandexReader = new IndexReader( jandexIndexStreamResource ); - Index index; - try { - index = jandexReader.read(); - } - catch (IOException e) { - throw log.getParsingJandexIndexException( e ); - } + private static List extractConstraintAnnotations(IndexView indexView) { + return indexView.getAnnotations( CONSTRAINT_ANNOTATION ).stream() + .filter( ai -> Kind.CLASS.equals( ai.target().kind() ) ) + .map( ai -> ai.target().asClass().name() ) + .collect( Collectors.toList() ); + } - // go through all classes (and interfaces ?) to build configuration map - return index.getKnownClasses().stream() + private static Map> extractConfiguredBeans(IndexView indexView, ConstraintHelper constraintHelper, JandexHelper jandexHelper, + AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider, List constraintAnnotations) { + return indexView.getKnownClasses().stream() .collect( Collectors.toMap( - classInfo -> classInfo.name().toString(), + classInfo -> classInfo.name(), classInfo -> getBeanConfiguration( constraintHelper, jandexHelper, classInfo, annotationProcessingOptions, - parameterNameProvider + parameterNameProvider, + constraintAnnotations ) ) ); - } - private static BeanConfiguration getBeanConfiguration(ConstraintHelper constraintHelper, JandexHelper jandexHelper, - ClassInfo classInfo, AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider) { + private static BeanConfiguration getBeanConfiguration(ConstraintHelper constraintHelper, JandexHelper jandexHelper, + ClassInfo classInfo, AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider, + List constraintAnnotations) { GroupSequenceJandexHelper groupSequenceJandexHelper = GroupSequenceJandexHelper.getInstance( jandexHelper ); Class bean = jandexHelper.getClassForName( classInfo.name().toString() ); - return new BeanConfiguration( + return new BeanConfiguration<>( ConfigurationSource.JANDEX, bean, - getConstrainedElements( constraintHelper, jandexHelper, classInfo, bean, annotationProcessingOptions, parameterNameProvider ) + getConstrainedElements( constraintHelper, jandexHelper, classInfo, bean, annotationProcessingOptions, parameterNameProvider, constraintAnnotations ) .collect( Collectors.toSet() ), groupSequenceJandexHelper.getGroupSequence( classInfo ).collect( Collectors.toList() ), groupSequenceJandexHelper.getGroupSequenceProvider( classInfo ) @@ -100,27 +106,31 @@ private static Stream getConstrainedElements( ClassInfo classInfo, Class bean, AnnotationProcessingOptions annotationProcessingOptions, - ExecutableParameterNameProvider parameterNameProvider + ExecutableParameterNameProvider parameterNameProvider, + List constraintAnnotations ) { //get constrained fields - Stream constrainedElementStream = ConstrainedFieldJandexBuilder.getInstance( + Stream constrainedElementStream = new ConstrainedFieldJandexBuilder( constraintHelper, jandexHelper, - annotationProcessingOptions + annotationProcessingOptions, + constraintAnnotations ).getConstrainedFields( classInfo, bean ); //get constrained methods/constructors ? - Stream.concat( constrainedElementStream, ConstrainedMethodJandexBuilder.getInstance( + Stream.concat( constrainedElementStream, new ConstrainedMethodJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions, - parameterNameProvider + parameterNameProvider, + constraintAnnotations ).getConstrainedExecutables( classInfo, bean ) ); //get class level constraints - Stream.concat( constrainedElementStream, ClassConstrainsJandexBuilder.getInstance( + Stream.concat( constrainedElementStream, new ClassConstraintsJandexBuilder( constraintHelper, jandexHelper, - annotationProcessingOptions - ).getClassConstrains( classInfo, bean ) ); + annotationProcessingOptions, + constraintAnnotations + ).getClassConstraints( classInfo, bean ) ); return constrainedElementStream; } @@ -129,4 +139,24 @@ private static Stream getConstrainedElements( public AnnotationProcessingOptions getAnnotationProcessingOptions() { return annotationProcessingOptions; } + + @Override + public List> getBeanConfigurationForHierarchy(Class beanClass) { + List> configurations = newArrayList(); + + for ( Class clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) { + BeanConfiguration configuration = getBeanConfiguration( clazz ); + if ( configuration != null ) { + configurations.add( configuration ); + } + } + + return configurations; + } + + @SuppressWarnings("unchecked") + protected BeanConfiguration getBeanConfiguration(Class beanClass) { + Contracts.assertNotNull( beanClass ); + return (BeanConfiguration) configuredBeans.get( DotName.createSimple( beanClass.getName() ) ); + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java index 7e8b1132c6..cd21cecad8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java @@ -10,32 +10,30 @@ import java.io.InputStream; import java.util.stream.Collectors; +import org.assertj.core.api.ListAssert; +import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.jandex.ClassConstrainsJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.ConstrainedFieldJandexBuilder; -import org.hibernate.validator.internal.metadata.jandex.ConstrainedMethodJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.util.JandexHelper; +import org.hibernate.validator.internal.metadata.provider.JandexMetaDataProvider; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider; import org.hibernate.validator.test.internal.metadata.jandex.model.ConstrainedFieldJandexBuilderModel; - import org.jboss.jandex.DotName; -import org.jboss.jandex.Index; +import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; - -import org.assertj.core.api.ListAssert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * @author Marko Bekhta + * @author Guillaume Smet */ public class ConstrainedFieldJandexBuilderTest { - private Index index; + private IndexView indexView; @BeforeClass public void setUp() throws IOException { @@ -45,7 +43,7 @@ public void setUp() throws IOException { "org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.class" ) ) { indexer.index( stream ); - index = indexer.complete(); + indexView = indexer.complete(); } } @@ -54,29 +52,15 @@ public void setUp() throws IOException { */ @Test public void testGetConstrainedFields() { - ConstrainedFieldJandexBuilder.getInstance( new ConstraintHelper(), JandexHelper.getInstance(), new AnnotationProcessingOptionsImpl() ) - .getConstrainedFields( - index.getClassByName( DotName.createSimple( ConstrainedFieldJandexBuilderModel.class.getName() ) ), - ConstrainedFieldJandexBuilderModel.class - ).collect( Collectors.toSet() ); - - ConstrainedMethodJandexBuilder.getInstance( new ConstraintHelper(), JandexHelper.getInstance(), new AnnotationProcessingOptionsImpl(), - new ExecutableParameterNameProvider( new ParanamerParameterNameProvider() ) - ).getConstrainedExecutables( - index.getClassByName( DotName.createSimple( ConstrainedFieldJandexBuilderModel.class.getName() ) ), - ConstrainedFieldJandexBuilderModel.class - ).collect( Collectors.toSet() ); + JandexMetaDataProvider jandexMetaDataProvider = new JandexMetaDataProvider( new ConstraintHelper(), JandexHelper.getInstance(), indexView, + new AnnotationProcessingOptionsImpl(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ) ); - ClassConstrainsJandexBuilder.getInstance( new ConstraintHelper(), JandexHelper.getInstance(), new AnnotationProcessingOptionsImpl() ) - .getClassConstrains( - index.getClassByName( DotName.createSimple( ConstrainedFieldJandexBuilderModel.class.getName() ) ), - ConstrainedFieldJandexBuilderModel.class - ).collect( Collectors.toSet() ); + jandexMetaDataProvider.getBeanConfigurationForHierarchy( ConstrainedFieldJandexBuilderModel.class ); } @Test public void validAnnotationIsMissing() { - MethodInfo method = index.getClassByName( DotName.createSimple( ConstrainedFieldJandexBuilderModel.class.getName() ) ) + MethodInfo method = indexView.getClassByName( DotName.createSimple( ConstrainedFieldJandexBuilderModel.class.getName() ) ) .methods().stream().filter( methodInfo -> methodInfo.name().equals( "someMethod1" ) ) .findAny().get(); ListAssert parametersAssert = new ListAssert<>( method.parameters() ); @@ -93,7 +77,6 @@ public void validAnnotationIsMissing() { .collect( Collectors.toList() ) ); annotations.hasSize( 3 ); annotations.contains( "javax.validation.constraints.NotNull", "javax.validation.constraints.Size", "javax.validation.Valid" ); - } } From 3ec21698c72febc230d19266883b996a2b0076a0 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 22 Dec 2016 16:06:58 +0100 Subject: [PATCH 4/8] HV-644 Fix stream concatenation --- .../provider/JandexMetaDataProvider.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java index 5434402b81..c3228a5a9d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java @@ -109,30 +109,34 @@ private static Stream getConstrainedElements( ExecutableParameterNameProvider parameterNameProvider, List constraintAnnotations ) { - //get constrained fields - Stream constrainedElementStream = new ConstrainedFieldJandexBuilder( + // get constrained fields + Stream constrainedFieldStream = new ConstrainedFieldJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ).getConstrainedFields( classInfo, bean ); - //get constrained methods/constructors ? - Stream.concat( constrainedElementStream, new ConstrainedMethodJandexBuilder( + + //get constrained methods/constructors + Stream constrainedMethodStream = new ConstrainedMethodJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions, parameterNameProvider, constraintAnnotations - ).getConstrainedExecutables( classInfo, bean ) ); - //get class level constraints - Stream.concat( constrainedElementStream, new ClassConstraintsJandexBuilder( + ).getConstrainedExecutables( classInfo, bean ); + + // get class level constraints + Stream constrainedClassStream = new ClassConstraintsJandexBuilder( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations - ).getClassConstraints( classInfo, bean ) ); + ).getClassConstraints( classInfo, bean ); - return constrainedElementStream; + return Stream.of( constrainedClassStream, constrainedFieldStream, constrainedMethodStream ) + .reduce( Stream::concat ) + .orElse( Stream.empty() ); } @Override From 865720ab7df43622a3a884224745ec754f2f7991 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 22 Dec 2016 16:07:24 +0100 Subject: [PATCH 5/8] HV-644 Fix method constraints retrieval --- .../metadata/jandex/ConstrainedMethodJandexBuilder.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java index a0d8063789..68cc2cf803 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java @@ -97,7 +97,9 @@ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, Method crossParameterConstraints = Collections.emptySet(); } else { - crossParameterConstraints = executableConstraints.get( ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER ); + crossParameterConstraints = executableConstraints.containsKey( ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER ) ? + executableConstraints.get( ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER ) : + Collections.emptySet(); } Set> returnValueConstraints; @@ -129,7 +131,9 @@ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, Method isCascading ); - returnValueConstraints = executableConstraints.get( ConstraintDescriptorImpl.ConstraintType.GENERIC ); + returnValueConstraints = executableConstraints.containsKey( ConstraintDescriptorImpl.ConstraintType.GENERIC ) ? + executableConstraints.get( ConstraintDescriptorImpl.ConstraintType.GENERIC ) : + Collections.emptySet(); } return new ConstrainedExecutable( From d7a437a6bc1a058c6894c1fabe1743ad1873adf2 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 22 Dec 2016 17:45:39 +0100 Subject: [PATCH 6/8] HV-644 Filter out synthetic methods We will probably need to ask the Jandex guys to add a proper method. Same for the constructor thing. --- .../ConstrainedMethodJandexBuilder.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java index 68cc2cf803..ef79f4d6da 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java @@ -38,9 +38,13 @@ * Builder for constrained methods that uses Jandex index. * * @author Marko Bekhta + * @author Guillaume Smet */ public class ConstrainedMethodJandexBuilder extends AbstractConstrainedElementJandexBuilder { + private static final int SYNTHETIC = 0x1000; + private static final int BRIDGE = 0x0040; + protected final ExecutableParameterNameProvider parameterNameProvider; public ConstrainedMethodJandexBuilder(ConstraintHelper constraintHelper, JandexHelper jandexHelper, @@ -61,11 +65,9 @@ public ConstrainedMethodJandexBuilder(ConstraintHelper constraintHelper, JandexH public Stream getConstrainedExecutables(ClassInfo classInfo, Class beanClass) { // HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints // anyway and possibly hide the actual method with the same signature in the built meta model - //TODO: check for synthetic somehow ? - // void () - such method is included in the method list but how to filter it out ? return classInfo.methods().stream() - .filter( methodInfo -> !Modifier.isStatic( methodInfo.flags() ) /*&& !Modifier.isSynthetic( methodInfo.flags() )*/ ) + .filter( methodInfo -> !Modifier.isStatic( methodInfo.flags() ) && !isSynthetic( methodInfo ) ) .map( methodInfo -> toConstrainedExecutable( beanClass, methodInfo ) ); } @@ -78,16 +80,10 @@ public Stream getConstrainedExecutables(ClassInfo classInfo, * @return {@link ConstrainedExecutable} representation of a given method */ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, MethodInfo methodInfo) { - Executable executable = null; - //TODO: this try/catch should be removed once filtering of methods is fixed. - try { - executable = findExecutable( beanClass, methodInfo ); - } - catch (IllegalArgumentException e) { - e.printStackTrace(); - return null; - } + Executable executable = findExecutable( beanClass, methodInfo ); + Stream parameterConstraints = getParameterMetaData( beanClass, executable, methodInfo ); + Set> crossParameterConstraints; Map>> executableConstraints = findMetaConstraints( methodInfo.annotations(), executable ) @@ -271,8 +267,7 @@ private Stream> findMetaConstraints(ParameterInformation param */ private Executable findExecutable(Class beanClass, MethodInfo methodInfo) { try { - if ( "".equals( methodInfo.name() ) ) { - // it means it is a constructor: + if ( isConstructor( methodInfo ) ) { return beanClass.getDeclaredConstructor( methodInfo.parameters().stream() .map( type -> jandexHelper.getClassForName( type.name().toString() ) ) .toArray( size -> new Class[size] ) ); @@ -296,6 +291,16 @@ private Executable findExecutable(Class beanClass, MethodInfo methodInfo) { } } + private boolean isConstructor(MethodInfo methodInfo) { + // currently, Jandex does not expose this in a practical way + return "".equals( methodInfo.name() ); + } + + private boolean isSynthetic(MethodInfo methodInfo) { + // currently, Jandex does not expose this in a practical way + return ( methodInfo.flags() & ( SYNTHETIC | BRIDGE ) ) != 0; + } + /** * Simple POJO that contains {@link Type}, name and index of a method parameter. */ From a97d75ae5ea3ec240fdd3c06952cb8a284e93521 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 23 Dec 2016 17:29:38 +0100 Subject: [PATCH 7/8] HV-644 Major cleanup and fixes --- ...stractConstrainedElementJandexBuilder.java | 309 +++++------------- .../jandex/ClassConstraintsJandexBuilder.java | 25 +- .../jandex/ConstrainedFieldJandexBuilder.java | 46 +-- .../ConstrainedMethodJandexBuilder.java | 91 ++---- .../util/GroupSequenceJandexHelper.java | 97 ------ .../metadata/jandex/util/JandexHelper.java | 63 +--- .../provider/JandexMetaDataProvider.java | 117 ++++++- .../validator/internal/util/logging/Log.java | 11 +- ...t.java => JandexMetaDataProviderTest.java} | 36 +- .../ConstrainedFieldJandexBuilderModel.java | 4 +- 10 files changed, 278 insertions(+), 521 deletions(-) delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java rename engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/{ConstrainedFieldJandexBuilderTest.java => JandexMetaDataProviderTest.java} (72%) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java index 7b0e62fba7..952b193e34 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java @@ -8,8 +8,6 @@ import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; import java.lang.reflect.Member; import java.util.Arrays; import java.util.Collection; @@ -18,7 +16,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import javax.validation.Valid; + import javax.validation.groups.ConvertGroup; import org.hibernate.validator.internal.engine.valuehandling.UnwrapMode; @@ -28,28 +26,29 @@ import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.jandex.util.JandexHelper; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.annotationfactory.AnnotationDescriptor; import org.hibernate.validator.internal.util.annotationfactory.AnnotationFactory; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.valuehandling.UnwrapValidatedValue; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.DotName; import org.jboss.jandex.Type; /** - * Base builder for constrained elements that uses Jandex index. + * Abstract builder exploiting the Jandex index to build {@link ConstrainedElement}s. * * @author Marko Bekhta + * @author Guillaume Smet */ public abstract class AbstractConstrainedElementJandexBuilder { - protected static final Log log = LoggerFactory.make(); + protected static final Log LOG = LoggerFactory.make(); - protected final ConstraintHelper constraintHelper; + private final ConstraintHelper constraintHelper; protected final JandexHelper jandexHelper; @@ -65,16 +64,60 @@ protected AbstractConstrainedElementJandexBuilder(ConstraintHelper constraintHel this.constraintAnnotations = constraintAnnotations; } - /** - * Determine the unwrap mode. - * - * @param type a {@link Type} of an element - * @param annotationInstances a {@link Collection} of annotations present on that element - * @param typeArgumentAnnotated ? - * - * @return {@link UnwrapMode} for a given field - */ - protected UnwrapMode determineUnwrapMode(Type type, Collection annotationInstances, boolean typeArgumentAnnotated) { + protected Stream> findConstraints(Collection annotationInstances, Member member) { + return findConstrainAnnotations( annotationInstances ) + .flatMap( annotationInstance -> findConstraintAnnotations( member, annotationInstance ) ); + } + + protected Stream> findTypeAnnotationConstraintsForMember(MemberInformation information, boolean isCascaded) { + //TODO: Do we need to include Type.Kind.WILDCARD_TYPE ? + if ( !Type.Kind.PARAMETERIZED_TYPE.equals( information.getType().kind() ) ) { + return Stream.empty(); + } + + List arguments = information.getType().asParameterizedType().arguments(); + Optional argument; + if ( arguments.size() == 1 ) { + argument = Optional.of( arguments.get( 0 ) ); + } + else if ( jandexHelper.isMap( information.getType() ) ) { + argument = Optional.of( arguments.get( 1 ) ); + } + else { + argument = Optional.empty(); + } + if ( argument.isPresent() ) { + // HV-925 + // We need to determine the validated type used for constraint validator resolution. + // Iterables and maps need special treatment at this point, since the validated type is the type of the + // specified type parameter. In the other cases the validated type is the parameterized type, eg Optional. + // In the latter case a value unwrapping has to occur + Type validatedType = information.getType(); + if ( jandexHelper.isIterable( validatedType ) || jandexHelper.isMap( validatedType ) ) { + validatedType = argument.get(); + } + Class memberType = jandexHelper.getClassForName( validatedType.name() ); + return findConstrainAnnotations( argument.get().annotations() ) + .flatMap( annotationInstance -> findConstraintAnnotations( information.getMember(), annotationInstance ) ) + .map( constraintDescriptor -> createTypeArgumentMetaConstraint( information.getConstraintLocation(), constraintDescriptor, memberType ) ); + } + + return Stream.empty(); + } + + protected CommonConstraintInformation findCommonConstraintInformation(Type type, Collection annotationInstances, + boolean typeArgumentAnnotated, boolean isCascading) { + return new CommonConstraintInformation( + getGroupConversions( + jandexHelper.findAnnotation( annotationInstances, ConvertGroup.class ), + jandexHelper.findAnnotation( annotationInstances, ConvertGroup.List.class ) + ), + isCascading, + determineUnwrapMode( type, annotationInstances, typeArgumentAnnotated ) + ); + } + + private UnwrapMode determineUnwrapMode(Type type, Collection annotationInstances, boolean typeArgumentAnnotated) { boolean indexable = jandexHelper.isIndexable( type ); Optional unwrapValidatedValue = jandexHelper.findAnnotation( annotationInstances, UnwrapValidatedValue.class ); @@ -87,15 +130,7 @@ else if ( unwrapValidatedValue.isPresent() ) { return UnwrapMode.AUTOMATIC; } - /** - * Builds a map of group conversions based on given {@link ConvertGroup} and {@link ConvertGroup.List} parameters. - * - * @param convertGroup an optional for {@link ConvertGroup} - * @param convertGroupList an optional for {@link ConvertGroup.List} - * - * @return a {@link Map} containing group conversions - */ - protected Map, Class> getGroupConversions(Optional convertGroup, Optional convertGroupList) { + private Map, Class> getGroupConversions(Optional convertGroup, Optional convertGroupList) { Map, Class> groupConversionMap = CollectionHelper.newHashMap(); convertGroup.ifPresent( annotation -> addToConversionGroup( groupConversionMap, annotation ) ); @@ -109,19 +144,13 @@ protected Map, Class> getGroupConversions(Optional, Class> groupConversionMap, AnnotationInstance annotation) { - Class from = jandexHelper.getClassForName( annotation.value( "from" ).asClass().name().toString() ); + Class from = jandexHelper.getClassForName( annotation.value( "from" ).asClass().name() ); groupConversionMap.merge( from, - jandexHelper.getClassForName( annotation.value( "to" ).asClass().name().toString() ), + jandexHelper.getClassForName( annotation.value( "to" ).asClass().name() ), ( val1, val2 ) -> { - throw log.getMultipleGroupConversionsForSameSourceException( + throw LOG.getMultipleGroupConversionsForSameSourceException( from, CollectionHelper.asSet( val1, val2 ) ); @@ -129,43 +158,13 @@ private void addToConversionGroup(Map, Class> groupConversionMap, An ); } - /** - * Finds and converts constraint annotations to {@link ConstraintDescriptorImpl} - * - * @param annotationInstances a collection of annotation instances - * @param member a {@link Member} under investigation - * - * @return a stream of {@link MetaConstraint}s based on input parameters - */ - protected Stream> findConstraints(Collection annotationInstances, Member member) { - return findConstrainAnnotations( annotationInstances ) - .flatMap( annotationInstance -> findConstraintAnnotations( member, annotationInstance ) ); - } - - /** - * Examines the given annotation to see whether it is a single- or multi-valued constraint annotation. - * - * @param member The member to check for constraints annotations - * @param annotationInstance The annotation to examine - * - * @return A stream of constraint descriptors or the empty stream in case given {@code annotationInstance} is neither a - * single nor multi-valued annotation. - */ - protected Stream> findConstraintAnnotations(Member member, AnnotationInstance annotationInstance) { + private Stream> findConstraintAnnotations(Member member, AnnotationInstance annotationInstance) { return instanceToAnnotations( annotationInstance ).map( constraint -> buildConstraintDescriptor( member, constraint, ElementType.FIELD ) ); } - /** - * Converts a given annotation instance of ({@link AnnotationInstance}) to a stream of {@link Annotation}s - * - * @param annotationInstance instance to convert - * - * @return a stream that contains {@link Annotation}s based on given instance. If given instance is a simple constraint annotation - * it will contain it. If given instance is a multivalued constraint - it will be unwrapped and stream will contain annotations - * from that multivalued one. - */ - protected Stream instanceToAnnotations(AnnotationInstance annotationInstance) { - Class annotationClass = (Class) jandexHelper.getClassForName( annotationInstance.name().toString() ); + @SuppressWarnings("unchecked") + private Stream instanceToAnnotations(AnnotationInstance annotationInstance) { + Class annotationClass = (Class) jandexHelper.getClassForName( annotationInstance.name() ); if ( constraintHelper.isMultiValueConstraint( annotationClass ) ) { return Arrays.stream( (AnnotationValue[]) annotationInstance.value().value() ) .map( annotationValue -> instanceToAnnotation( annotationValue.asNested() ) ); @@ -178,27 +177,12 @@ else if ( constraintHelper.isConstraintAnnotation( annotationClass ) ) { } } - /** - * Converts given annotation instance of ({@link AnnotationInstance}) type to its {@link Annotation} representation. - * - * @param annotationInstance annotation instance to convert - * - * @return an annotation based on input parameters - */ - protected A instanceToAnnotation(AnnotationInstance annotationInstance) { - return instanceToAnnotation( (Class) jandexHelper.getClassForName( annotationInstance.name() - .toString() ), annotationInstance ); + @SuppressWarnings("unchecked") + private A instanceToAnnotation(AnnotationInstance annotationInstance) { + return instanceToAnnotation( (Class) jandexHelper.getClassForName( annotationInstance.name() ), annotationInstance ); } - /** - * Converts given annotation instance of ({@link AnnotationInstance}) type to its {@link Annotation} representation. - * - * @param annotationClass a class of annotation to convert to - * @param annotationInstance annotation instance to convert - * - * @return an annotation based on input parameters - */ - protected A instanceToAnnotation(Class annotationClass, AnnotationInstance annotationInstance) { + private A instanceToAnnotation(Class annotationClass, AnnotationInstance annotationInstance) { AnnotationDescriptor annotationDescriptor = new AnnotationDescriptor<>( annotationClass ); annotationInstance.values().stream() @@ -209,159 +193,20 @@ protected A instanceToAnnotation(Class annotationClass annotation = AnnotationFactory.create( annotationDescriptor ); } catch (RuntimeException e) { - throw log.getUnableToCreateAnnotationForConfiguredConstraintException( e ); + throw LOG.getUnableToCreateAnnotationForConfiguredConstraintException( e ); } return annotation; } - /** - * Finds all constrain annotations in given collection. - * - * @param allAnnotations collection to look for constraints in - * - * @return a {@link Stream} of the constraint annotations - */ - protected Stream findConstrainAnnotations(Collection allAnnotations) { + private Stream findConstrainAnnotations(Collection allAnnotations) { return allAnnotations.stream().filter( this::isConstraintAnnotation ); } - /** - * Checks if given annotation is a constraint or not. - * - * @param annotationInstance an annotation to check - * - * @return {@code true} if given annotation is a constraint, {@code false} otherwise - */ - protected boolean isConstraintAnnotation(AnnotationInstance annotationInstance) { + private boolean isConstraintAnnotation(AnnotationInstance annotationInstance) { return constraintAnnotations.contains( annotationInstance.name() ); } - /** - * Provides a {@link Stream} of type constraints based on given member information. - * - * @param information information about member of interest - * @param isCascaded if member is marked with {@link Valid} - * - * @return a stream of {@link MetaConstraint}s - */ - protected Stream> findTypeAnnotationConstraintsForMember(MemberInformation information, boolean isCascaded) { - //TODO: Do we need to include Type.Kind.WILDCARD_TYPE ? - if ( !Type.Kind.PARAMETERIZED_TYPE.equals( information.getType().kind() ) ) { - return Stream.empty(); - } - - List arguments = information.getType().asParameterizedType().arguments(); - Optional argument; - if ( arguments.size() == 1 ) { - argument = Optional.of( arguments.get( 0 ) ); - } - else if ( jandexHelper.isMap( information.getType() ) ) { - argument = Optional.of( arguments.get( 1 ) ); - } - else { - argument = Optional.empty(); - } - if ( argument.isPresent() ) { - // HV-925 - // We need to determine the validated type used for constraint validator resolution. - // Iterables and maps need special treatment at this point, since the validated type is the type of the - // specified type parameter. In the other cases the validated type is the parameterized type, eg Optional. - // In the latter case a value unwrapping has to occur - Type validatedType = information.getType(); - if ( jandexHelper.isIterable( validatedType ) || jandexHelper.isMap( validatedType ) ) { - validatedType = argument.get(); - } - Class memberType = jandexHelper.getClassForName( validatedType.name().toString() ); - return findConstrainAnnotations( argument.get().annotations() ) - .flatMap( annotationInstance -> findConstraintAnnotations( information.getMember(), annotationInstance ) ) - .map( constraintDescriptor -> createTypeArgumentMetaConstraint( information.getConstraintLocation(), constraintDescriptor, memberType ) ); - } - - return Stream.empty(); - } - - /** - * Collects common constraint information for given parameters. - * - * @param type a type of an element under investigation - * @param annotationInstances a collection of {@link AnnotationInstance}s on an element under investigation - * @param typeArgumentAnnotated is type argument annotated - * @param isCascading is an element marked with {@link Valid} ? - * - * @return a {@link CommonConstraintInformation} instance containing common constraint information - */ - protected CommonConstraintInformation findCommonConstraintInformation(Type type, Collection annotationInstances, - boolean typeArgumentAnnotated, boolean isCascading) { - return new CommonConstraintInformation( - getGroupConversions( - jandexHelper.findAnnotation( annotationInstances, ConvertGroup.class ), - jandexHelper.findAnnotation( annotationInstances, ConvertGroup.List.class ) - ), - isCascading, - determineUnwrapMode( type, annotationInstances, typeArgumentAnnotated ) - ); - } - - /** - * Creates {@link MetaConstraint} based on given {@link ConstraintDescriptorImpl}. - * - * @param declaringClass a class on which constraints are defined - * @param descriptor a descriptor to convert - * - * @return {@link MetaConstraint} created based on given parameters - */ - protected MetaConstraint createMetaConstraint(Class declaringClass, ConstraintDescriptorImpl descriptor) { - return new MetaConstraint<>( descriptor, ConstraintLocation.forClass( declaringClass ) ); - } - - /** - * Creates {@link MetaConstraint} based on given {@link ConstraintDescriptorImpl}. - * - * @param member a field on which constraints are defined - * @param descriptor a descriptor to convert - * - * @return {@link MetaConstraint} created based on given parameters - */ - protected MetaConstraint createMetaConstraint(Field member, ConstraintDescriptorImpl descriptor) { - return new MetaConstraint<>( descriptor, ConstraintLocation.forProperty( member ) ); - } - - /** - * Creates {@link MetaConstraint} based on given {@link ConstraintDescriptorImpl}. - * - * @param member an executable on which constraints are defined (for return value) - * @param descriptor a descriptor to convert - * - * @return {@link MetaConstraint} created based on given parameters - */ - protected MetaConstraint createMetaConstraint(Executable member, ConstraintDescriptorImpl descriptor) { - return new MetaConstraint<>( - descriptor, - ConstraintDescriptorImpl.ConstraintType.GENERIC.equals( descriptor.getConstraintType() ) ? - ConstraintLocation.forReturnValue( member ) : ConstraintLocation.forCrossParameter( member ) - ); - } - - /** - * Creates {@link MetaConstraint} based on given {@link ConstraintDescriptorImpl}. - * - * @param member an executable of iterest - * @param parameterIndex index of executable parameter on which constraints are defined - * @param descriptor a descriptor to convert - * - * @return {@link MetaConstraint} created based on given parameters - */ - protected MetaConstraint createMetaConstraint(Executable member, int parameterIndex, ConstraintDescriptorImpl descriptor) { - return new MetaConstraint<>( - descriptor, - ConstraintLocation.forParameter( member, parameterIndex ) - ); - } - - /** - * Creates a {@link MetaConstraint} for a type argument constraint. - */ - protected MetaConstraint createTypeArgumentMetaConstraint( + private MetaConstraint createTypeArgumentMetaConstraint( ConstraintLocation location, ConstraintDescriptorImpl descriptor, java.lang.reflect.Type type @@ -370,7 +215,7 @@ protected MetaConstraint createTypeArgumentMetaConstra return new MetaConstraint<>( descriptor, ConstraintLocation.forTypeArgument( location, null, type ) ); } - protected ConstraintDescriptorImpl buildConstraintDescriptor( + private ConstraintDescriptorImpl buildConstraintDescriptor( Member member, A annotation, ElementType type) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ClassConstraintsJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ClassConstraintsJandexBuilder.java index 0cf24b5d18..4698150fa6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ClassConstraintsJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ClassConstraintsJandexBuilder.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.internal.metadata.jandex; +import java.lang.annotation.Annotation; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -16,6 +17,7 @@ import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.jandex.util.JandexHelper; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; @@ -24,9 +26,10 @@ import org.jboss.jandex.DotName; /** - * Builder for class level constrains that uses Jandex index. + * Builder used to extract class constraints from the Jandex index. * * @author Marko Bekhta + * @author Guillaume Smet */ public class ClassConstraintsJandexBuilder extends AbstractConstrainedElementJandexBuilder { @@ -35,14 +38,6 @@ public ClassConstraintsJandexBuilder(ConstraintHelper constraintHelper, JandexHe super( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ); } - /** - * Gets {@link ConstrainedType}s from a given class. - * - * @param classInfo a class in which to look for class level constraints - * @param beanClass same class as {@code classInfo} but represented as {@link Class} - * - * @return a stream of {@link ConstrainedElement}s that represent class type - */ public Stream getClassConstraints(ClassInfo classInfo, Class beanClass) { if ( annotationProcessingOptions.areClassLevelConstraintsIgnoredFor( beanClass ) ) { return Stream.empty(); @@ -55,17 +50,13 @@ public Stream getClassConstraints(ClassInfo classInfo, Class ) ); } - /** - * Converts {@link ConstraintDescriptorImpl} to {@link MetaConstraint}. - * - * @param annotationInstances collection of annotations declared on a class - * @param beanClass a class under investigation - * - * @return a stream of {@link MetaConstraint}s for a given class describing class level constraints - */ private Stream> findMetaConstraints(Collection annotationInstances, Class beanClass) { return findConstraints( annotationInstances, null ) .map( descriptor -> createMetaConstraint( beanClass, descriptor ) ); } + private MetaConstraint createMetaConstraint(Class declaringClass, ConstraintDescriptorImpl descriptor) { + return new MetaConstraint<>( descriptor, ConstraintLocation.forClass( declaringClass ) ); + } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java index 534a747cec..9de110c5d1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.internal.metadata.jandex; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; @@ -29,9 +30,10 @@ import org.jboss.jandex.FieldInfo; /** - * Builder for constrained fields that uses Jandex index. + * Builder used to extract field constraints from the Jandex index. * * @author Marko Bekhta + * @author Guillaume Smet */ public class ConstrainedFieldJandexBuilder extends AbstractConstrainedElementJandexBuilder { @@ -40,27 +42,11 @@ public ConstrainedFieldJandexBuilder(ConstraintHelper constraintHelper, JandexHe super( constraintHelper, jandexHelper, annotationProcessingOptions, constraintAnnotations ); } - /** - * Gets {@link ConstrainedField}s from a given class. - * - * @param classInfo a class in which to look for constrained fields - * @param beanClass same class as {@code classInfo} but represented as {@link Class} - * - * @return a stream of {@link ConstrainedElement}s that represents fields - */ public Stream getConstrainedFields(ClassInfo classInfo, Class beanClass) { return classInfo.fields().stream() .map( fieldInfo -> toConstrainedField( beanClass, fieldInfo ) ); } - /** - * Converts given field to {@link ConstrainedField}. - * - * @param beanClass a {@link Class} where {@code fieldInfo} is located - * @param fieldInfo a field to convert - * - * @return {@link ConstrainedField} representation of a given field - */ private ConstrainedField toConstrainedField(Class beanClass, FieldInfo fieldInfo) { Field field = findField( beanClass, fieldInfo ); Set> constraints = findMetaConstraints( fieldInfo.annotations(), field ).collect( Collectors.toSet() ); @@ -95,39 +81,21 @@ private ConstrainedField toConstrainedField(Class beanClass, FieldInfo fieldI ); } - /** - * Converts {@link ConstraintDescriptorImpl} to {@link MetaConstraint}. - * - * @param annotationInstances collection of annotations declared on a field - * @param field a field under investigation - * - * @return a stream of {@link MetaConstraint}s for a given field - */ private Stream> findMetaConstraints(Collection annotationInstances, Field field) { return findConstraints( annotationInstances, field ) .map( descriptor -> createMetaConstraint( field, descriptor ) ); } - /** - * Find a {@link Field} by given bean class and field information. - * - * @param beanClass a bean class in which to look for the field - * @param fieldInfo {@link FieldInfo} representing information about the field - * - * @return a {@link Field} for the given information - * - * @throws IllegalArgumentException if no filed was found for a given bean class and field information - */ private Field findField(Class beanClass, FieldInfo fieldInfo) { try { return beanClass.getDeclaredField( fieldInfo.name() ); } catch (NoSuchFieldException e) { - throw new IllegalArgumentException( - String.format( "Wasn't able to find a filed for a given parameters. Field name - %s in bean - %s", fieldInfo.name(), beanClass.getName() ), - e - ); + throw LOG.getUnableToFindFieldReferencedInJandexIndex( beanClass, fieldInfo.name().toString(), e ); } } + private MetaConstraint createMetaConstraint(Field member, ConstraintDescriptorImpl descriptor) { + return new MetaConstraint<>( descriptor, ConstraintLocation.forProperty( member ) ); + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java index ef79f4d6da..6d488a88b2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.internal.metadata.jandex; +import java.lang.annotation.Annotation; import java.lang.reflect.Executable; import java.lang.reflect.Modifier; import java.util.Collection; @@ -35,7 +36,7 @@ import org.jboss.jandex.Type; /** - * Builder for constrained methods that uses Jandex index. + * Builder used to extract builder and method constraints from the Jandex index. * * @author Marko Bekhta * @author Guillaume Smet @@ -54,14 +55,6 @@ public ConstrainedMethodJandexBuilder(ConstraintHelper constraintHelper, JandexH this.parameterNameProvider = parameterNameProvider; } - /** - * Gets {@link ConstrainedExecutable}s from a given class. - * - * @param classInfo a class in which to look for constrained fields - * @param beanClass same class as {@code classInfo} but represented as {@link Class} - * - * @return a stream of {@link ConstrainedElement}s that represents fields - */ public Stream getConstrainedExecutables(ClassInfo classInfo, Class beanClass) { // HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints // anyway and possibly hide the actual method with the same signature in the built meta model @@ -71,14 +64,6 @@ public Stream getConstrainedExecutables(ClassInfo classInfo, .map( methodInfo -> toConstrainedExecutable( beanClass, methodInfo ) ); } - /** - * Converts given method to {@link ConstrainedExecutable}. - * - * @param beanClass a {@link Class} where {@code methodInfo} is located - * @param methodInfo a method to convert - * - * @return {@link ConstrainedExecutable} representation of a given method - */ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, MethodInfo methodInfo) { Executable executable = findExecutable( beanClass, methodInfo ); @@ -146,15 +131,6 @@ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, Method ); } - /** - * Provides a stream of parameter constraints for a given method. - * - * @param beanClass a hosting class of a given method - * @param executable an executable member that represents {@code methodInfo} - * @param methodInfo method to retrieve parameters from - * - * @return a {@link Stream} of {@link ConstrainedParameter} for a given method - */ private Stream getParameterMetaData(Class beanClass, Executable executable, MethodInfo methodInfo) { if ( methodInfo.parameters().isEmpty() ) { return Stream.empty(); @@ -172,19 +148,11 @@ private Stream getParameterMetaData(Class beanClass, Ex .map( parameterInformation -> toConstrainedParameter( parameterInformation, executable ) ); } - /** - * Converts given parameter information to {@link ConstrainedParameter}. - * - * @param parameterInformation {@link ParameterInformation} containing parameter information - * @param executable represents a method of interest - * - * @return an instance of {@link ConstrainedParameter} based on input parameters - */ private ConstrainedParameter toConstrainedParameter(ParameterInformation parameterInformation, Executable executable) { CommonConstraintInformation commonInformation; Stream> parameterConstraints; Set> typeArgumentsConstraints; - Class parameterType = jandexHelper.getClassForName( parameterInformation.getType().name().toString() ); + Class parameterType = jandexHelper.getClassForName( parameterInformation.getType().name() ); if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( executable, parameterInformation.getIndex() ) ) { parameterConstraints = Stream.empty(); @@ -229,68 +197,53 @@ private ConstrainedParameter toConstrainedParameter(ParameterInformation paramet ); } - /** - * Converts {@link ConstraintDescriptorImpl} to {@link MetaConstraint}. - * - * @param annotationInstances collection of annotations declared on an executable - * @param executable an executable under investigation - * - * @return a stream of {@link MetaConstraint}s for a given executable - */ private Stream> findMetaConstraints(Collection annotationInstances, Executable executable) { return findConstraints( annotationInstances, executable ) .map( descriptor -> createMetaConstraint( executable, descriptor ) ); } - /** - * Converts {@link ConstraintDescriptorImpl} to {@link MetaConstraint}. - * - * @param parameterInformation parameter information - * @param executable an executable under investigation - * - * @return a stream of {@link MetaConstraint}s for a given parameter executable - */ private Stream> findMetaConstraints(ParameterInformation parameterInformation, Executable executable) { return findConstraints( parameterInformation.getType().annotations(), executable ) .map( descriptor -> createMetaConstraint( executable, parameterInformation.getIndex(), descriptor ) ); } - /** - * Find an {@link Executable} by given bean class and method information. - * - * @param beanClass a bean class in which to look for the executable - * @param methodInfo {@link MethodInfo} representing information about the executable - * - * @return a {@link Executable} for the given information - * - * @throws IllegalArgumentException if no executable was found for a given bean class and method information - */ private Executable findExecutable(Class beanClass, MethodInfo methodInfo) { try { if ( isConstructor( methodInfo ) ) { return beanClass.getDeclaredConstructor( methodInfo.parameters().stream() - .map( type -> jandexHelper.getClassForName( type.name().toString() ) ) + .map( type -> jandexHelper.getClassForName( type.name() ) ) .toArray( size -> new Class[size] ) ); } else { return beanClass.getDeclaredMethod( methodInfo.name(), methodInfo.parameters().stream() - .map( type -> jandexHelper.getClassForName( type.name().toString() ) ) + .map( type -> jandexHelper.getClassForName( type.name() ) ) .toArray( size -> new Class[size] ) ); } } catch (NoSuchMethodException e) { - throw new IllegalArgumentException( - String.format( "Wasn't able to find a executable for a given parameters. Executable name - %s in bean - %s", methodInfo.name(), - beanClass.getName() - ), - e - ); + // TODO add the parameter information to the log. It would probably be nice if Jandex could expose a method for that. + throw LOG.getUnableToFindMethodReferencedInJandexIndex( beanClass, methodInfo.name(), e ); } } + private MetaConstraint createMetaConstraint(Executable member, ConstraintDescriptorImpl descriptor) { + return new MetaConstraint<>( + descriptor, + ConstraintDescriptorImpl.ConstraintType.GENERIC.equals( descriptor.getConstraintType() ) ? + ConstraintLocation.forReturnValue( member ) : ConstraintLocation.forCrossParameter( member ) + ); + } + + private MetaConstraint createMetaConstraint(Executable member, int parameterIndex, ConstraintDescriptorImpl descriptor) { + return new MetaConstraint<>( + descriptor, + ConstraintLocation.forParameter( member, parameterIndex ) + ); + } + private boolean isConstructor(MethodInfo methodInfo) { // currently, Jandex does not expose this in a practical way return "".equals( methodInfo.name() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java deleted file mode 100644 index 691742ee0d..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/GroupSequenceJandexHelper.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.metadata.jandex.util; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Optional; -import java.util.stream.Stream; - -import javax.validation.GroupSequence; - -import org.hibernate.validator.group.GroupSequenceProvider; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetMethods; -import org.hibernate.validator.internal.util.privilegedactions.NewInstance; -import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; -import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.ClassInfo; - -/** - * Helper class for working with {@link GroupSequence} and {@link GroupSequenceProvider}. - * - * @author Marko Bekhta - */ -public class GroupSequenceJandexHelper { - - protected static final Log log = LoggerFactory.make(); - - private final JandexHelper jandexHelper; - - private GroupSequenceJandexHelper(JandexHelper jandexHelper) { - this.jandexHelper = jandexHelper; - } - - /** - * Creates an instance of a {@link GroupSequenceJandexHelper}. - * - * @param jandexHelper an instance of {@link JandexHelper} - * - * @return a new instance of {@link GroupSequenceJandexHelper} - */ - public static GroupSequenceJandexHelper getInstance(JandexHelper jandexHelper) { - return new GroupSequenceJandexHelper( jandexHelper ); - } - - /** - * Finds a group sequence if present. - * - * @param classInfo class to look for {@link GroupSequence} on. - * - * @return an empty stream if there is no {@link GroupSequence} present, a stream of classes from {@link GroupSequence}'s {@code value} otherwise - */ - public Stream> getGroupSequence(ClassInfo classInfo) { - Optional groupSequence = jandexHelper.findAnnotation( classInfo.classAnnotations(), GroupSequence.class ); - if ( groupSequence.isPresent() ) { - return Arrays.stream( (Class[]) jandexHelper.convertAnnotationValue( groupSequence.get().value() ) ); - } - else { - return Stream.empty(); - } - } - - /** - * Finds a group sequence provider if present. - * - * @param classInfo class to look for {@link GroupSequenceProvider} on. - * - * @return an empty stream if there is no {@link GroupSequence} present, a stream of classes from {@link GroupSequence}'s {@code value} otherwise - */ - public DefaultGroupSequenceProvider getGroupSequenceProvider(ClassInfo classInfo) { - Optional groupSequenceProvider = jandexHelper.findAnnotation( classInfo.classAnnotations(), GroupSequenceProvider.class ); - if ( groupSequenceProvider.isPresent() ) { - Class> providerClass = - (Class>) jandexHelper.convertAnnotationValue( groupSequenceProvider.get().value() ); - return newGroupSequenceProviderClassInstance( jandexHelper.getClassForName( classInfo.name().toString() ), providerClass ); - } - return null; - } - - // TODO: this method is copied over from AnnotationMetaDataProvider. Is there anything to be done about it ? - private DefaultGroupSequenceProvider newGroupSequenceProviderClassInstance(Class beanClass, Class> providerClass) { - Method[] providerMethods = jandexHelper.run( GetMethods.action( providerClass ) ); - for ( Method method : providerMethods ) { - Class[] paramTypes = method.getParameterTypes(); - if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge() - && paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) { - return jandexHelper.run( NewInstance.action( providerClass, "the default group sequence provider" ) ); - } - } - throw log.getWrongDefaultGroupSequenceProviderTypeException( beanClass ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java index 252a884bff..57b1dd9703 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/util/JandexHelper.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.metadata.jandex.util; -import java.lang.annotation.Annotation; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; @@ -19,55 +18,46 @@ import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.annotationfactory.AnnotationDescriptor; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.DotName; import org.jboss.jandex.Type; /** * Utility methods used for Jandex metadata retrieval. * * @author Marko Bekhta + * @author Guillaume Smet */ public final class JandexHelper { - private static final Log log = LoggerFactory.make(); + private static final Log LOG = LoggerFactory.make(); private final ClassLoader classLoader; - private JandexHelper(ClassLoader classLoader) { + public JandexHelper(ClassLoader classLoader) { this.classLoader = classLoader; } - /** - * @return an instance of {@link JandexHelper} - */ - public static JandexHelper getInstance(ClassLoader classLoader) { - return new JandexHelper( classLoader ); - } - - public static JandexHelper getInstance() { - return getInstance( HibernateValidator.class.getClassLoader() ); + public JandexHelper() { + this( HibernateValidator.class.getClassLoader() ); } /** - * Finds a class ({@link Class}) for a given name. - * - * @param className a name of a class to find - * - * @return a found {@link Class} + * Returns a class ({@link Class}) for a given class {@link DotName}. */ - public Class getClassForName(String className) { + public Class getClassForName(DotName className) { // TODO: change how class are loaded (from GM: Using this one without passing a classloader may give us trouble due to classes not being visible to // the implicitly used loader. You can check out org.hibernate.validator.internal.util.privilegedactions.LoadClass and its usage as a starting point. try { - return run( LoadClass.action( className, classLoader, true ) ); + return run( LoadClass.action( className.toString(), classLoader, true ) ); } catch (ValidationException e) { - throw log.getFindingClassReflectionJandexIndexException( className, e ); + throw LOG.getUnableToFindClassReferencedInJandexIndex( className.toString(), e ); } } @@ -75,46 +65,37 @@ public Class getClassForName(String className) { * Indicates if the type is considered indexable (ie is an {@link Iterable}, an array or a {@link Map}). * * @param type the type to inspect. - * * @return Returns {@code true} if the type is indexable. */ public boolean isIndexable(Type type) { // TODO: Can this property be somehow determined from a type parameter and without converting it to a class ??? - return ReflectionHelper.isIndexable( getClassForName( type.name().toString() ) ); + return ReflectionHelper.isIndexable( getClassForName( type.name() ) ); } /** * Checks if given type is a {@link Map} implementation. * * @param type a type to check - * * @return {@code true} if given type is an implementation of a {@link Map}, {@code false} otherwise */ public boolean isMap(Type type) { // TODO: Can this property be somehow determined from a type parameter and without converting it to a class ??? - return ReflectionHelper.isMap( getClassForName( type.name().toString() ) ); + return ReflectionHelper.isMap( getClassForName( type.name() ) ); } /** * Checks if given type is an {@link Iterable} implementation. * * @param type a type to check - * * @return {@code true} if given type is an implementation of an {@link Iterable}, {@code false} otherwise */ public boolean isIterable(Type type) { // TODO: Can this property be somehow determined from a type parameter and without converting it to a class ??? - return ReflectionHelper.isIterable( getClassForName( type.name().toString() ) ); + return ReflectionHelper.isIterable( getClassForName( type.name() ) ); } /** * Finds an annotation of a given type inside the provided collection of annotations. - * - * @param annotations a collection of annotation in which to look for a provided annotation type - * @param clazz a type of annotation to look for - * @return an {@code Optional} which will contain a found annotation, the {@code Optional} being - * empty if none was found. Also if there are more than one annotation of the provided type present in the - * collection there's no guarantee which one will be returned. */ public Optional findAnnotation(Collection annotations, Class clazz) { return annotations.stream() @@ -123,32 +104,24 @@ public Optional findAnnotation(Collection getClassForName( type.name().toString() ) ) + .map( type -> getClassForName( type.name() ) ) .toArray( size -> new Class[size] ); } } else if ( AnnotationValue.Kind.CLASS.equals( annotationValue.kind() ) ) { - return getClassForName( annotationValue.asClass().name().toString() ); + return getClassForName( annotationValue.asClass().name() ); } return annotationValue.value(); } /** * Checks if there's a {@link Valid} annotation present in the collection. - * - * @param annotations a collection of {@link AnnotationInstance}s to check in - * - * @return {@code true} if {@link Valid} is present in collection, {@code false} otherwise */ public boolean isCascading(Collection annotations) { return findAnnotation( annotations, Valid.class ).isPresent(); @@ -160,7 +133,7 @@ public boolean isCascading(Collection annotations) { * NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary * privileged actions within HV's protection domain. */ - static T run(PrivilegedAction action) { + private static T run(PrivilegedAction action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java index c3228a5a9d..1a89071199 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java @@ -8,20 +8,28 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.validation.Constraint; +import javax.validation.GroupSequence; +import org.hibernate.validator.group.GroupSequenceProvider; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.jandex.ClassConstraintsJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.ConstrainedFieldJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.ConstrainedMethodJandexBuilder; -import org.hibernate.validator.internal.metadata.jandex.util.GroupSequenceJandexHelper; import org.hibernate.validator.internal.metadata.jandex.util.JandexHelper; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; @@ -29,23 +37,34 @@ import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetMethods; +import org.hibernate.validator.internal.util.privilegedactions.NewInstance; +import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationTarget.Kind; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; /** + * {@code MetaDataProvider} which reads the metadata from a Jandex index. + * * @author Marko Bekhta * @author Guillaume Smet */ public class JandexMetaDataProvider implements MetaDataProvider { - private static final DotName CONSTRAINT_ANNOTATION = DotName.createSimple( Constraint.class.getName() ); + private static final Log LOG = LoggerFactory.make(); - private final Map> configuredBeans; + private static final DotName CONSTRAINT_ANNOTATION = DotName.createSimple( Constraint.class.getName() ); private final AnnotationProcessingOptions annotationProcessingOptions; + private final Map> configuredBeans; + public JandexMetaDataProvider( ConstraintHelper constraintHelper, JandexHelper jandexHelper, @@ -71,7 +90,7 @@ private static List extractConstraintAnnotations(IndexView indexView) { private static Map> extractConfiguredBeans(IndexView indexView, ConstraintHelper constraintHelper, JandexHelper jandexHelper, AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider, List constraintAnnotations) { - return indexView.getKnownClasses().stream() + return findConstrainedBeans( indexView, constraintAnnotations ).stream() .collect( Collectors.toMap( classInfo -> classInfo.name(), classInfo -> getBeanConfiguration( @@ -85,18 +104,53 @@ private static Map> extractConfiguredBeans(IndexVi ) ); } + private static Set findConstrainedBeans(IndexView indexView, List constraintAnnotations) { + // TODO HV-644: not sure this is totally accurate. I'm especially wondering how it will behave with subclasses and such. + Set constrainedBeans = new HashSet<>(); + for ( DotName constraintAnnotation : constraintAnnotations ) { + constrainedBeans.addAll( indexView.getAnnotations( constraintAnnotation ).stream() + .map( ai -> ai.target() ) + .map( JandexMetaDataProvider::annotationInstanceToClassInfo ) + .collect( Collectors.toSet() ) ); + } + return constrainedBeans; + } + + private static ClassInfo annotationInstanceToClassInfo(AnnotationTarget target) { + ClassInfo classInfo; + switch ( target.kind() ) { + case CLASS: + classInfo = target.asClass(); + break; + case FIELD: + classInfo = target.asField().declaringClass(); + break; + case METHOD: + classInfo = target.asMethod().declaringClass(); + break; + case METHOD_PARAMETER: + classInfo = target.asMethodParameter().method().declaringClass(); + break; + case TYPE: + classInfo = annotationInstanceToClassInfo( target.asType().enclosingTarget() ); + break; + default: + throw new IllegalStateException( target.kind() + " is not supported here." ); + } + return classInfo; + } + private static BeanConfiguration getBeanConfiguration(ConstraintHelper constraintHelper, JandexHelper jandexHelper, ClassInfo classInfo, AnnotationProcessingOptions annotationProcessingOptions, ExecutableParameterNameProvider parameterNameProvider, List constraintAnnotations) { - GroupSequenceJandexHelper groupSequenceJandexHelper = GroupSequenceJandexHelper.getInstance( jandexHelper ); - Class bean = jandexHelper.getClassForName( classInfo.name().toString() ); + Class bean = jandexHelper.getClassForName( classInfo.name() ); return new BeanConfiguration<>( ConfigurationSource.JANDEX, bean, getConstrainedElements( constraintHelper, jandexHelper, classInfo, bean, annotationProcessingOptions, parameterNameProvider, constraintAnnotations ) .collect( Collectors.toSet() ), - groupSequenceJandexHelper.getGroupSequence( classInfo ).collect( Collectors.toList() ), - groupSequenceJandexHelper.getGroupSequenceProvider( classInfo ) + getGroupSequence( jandexHelper, classInfo ).collect( Collectors.toList() ), + getGroupSequenceProvider( jandexHelper, classInfo ) ); } @@ -117,7 +171,7 @@ private static Stream getConstrainedElements( constraintAnnotations ).getConstrainedFields( classInfo, bean ); - //get constrained methods/constructors + // get constrained methods/constructors Stream constrainedMethodStream = new ConstrainedMethodJandexBuilder( constraintHelper, jandexHelper, @@ -163,4 +217,49 @@ protected BeanConfiguration getBeanConfiguration(Class beanClass) { Contracts.assertNotNull( beanClass ); return (BeanConfiguration) configuredBeans.get( DotName.createSimple( beanClass.getName() ) ); } + + private static Stream> getGroupSequence(JandexHelper jandexHelper, ClassInfo classInfo) { + Optional groupSequence = jandexHelper.findAnnotation( classInfo.classAnnotations(), GroupSequence.class ); + if ( groupSequence.isPresent() ) { + return Arrays.stream( (Class[]) jandexHelper.convertAnnotationValue( groupSequence.get().value() ) ); + } + else { + return Stream.empty(); + } + } + + @SuppressWarnings("unchecked") + private static DefaultGroupSequenceProvider getGroupSequenceProvider(JandexHelper jandexHelper, ClassInfo classInfo) { + Optional groupSequenceProvider = jandexHelper.findAnnotation( classInfo.classAnnotations(), GroupSequenceProvider.class ); + if ( groupSequenceProvider.isPresent() ) { + Class> providerClass = + (Class>) jandexHelper.convertAnnotationValue( groupSequenceProvider.get().value() ); + return newGroupSequenceProviderClassInstance( jandexHelper.getClassForName( classInfo.name() ), providerClass ); + } + return null; + } + + // TODO: this method is copied over from AnnotationMetaDataProvider. Is there anything to be done about it ? + private static DefaultGroupSequenceProvider newGroupSequenceProviderClassInstance(Class beanClass, Class> providerClass) { + Method[] providerMethods = run( GetMethods.action( providerClass ) ); + for ( Method method : providerMethods ) { + Class[] paramTypes = method.getParameterTypes(); + if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge() + && paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) { + return run( NewInstance.action( providerClass, "the default group sequence provider" ) ); + } + } + throw LOG.getWrongDefaultGroupSequenceProviderTypeException( beanClass ); + } + + /** + * Runs the given privileged action, using a privileged block if required. + *

+ * NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary + * privileged actions within HV's protection domain. + */ + private static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index 7af2ed2240..96ab71f338 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -696,6 +696,13 @@ ConstraintDeclarationException getInconsistentValueUnwrappingConfigurationBetwee @Message(id = 203, value = "Error parsing Jandex index file.") ValidationException getParsingJandexIndexException(@Cause Exception e); - @Message(id = 204, value = "Unable to find class for a name: '%s'.") - ValidationException getFindingClassReflectionJandexIndexException(String className, @Cause Exception e); + @Message(id = 204, value = "Unable to find the class '%s' referenced in the Jandex index.") + ValidationException getUnableToFindClassReferencedInJandexIndex(String className, @Cause Exception e); + + @Message(id = 205, value = "Unable to find the field '%2$s' referenced in the Jandex index for class '%1$s'.") + ValidationException getUnableToFindFieldReferencedInJandexIndex(@FormatWith(ClassObjectFormatter.class) Class beanClass, String string, @Cause Exception e); + + @Message(id = 206, value = "Unable to find the method '%2$s' referenced in the Jandex index for class '%1$s'.") + ValidationException getUnableToFindMethodReferencedInJandexIndex(@FormatWith(ClassObjectFormatter.class) Class beanClass, String string, @Cause Exception e); + } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/JandexMetaDataProviderTest.java similarity index 72% rename from engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java rename to engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/JandexMetaDataProviderTest.java index cd21cecad8..ccc10a0ca8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/ConstrainedFieldJandexBuilderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/JandexMetaDataProviderTest.java @@ -6,10 +6,17 @@ */ package org.hibernate.validator.test.internal.metadata.jandex; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.IOException; import java.io.InputStream; +import java.util.List; import java.util.stream.Collectors; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + import org.assertj.core.api.ListAssert; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; @@ -17,6 +24,7 @@ import org.hibernate.validator.internal.metadata.jandex.ConstrainedFieldJandexBuilder; import org.hibernate.validator.internal.metadata.jandex.util.JandexHelper; import org.hibernate.validator.internal.metadata.provider.JandexMetaDataProvider; +import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.test.internal.metadata.jandex.model.ConstrainedFieldJandexBuilderModel; import org.jboss.jandex.DotName; @@ -31,20 +39,27 @@ * @author Marko Bekhta * @author Guillaume Smet */ -public class ConstrainedFieldJandexBuilderTest { +public class JandexMetaDataProviderTest { private IndexView indexView; @BeforeClass public void setUp() throws IOException { + Class[] classesToIndex = { + Min.class, + Max.class, + NotNull.class, + ConstrainedFieldJandexBuilderModel.class + }; + Indexer indexer = new Indexer(); - // Normally a direct file is opened, but class-loader backed streams work as well. - try ( InputStream stream = getClass().getClassLoader().getResourceAsStream( - "org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.class" ) - ) { - indexer.index( stream ); - indexView = indexer.complete(); + // Normally a direct file is opened, but classloader backed streams work as well. + for ( Class classToIndex : classesToIndex ) { + try ( InputStream stream = getClass().getClassLoader().getResourceAsStream( classToIndex.getCanonicalName().replace( '.', '/' ) + ".class" ) ) { + indexer.index( stream ); + } } + indexView = indexer.complete(); } /** @@ -52,10 +67,13 @@ public void setUp() throws IOException { */ @Test public void testGetConstrainedFields() { - JandexMetaDataProvider jandexMetaDataProvider = new JandexMetaDataProvider( new ConstraintHelper(), JandexHelper.getInstance(), indexView, + JandexMetaDataProvider jandexMetaDataProvider = new JandexMetaDataProvider( new ConstraintHelper(), new JandexHelper(), indexView, new AnnotationProcessingOptionsImpl(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ) ); - jandexMetaDataProvider.getBeanConfigurationForHierarchy( ConstrainedFieldJandexBuilderModel.class ); + List> beanConfigurations = + jandexMetaDataProvider.getBeanConfigurationForHierarchy( ConstrainedFieldJandexBuilderModel.class ); + + assertThat( beanConfigurations ).isNotEmpty(); } @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java index 6c9babf8ed..98a0988d3c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java @@ -16,10 +16,10 @@ import javax.validation.constraints.Size; import javax.validation.groups.ConvertGroup; -import org.hibernate.validator.test.internal.metadata.jandex.ConstrainedFieldJandexBuilderTest; +import org.hibernate.validator.test.internal.metadata.jandex.JandexMetaDataProviderTest; /** - * Simple bean class to be used in {@link ConstrainedFieldJandexBuilderTest}. + * Simple bean class to be used in {@link JandexMetaDataProviderTest}. * * @author Marko Bekhta */ From 3c28fae432053f3713dec7447aa9bd0eacfc8fa3 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 6 Jan 2017 18:27:47 +0100 Subject: [PATCH 8/8] HV-644 Comply with the latest changes in master --- ...stractConstrainedElementJandexBuilder.java | 29 +++++++++++ .../jandex/ConstrainedFieldJandexBuilder.java | 31 ++++++++++-- .../ConstrainedMethodJandexBuilder.java | 49 ++++++++++++++++--- .../provider/JandexMetaDataProvider.java | 5 +- .../ConstrainedFieldJandexBuilderModel.java | 8 ++- 5 files changed, 104 insertions(+), 18 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java index 952b193e34..63679d4771 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/AbstractConstrainedElementJandexBuilder.java @@ -8,7 +8,11 @@ import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; import java.lang.reflect.Member; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -17,6 +21,7 @@ import java.util.Optional; import java.util.stream.Stream; +import javax.validation.Valid; import javax.validation.groups.ConvertGroup; import org.hibernate.validator.internal.engine.valuehandling.UnwrapMode; @@ -227,6 +232,30 @@ private ConstraintDescriptorImpl buildConstraintDescri ); } + /** + * TODO: this method was directly copied from AnnotationMetaDataProvider, we will need to refactor this but it's + * better to wait for the dust to settle a bit + */ + protected List> getCascadingTypeParameters(TypeVariable[] typeParameters, AnnotatedType annotatedType) { + List> cascadingTypeParameters = new ArrayList<>(); + + if ( annotatedType instanceof AnnotatedParameterizedType ) { + AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) annotatedType; + AnnotatedType[] annotatedTypeArguments = annotatedParameterizedType.getAnnotatedActualTypeArguments(); + + int i = 0; + + for ( AnnotatedType annotatedTypeArgument : annotatedTypeArguments ) { + if ( annotatedTypeArgument.isAnnotationPresent( Valid.class ) ) { + cascadingTypeParameters.add( typeParameters[i] ); + } + i++; + } + } + + return cascadingTypeParameters; + } + /** * Simple POJO that contains constraint information common to all elements. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java index 9de110c5d1..fac880bf01 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedFieldJandexBuilder.java @@ -7,14 +7,19 @@ package org.hibernate.validator.internal.metadata.jandex; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; +import java.lang.reflect.TypeVariable; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.Valid; + +import org.hibernate.validator.internal.engine.cascading.AnnotatedObject; +import org.hibernate.validator.internal.engine.cascading.ArrayElement; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; @@ -69,14 +74,14 @@ private ConstrainedField toConstrainedField(Class beanClass, FieldInfo fieldI !typeArgumentsConstraints.isEmpty(), isCascading ); + return new ConstrainedField( ConfigurationSource.JANDEX, - findField( beanClass, fieldInfo ), + field, constraints, typeArgumentsConstraints, commonInformation.getGroupConversions(), - // TODO Jandex: we need to determine the TypeVariables - Collections.emptyList(), + findCascadingTypeParameters( field ), commonInformation.getUnwrapMode() ); } @@ -98,4 +103,22 @@ private Field findField(Class beanClass, FieldInfo fieldInfo) { private MetaConstraint createMetaConstraint(Field member, ConstraintDescriptorImpl descriptor) { return new MetaConstraint<>( descriptor, ConstraintLocation.forProperty( member ) ); } + + /** + * TODO: this method was directly copied from AnnotationMetaDataProvider, we will need to refactor this but it's + * better to wait for the dust to settle a bit + */ + private List> findCascadingTypeParameters(Field field) { + TypeVariable[] typeParameters = field.getType().getTypeParameters(); + AnnotatedType annotatedType = field.getAnnotatedType(); + + List> cascadingTypeParameters = getCascadingTypeParameters( typeParameters, annotatedType ); + + if ( field.isAnnotationPresent( Valid.class ) ) { + cascadingTypeParameters.add( field.getType().isArray() ? ArrayElement.INSTANCE : AnnotatedObject.INSTANCE ); + } + + return cascadingTypeParameters; + } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java index 6d488a88b2..9d04c21f7a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/jandex/ConstrainedMethodJandexBuilder.java @@ -7,8 +7,12 @@ package org.hibernate.validator.internal.metadata.jandex; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -17,6 +21,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.Valid; + +import org.hibernate.validator.internal.engine.cascading.AnnotatedObject; +import org.hibernate.validator.internal.engine.cascading.ArrayElement; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; @@ -125,8 +133,7 @@ private ConstrainedExecutable toConstrainedExecutable(Class beanClass, Method returnValueConstraints, typeArgumentsConstraints, commonInformation.getGroupConversions(), - // TODO Jandex: we need to determine the TypeVariables - Collections.emptyList(), + findCascadingTypeParameters( executable ), commonInformation.getUnwrapMode() ); } @@ -149,10 +156,10 @@ private Stream getParameterMetaData(Class beanClass, Ex } private ConstrainedParameter toConstrainedParameter(ParameterInformation parameterInformation, Executable executable) { - CommonConstraintInformation commonInformation; - Stream> parameterConstraints; - Set> typeArgumentsConstraints; - Class parameterType = jandexHelper.getClassForName( parameterInformation.getType().name() ); + CommonConstraintInformation commonInformation; + Stream> parameterConstraints; + Set> typeArgumentsConstraints; + Class parameterType = jandexHelper.getClassForName( parameterInformation.getType().name() ); if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( executable, parameterInformation.getIndex() ) ) { parameterConstraints = Stream.empty(); @@ -191,8 +198,7 @@ private ConstrainedParameter toConstrainedParameter(ParameterInformation paramet parameterConstraints.collect( Collectors.toSet() ), typeArgumentsConstraints, commonInformation.getGroupConversions(), - // TODO Jandex: we need to determine the TypeVariables - Collections.emptyList(), + findCascadingTypeParameters( executable ), commonInformation.getUnwrapMode() ); } @@ -254,6 +260,33 @@ private boolean isSynthetic(MethodInfo methodInfo) { return ( methodInfo.flags() & ( SYNTHETIC | BRIDGE ) ) != 0; } + /** + * TODO: this method was directly copied from AnnotationMetaDataProvider, we will need to refactor this but it's + * better to wait for the dust to settle a bit + */ + private List> findCascadingTypeParameters(Executable executable) { + boolean isArray; + TypeVariable[] typeParameters; + + if ( executable instanceof Method ) { + isArray = ( (Method) executable ).getReturnType().isArray(); + typeParameters = ( (Method) executable ).getReturnType().getTypeParameters(); + } + else { + isArray = false; + typeParameters = ( (Constructor) executable ).getDeclaringClass().getTypeParameters(); + } + AnnotatedType annotatedType = executable.getAnnotatedReturnType(); + + List> cascadingTypeParameters = getCascadingTypeParameters( typeParameters, annotatedType ); + + if ( executable.isAnnotationPresent( Valid.class ) ) { + cascadingTypeParameters.add( isArray ? ArrayElement.INSTANCE : AnnotatedObject.INSTANCE ); + } + + return cascadingTypeParameters; + } + /** * Simple POJO that contains {@link Type}, name and index of a method parameter. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java index 1a89071199..8c96b5d256 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/JandexMetaDataProvider.java @@ -239,7 +239,10 @@ private static DefaultGroupSequenceProvider getGroupSequenceProvi return null; } - // TODO: this method is copied over from AnnotationMetaDataProvider. Is there anything to be done about it ? + /** + * TODO: this method was directly copied from AnnotationMetaDataProvider, we will need to refactor this but it's + * better to wait for the dust to settle a bit + */ private static DefaultGroupSequenceProvider newGroupSequenceProviderClassInstance(Class beanClass, Class> providerClass) { Method[] providerMethods = run( GetMethods.action( providerClass ) ); for ( Method method : providerMethods ) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java index 98a0988d3c..0b3ac85c61 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/jandex/model/ConstrainedFieldJandexBuilderModel.java @@ -16,6 +16,7 @@ import javax.validation.constraints.Size; import javax.validation.groups.ConvertGroup; +import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.test.internal.metadata.jandex.JandexMetaDataProviderTest; /** @@ -30,13 +31,10 @@ public class ConstrainedFieldJandexBuilderModel { private String var1; @Min(value = 2, groups = Group3.class) - @ConvertGroup.List({ - @ConvertGroup(from = Group1.class, to = Group2.class), - @ConvertGroup(from = Group3.class, to = Group2.class) - }) + @ConvertGroup(from = Group1.class, to = Group2.class) private String var2; - private Optional optional; + private Optional<@NotBlank String> optional; @Valid private List<@NotNull String> list;