From b909520acd00e7f40372122d0e06e648170fc5f1 Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Sun, 18 May 2025 20:38:32 +0200 Subject: [PATCH] BAVL-223 - rely on CDI AnnotatedTypes where possible instead of pure java reflection --- .../org/apache/bval/cdi/BValExtension.java | 62 ++++++++----------- .../CdiConstraintOnlyOnParentClassTest.java | 3 +- 2 files changed, 28 insertions(+), 37 deletions(-) rename bval-jsr/src/test/java/org/apache/bval/{jsr => }/cdi/CdiConstraintOnlyOnParentClassTest.java (97%) diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java index f5723b0883..358453fffd 100644 --- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java +++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java @@ -19,12 +19,8 @@ package org.apache.bval.cdi; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Executable; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; import java.lang.reflect.Type; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -39,6 +35,11 @@ import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.AfterBeanDiscovery; import jakarta.enterprise.inject.spi.AfterDeploymentValidation; +import jakarta.enterprise.inject.spi.Annotated; +import jakarta.enterprise.inject.spi.AnnotatedCallable; +import jakarta.enterprise.inject.spi.AnnotatedConstructor; +import jakarta.enterprise.inject.spi.AnnotatedMethod; +import jakarta.enterprise.inject.spi.AnnotatedParameter; import jakarta.enterprise.inject.spi.AnnotatedType; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; @@ -70,7 +71,8 @@ public class BValExtension implements Extension { private static final Logger LOGGER = Logger.getLogger(BValExtension.class.getName()); - public static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER = + // protected so can be accessed in tests + protected static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER = annotatedType -> !annotatedType.getJavaClass().getName().startsWith("org.apache.bval."); private static AnnotatedTypeFilter annotatedTypeFilter = DEFAULT_ANNOTATED_TYPE_FILTER; @@ -119,7 +121,7 @@ public void addBvalBinding(final @Observes BeforeBeanDiscovery beforeBeanDiscove } // @WithAnnotations(ValidateOnExecution.class) doesn't check interfaces so not enough - public void processAnnotatedType(final @Observes ProcessAnnotatedType pat) { + public void processAnnotatedType(final @Observes ProcessAnnotatedType pat, BeanManager beanManager) { if (!isExecutableValidationEnabled) { return; } @@ -132,28 +134,31 @@ public void processAnnotatedType(final @Observes ProcessAnnotatedType pat final int modifiers = javaClass.getModifiers(); if (!javaClass.isInterface() && !javaClass.isAnonymousClass() && !Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) { try { - Queue> toProcess = new LinkedList<>(); - toProcess.add(annotatedType.getJavaClass()); + Queue> toProcess = new LinkedList<>(); + toProcess.add(annotatedType); while (!toProcess.isEmpty()) { - Class now = toProcess.poll(); - Executable[] methods = now.getMethods(); - Executable[] constructors = now.getConstructors(); + AnnotatedType now = toProcess.poll(); + Set> methods = now.getMethods(); + Set> constructors = now.getConstructors(); if (hasValidation(now) - || hasValidation(methods) || hasValidation(constructors) - || hasParamsWithValidation(methods) || hasParamsWithValidation(constructors)) { + || methods.stream().anyMatch(this::hasValidation) + || constructors.stream().anyMatch(this::hasValidation) + || methods.stream().anyMatch(this::hasParamsWithValidation) + || constructors.stream().anyMatch(this::hasParamsWithValidation)) { pat.setAnnotatedType(new BValAnnotatedType<>(annotatedType)); - break; } - // Nothing found, collect superclass/interface and repeat (See BVAL-222) - if (now.getSuperclass() != Object.class && now.getSuperclass() != null) { - toProcess.add(now.getSuperclass()); + Class superclass = now.getJavaClass().getSuperclass(); + if (superclass != Object.class && superclass != null) { + toProcess.add(beanManager.createAnnotatedType(superclass)); } - toProcess.addAll(Arrays.asList(now.getInterfaces())); + for (Class iface : now.getJavaClass().getInterfaces()) { + toProcess.add(beanManager.createAnnotatedType(iface)); + } } } catch (final Exception e) { if (e instanceof ValidationException) { @@ -165,7 +170,6 @@ public void processAnnotatedType(final @Observes ProcessAnnotatedType pat } } } - public void processBean(final @Observes ProcessBean processBeanEvent) { if (validatorFound && validatorFactoryFound) { return; @@ -220,9 +224,9 @@ public void afterStart(@Observes final AfterDeploymentValidation clearEvent) { notBValAnnotation.clear(); } - private boolean hasValidation(final AnnotatedElement[] elements) { - for (AnnotatedElement element : elements) { - if (hasValidation(element)) { + private boolean hasParamsWithValidation(AnnotatedCallable callable) { + for (AnnotatedParameter param : callable.getParameters()) { + if (hasValidation(param)) { return true; } } @@ -230,19 +234,7 @@ private boolean hasValidation(final AnnotatedElement[] elements) { return false; } - private boolean hasParamsWithValidation(Executable[] executables) { - for (Executable executable : executables) { - for (Parameter param : executable.getParameters()) { - if (hasValidation(param)) { - return true; - } - } - } - - return false; - } - - private boolean hasValidation(final AnnotatedElement element) { + private boolean hasValidation(final Annotated element) { for (Annotation annotation : element.getAnnotations()) { final Class type = annotation.annotationType(); if (type == ValidateOnExecution.class || type == Valid.class) { diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java b/bval-jsr/src/test/java/org/apache/bval/cdi/CdiConstraintOnlyOnParentClassTest.java similarity index 97% rename from bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java rename to bval-jsr/src/test/java/org/apache/bval/cdi/CdiConstraintOnlyOnParentClassTest.java index dc1c8e28ef..f0c2d3e18b 100644 --- a/bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java +++ b/bval-jsr/src/test/java/org/apache/bval/cdi/CdiConstraintOnlyOnParentClassTest.java @@ -16,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.bval.jsr.cdi; +package org.apache.bval.cdi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.validation.ConstraintViolationException; import jakarta.validation.constraints.NotNull; -import org.apache.bval.cdi.BValExtension; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap;