This is an automated email from the ASF dual-hosted git repository.

jungm pushed a commit to branch bval-3.0.x
in repository https://gitbox.apache.org/repos/asf/bval.git


The following commit(s) were added to refs/heads/bval-3.0.x by this push:
     new e21d638  BVAL-222 - enable BValInterceptor when constraints are only 
placed on parent classes
e21d638 is described below

commit e21d638afa5ac56ee94f0941fabab18e6d416549
Author: Markus Jung <[email protected]>
AuthorDate: Sat Mar 8 20:29:35 2025 +0100

    BVAL-222 - enable BValInterceptor when constraints are only placed on 
parent classes
    
    cherry picked from eae0a94323265c0a0f3f099ffad0c6ec7469bd83
---
 bval-jsr/pom.xml                                   |  19 ++++
 .../java/org/apache/bval/cdi/BValExtension.java    | 108 ++++++++++++++-------
 .../cdi/CdiConstraintOnlyOnParentClassTest.java    |  75 ++++++++++++++
 bval-tck/pom.xml                                   |  12 +--
 pom.xml                                            |   4 +
 5 files changed, 177 insertions(+), 41 deletions(-)

diff --git a/bval-jsr/pom.xml b/bval-jsr/pom.xml
index 55c5c63..93ec67a 100644
--- a/bval-jsr/pom.xml
+++ b/bval-jsr/pom.xml
@@ -224,6 +224,25 @@
                 </exclusion>
             </exclusions>
         </dependency>
+
+        <dependency>
+            <groupId>org.jboss.arquillian.junit</groupId>
+            <artifactId>arquillian-junit-container</artifactId>
+            <scope>test</scope>
+            <version>${version.arquillian}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.openwebbeans.arquillian</groupId>
+            <artifactId>owb-arquillian-standalone</artifactId>
+            <version>${version.owb}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.openwebbeans</groupId>
+            <artifactId>openwebbeans-impl</artifactId>
+            <version>${version.owb}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
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 9943ca4..20467eb 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,11 +19,17 @@
 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;
+import java.util.LinkedList;
+import java.util.Queue;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -33,10 +39,8 @@ import jakarta.enterprise.context.spi.CreationalContext;
 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.AnnotatedType;
 import jakarta.enterprise.inject.spi.Bean;
-import jakarta.enterprise.inject.spi.BeanAttributes;
 import jakarta.enterprise.inject.spi.BeanManager;
 import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
 import jakarta.enterprise.inject.spi.CDI;
@@ -66,7 +70,7 @@ import org.apache.bval.util.Validate;
 public class BValExtension implements Extension {
     private static final Logger LOGGER = 
Logger.getLogger(BValExtension.class.getName());
 
-    private static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER =
+    public static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER =
         annotatedType -> 
!annotatedType.getJavaClass().getName().startsWith("org.apache.bval.");
 
     private static AnnotatedTypeFilter annotatedTypeFilter = 
DEFAULT_ANNOTATED_TYPE_FILTER;
@@ -128,13 +132,28 @@ public class BValExtension implements Extension {
         final int modifiers = javaClass.getModifiers();
         if (!javaClass.isInterface() && !javaClass.isAnonymousClass() && 
!Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) {
             try {
-                if (hasValidation(annotatedType)
-                    || hasValidationAnnotation(annotatedType.getMethods())
-                    || hasValidationAnnotation(annotatedType.getConstructors())
-                    || Stream.concat(annotatedType.getMethods().stream(), 
annotatedType.getConstructors().stream())
-                        .flatMap(it -> it.getParameters().stream())
-                        .anyMatch(this::hasValidation)) {
-                    pat.setAnnotatedType(new 
BValAnnotatedType<>(annotatedType));
+                Queue<Class<?>> toProcess = new LinkedList<>();
+                toProcess.add(annotatedType.getJavaClass());
+
+                while (!toProcess.isEmpty()) {
+                    Class<?> now = toProcess.poll();
+                    Executable[] methods = now.getMethods();
+                    Executable[] constructors = now.getConstructors();
+
+                    if (hasValidation(now)
+                            || hasValidation(methods) || 
hasValidation(constructors)
+                            || hasParamsWithValidation(methods) || 
hasParamsWithValidation(constructors)) {
+                        pat.setAnnotatedType(new 
BValAnnotatedType<>(annotatedType));
+
+                        break;
+                    }
+
+                    // Nothing found, collect superclass/interface and repeat 
(See BVAL-222)
+                    if (now.getSuperclass() != Object.class) {
+                        toProcess.add(now.getSuperclass());
+                    }
+
+                    toProcess.addAll(Arrays.asList(now.getInterfaces()));
                 }
             } catch (final Exception e) {
                 if (e instanceof ValidationException) {
@@ -201,32 +220,53 @@ public class BValExtension implements Extension {
         notBValAnnotation.clear();
     }
 
-    private boolean hasValidationAnnotation(final Collection<? extends 
Annotated> annotateds) {
-        return annotateds.stream().anyMatch(this::hasValidation);
+    private boolean hasValidation(final AnnotatedElement[] elements) {
+        for (AnnotatedElement element : elements) {
+            if (hasValidation(element)) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
-    private boolean hasValidation(final Annotated m) {
-        return m.getAnnotations().stream()
-                .anyMatch(it -> {
-                    final Class<? extends Annotation> type = 
it.annotationType();
-                    if (type == ValidateOnExecution.class || type == 
Valid.class) {
-                        return true;
-                    }
-                    if (isSkippedAnnotation(type)) {
-                        return false;
-                    }
-                    if 
(type.getName().startsWith("jakarta.validation.constraints")) {
-                        return true;
-                    }
-                    if (notBValAnnotation.contains(type)) { // more likely so 
faster first
-                        return false;
-                    }
-                    if (potentiallyBValAnnotation.contains(type)) {
-                        return true;
-                    }
-                    cacheIsBvalAnnotation(type);
-                    return potentiallyBValAnnotation.contains(type);
-                });
+    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) {
+        for (Annotation annotation : element.getAnnotations()) {
+            final Class<? extends Annotation> type = 
annotation.annotationType();
+            if (type == ValidateOnExecution.class || type == Valid.class) {
+                return true;
+            }
+            if (isSkippedAnnotation(type)) {
+                continue;
+            }
+            if (type.getName().startsWith("jakarta.validation.constraints")) {
+                return true;
+            }
+            if (notBValAnnotation.contains(type)) { // more likely so faster 
first
+                continue;
+            }
+            if (potentiallyBValAnnotation.contains(type)) {
+                return true;
+            }
+            cacheIsBvalAnnotation(type);
+            if (potentiallyBValAnnotation.contains(type)) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     private boolean isSkippedAnnotation(final Class<? extends Annotation> 
type) {
diff --git 
a/bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java
 
b/bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java
new file mode 100644
index 0000000..1713a6e
--- /dev/null
+++ 
b/bval-jsr/src/test/java/org/apache/bval/jsr/cdi/CdiConstraintOnlyOnParentClassTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.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;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.junit.runner.RunWith;
+
+@RunWith(Arquillian.class)
+public class CdiConstraintOnlyOnParentClassTest {
+    @ClassRule
+    public static ExternalResource allowMyServiceImplType = new 
ExternalResource() {
+        @Override
+        protected void before() throws Throwable {
+            BValExtension.setAnnotatedTypeFilter(at -> at.getJavaClass() == 
GreetingServiceImpl.class);
+        }
+
+        @Override
+        protected void after() {
+            
BValExtension.setAnnotatedTypeFilter(BValExtension.DEFAULT_ANNOTATED_TYPE_FILTER);
+        }
+    };
+
+    @Inject
+    private GreetingService service;
+
+    @Deployment
+    public static JavaArchive createDeployment() {
+        return ShrinkWrap.create(JavaArchive.class)
+                .addClasses(GreetingService.class, GreetingServiceImpl.class);
+    }
+
+    @Test
+    public void validationFail() {
+        Assert.assertThrows(ConstraintViolationException.class, () -> 
service.greet(null));
+    }
+
+    public interface GreetingService {
+        void greet(@NotNull String name);
+    }
+
+    @ApplicationScoped
+    public static class GreetingServiceImpl implements GreetingService {
+        @Override
+        public void greet(String name) {
+        }
+    }
+}
diff --git a/bval-tck/pom.xml b/bval-tck/pom.xml
index 8a0b9d5..985dab5 100644
--- a/bval-tck/pom.xml
+++ b/bval-tck/pom.xml
@@ -32,8 +32,6 @@ under the License.
 
     <properties>
         <tck.version>3.0.1</tck.version>
-        <owb.version>4.0.2</owb.version>
-        <arquillian.version>1.8.0.Final</arquillian.version>
         
<validation.provider>org.apache.bval.jsr.ApacheValidationProvider</validation.provider>
     </properties>
 
@@ -115,18 +113,18 @@ under the License.
         <dependency>
             <groupId>org.jboss.arquillian.container</groupId>
             <artifactId>arquillian-container-test-spi</artifactId>
-            <version>${arquillian.version}</version>
+            <version>${version.arquillian}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.openwebbeans.arquillian</groupId>
             <artifactId>owb-arquillian-standalone</artifactId>
-            <version>${owb.version}</version>
+            <version>${version.owb}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.openwebbeans</groupId>
             <artifactId>openwebbeans-impl</artifactId>
-            <version>${owb.version}</version>
+            <version>${version.owb}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -144,13 +142,13 @@ under the License.
         <dependency>
             <groupId>org.jboss.arquillian.testng</groupId>
             <artifactId>arquillian-testng-container</artifactId>
-            <version>${arquillian.version}</version>
+            <version>${version.arquillian}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.jboss.arquillian.protocol</groupId>
             <artifactId>arquillian-protocol-servlet-jakarta</artifactId>
-            <version>${arquillian.version}</version>
+            <version>${version.arquillian}</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/pom.xml b/pom.xml
index f57ff48..866b62c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,10 @@
         <site.server>bval.site</site.server>
         <built.by>${user.name}</built.by>
         <plugin.javadoc.version>3.6.3</plugin.javadoc.version>
+
+        <!-- testing dependencies -->
+        <version.owb>4.0.3</version.owb>
+        <version.arquillian>1.8.0.Final</version.arquillian>
     </properties>
 
     <inceptionYear>2010</inceptionYear>

Reply via email to