Skip to content

Commit

Permalink
Avoid adding the same class to be validated multiple times
Browse files Browse the repository at this point in the history
When using Bean Validation (which have to use the recorder.classProxy),
it makes sense to create the initial set from the names of the classes
that need to be validated and then create the class proxies. Doing it
the other way around negates the use of the Set since each proxy is
different even if the target class is the same
  • Loading branch information
geoand authored and gsmet committed Jan 8, 2019
1 parent 6c6fe25 commit a7cde11
Showing 1 changed file with 18 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,34 +109,38 @@ public void build(ValidatorTemplate template, RecorderContext recorder,
// Also consider elements that are marked with @ValidateOnExecution
consideredAnnotations.add(VALIDATE_ON_EXECUTION);

Set<Class<?>> classesToBeValidated = new HashSet<>();
Set<DotName> classNamesToBeValidated = new HashSet<>();

for (DotName consideredAnnotation : consideredAnnotations) {
Collection<AnnotationInstance> annotationInstances = indexView.getAnnotations(consideredAnnotation);

for (AnnotationInstance annotation : annotationInstances) {
if (annotation.target().kind() == AnnotationTarget.Kind.FIELD) {
contributeClass(classesToBeValidated, recorder, indexView, annotation.target().asField().declaringClass().name());
contributeClass(classNamesToBeValidated, indexView, annotation.target().asField().declaringClass().name());
reflectiveFields.produce(new ReflectiveFieldBuildItem(annotation.target().asField()));
contributeClassMarkedForCascadingValidation(classesToBeValidated, recorder, indexView, consideredAnnotation, annotation.target().asField().type());
contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, consideredAnnotation, annotation.target().asField().type());
} else if (annotation.target().kind() == AnnotationTarget.Kind.METHOD) {
contributeClass(classesToBeValidated, recorder, indexView, annotation.target().asMethod().declaringClass().name());
contributeClass(classNamesToBeValidated, indexView, annotation.target().asMethod().declaringClass().name());
// we need to register the method for reflection as it could be a getter
reflectiveMethods.produce(new ReflectiveMethodBuildItem(annotation.target().asMethod()));
contributeClassMarkedForCascadingValidation(classesToBeValidated, recorder, indexView, consideredAnnotation, annotation.target().asMethod().returnType());
contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, consideredAnnotation, annotation.target().asMethod().returnType());
} else if (annotation.target().kind() == AnnotationTarget.Kind.METHOD_PARAMETER) {
contributeClass(classesToBeValidated, recorder, indexView, annotation.target().asMethodParameter().method().declaringClass().name());
contributeClass(classNamesToBeValidated, indexView, annotation.target().asMethodParameter().method().declaringClass().name());
// a getter does not have parameters so it's a pure method: no need for reflection in this case
contributeClassMarkedForCascadingValidation(classesToBeValidated, recorder, indexView, consideredAnnotation,
contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, consideredAnnotation,
// FIXME this won't work in the case of synthetic parameters
annotation.target().asMethodParameter().method().parameters().get(annotation.target().asMethodParameter().position()));
} else if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
contributeClass(classesToBeValidated, recorder, indexView, annotation.target().asClass().name());
contributeClass(classNamesToBeValidated, indexView, annotation.target().asClass().name());
// no need for reflection in the case of a class level constraint
}
}
}

Set<Class<?>> classesToBeValidated = new HashSet<>();
for (DotName className : classNamesToBeValidated) {
classesToBeValidated.add(recorder.classProxy(className.toString()));
}
template.initializeValidatorFactory(classesToBeValidated);

// Add the annotations transformer to add @MethodValidated annotations on the methods requiring validation
Expand All @@ -159,28 +163,28 @@ private static void contributeBuiltinConstraints(Set<DotName> constraintCollecto
}
}

private static void contributeClass(Set<Class<?>> classCollector, RecorderContext recorder, IndexView indexView, DotName className) {
classCollector.add(recorder.classProxy(className.toString()));
private static void contributeClass(Set<DotName> classNamesCollector, IndexView indexView, DotName className) {
classNamesCollector.add(className);
for (ClassInfo subclass : indexView.getAllKnownSubclasses(className)) {
if (Modifier.isAbstract(subclass.flags())) {
// we can avoid adding the abstract classes here: either they are parent classes
// and they will be dealt with by Hibernate Validator or they are child classes
// without any proper implementation and we can ignore them.
continue;
}
classCollector.add(recorder.classProxy(subclass.name().toString()));
classNamesCollector.add(subclass.name());
}
}

private static void contributeClassMarkedForCascadingValidation(Set<Class<?>> classCollector, RecorderContext recorder, IndexView indexView,
DotName consideredAnnotation, Type type) {
private static void contributeClassMarkedForCascadingValidation(Set<DotName> classNamesCollector,
IndexView indexView, DotName consideredAnnotation, Type type) {
if (VALID != consideredAnnotation) {
return;
}

DotName className = getClassName(type);
if (className != null) {
contributeClass(classCollector, recorder, indexView, className);
contributeClass(classNamesCollector, indexView, className);
}
}

Expand Down

0 comments on commit a7cde11

Please sign in to comment.