You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2018/03/26 23:59:30 UTC

[1/2] bval git commit: ValueExtractors work for TCK and spec compliance

Repository: bval
Updated Branches:
  refs/heads/bv2 313512196 -> 7b915ca2a


ValueExtractors work for TCK and spec compliance


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/b2b1b246
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/b2b1b246
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/b2b1b246

Branch: refs/heads/bv2
Commit: b2b1b246133b8cb6636fa273575794e42541b802
Parents: 3135121
Author: Matt Benson <mb...@apache.org>
Authored: Mon Mar 26 17:44:09 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Mon Mar 26 17:44:09 2018 -0500

----------------------------------------------------------------------
 .../apache/bval/jsr/ApacheValidatorFactory.java |   3 +-
 .../org/apache/bval/jsr/ConfigurationImpl.java  |   4 +-
 .../jsr/valueextraction/ValueExtractors.java    | 109 ++++++++++++-------
 .../main/java/org/apache/bval/util/Lazy.java    |   6 +
 4 files changed, 78 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
index 40849ac..5e5118f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
@@ -45,6 +45,7 @@ import org.apache.bval.jsr.metadata.MetadataBuilders;
 import org.apache.bval.jsr.metadata.MetadataSource;
 import org.apache.bval.jsr.util.AnnotationsManager;
 import org.apache.bval.jsr.valueextraction.ValueExtractors;
+import org.apache.bval.jsr.valueextraction.ValueExtractors.OnDuplicateContainerElementKey;
 import org.apache.bval.util.CloseableAble;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.commons.weaver.privilizer.Privilizing;
@@ -87,7 +88,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     private static ValueExtractors createBaseValueExtractors(ParticipantFactory participantFactory) {
-        final ValueExtractors result = new ValueExtractors();
+        final ValueExtractors result = new ValueExtractors(OnDuplicateContainerElementKey.OVERWRITE);
         participantFactory.loadServices(ValueExtractor.class).forEach(result::add);
         return result;
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
index d726d4c..20ac2f8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
@@ -135,7 +135,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
 
     private final LazyParticipant<ClockProvider> clockProvider = new LazyParticipant<>(this::getDefaultClockProvider);
 
-    private final ValueExtractors bootstrapValueExtractors = new ValueExtractors();
+    private final ValueExtractors bootstrapValueExtractors = ValueExtractors.EMPTY.createChild();
     private final ValueExtractors valueExtractors = bootstrapValueExtractors.createChild();
 
     private final Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration);
@@ -371,7 +371,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
 
     @Override
     public Set<ValueExtractor<?>> getValueExtractors() {
-        return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getLocalValueExtractors().values()));
+        return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getValueExtractors().values()));
     }
 
     public void deferBootstrapOverrides() {

http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
index 00577e3..a99cd3d 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
@@ -26,7 +26,9 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
@@ -52,38 +54,39 @@ import org.apache.bval.util.reflection.TypeUtils;
  * {@link ValueExtractor} collection of some level of a bean validation hierarchy.
  */
 public class ValueExtractors {
-    public static final ValueExtractors DEFAULT;
+    public enum OnDuplicateContainerElementKey {
+        EXCEPTION, OVERWRITE;
+    }
+
+    public static final ValueExtractors EMPTY =
+        new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.emptyMap());
 
+    public static final ValueExtractors DEFAULT;
     static {
-        DEFAULT = new ValueExtractors(null) {
-            {
-                final Properties defaultExtractors = new Properties();
-                try {
-                    defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties"));
-                } catch (IOException e) {
-                    throw new IllegalStateException(e);
-                }
-                split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> {
-                    try {
-                        @SuppressWarnings("unchecked")
-                        final Class<? extends ValueExtractor<?>> result =
-                            (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn)
-                                .asSubclass(ValueExtractor.class);
-                        return result;
-                    } catch (Exception e) {
-                        throw new IllegalStateException(e);
-                    }
-                }).map(ValueExtractors::newInstance).forEach(super::add);
-
-                split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container"))
-                    .flatMap(ValueExtractors::loadValueExtractors).forEach(super::add);
+        final Properties defaultExtractors = new Properties();
+        try {
+            defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties"));
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+        final Map<ContainerElementKey, ValueExtractor<?>> m = new TreeMap<>();
+        final Consumer<ValueExtractor<?>> put = ve -> m.put(ContainerElementKey.forValueExtractor(ve), ve);
+
+        split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> {
+            try {
+                @SuppressWarnings("unchecked")
+                final Class<? extends ValueExtractor<?>> result =
+                    (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn).asSubclass(ValueExtractor.class);
+                return result;
+            } catch (Exception e) {
+                throw new IllegalStateException(e);
             }
+        }).map(ValueExtractors::newInstance).forEach(put);
 
-            @Override
-            public void add(ValueExtractor<?> extractor) {
-                throw new UnsupportedOperationException();
-            }
-        };
+        split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container"))
+            .flatMap(ValueExtractors::loadValueExtractors).forEach(put);
+
+        DEFAULT = new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.unmodifiableMap(m));
     }
 
     public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) {
@@ -152,32 +155,56 @@ public class ValueExtractors {
         return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
     }
 
-    private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(HashMap::new);
     private final ValueExtractors parent;
+    private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(TreeMap::new);
+    private final OnDuplicateContainerElementKey onDuplicateContainerElementKey;
 
     public ValueExtractors() {
-        this(DEFAULT);
+        this(OnDuplicateContainerElementKey.EXCEPTION);
+    }
+
+    public ValueExtractors(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+        this(DEFAULT, Validate.notNull(onDuplicateContainerElementKey));
     }
 
-    private ValueExtractors(ValueExtractors parent) {
+    private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
         this.parent = parent;
+        this.onDuplicateContainerElementKey = onDuplicateContainerElementKey;
+    }
+
+    private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey,
+        Map<ContainerElementKey, ValueExtractor<?>> backingMap) {
+        this(parent, onDuplicateContainerElementKey);
+        this.valueExtractors.reset(backingMap);
     }
 
     public ValueExtractors createChild() {
-        return new ValueExtractors(this);
+        return createChild(OnDuplicateContainerElementKey.EXCEPTION);
     }
 
-    public void add(ValueExtractor<?> extractor) {
-        Validate.notNull(extractor);
-        valueExtractors.get().compute(ContainerElementKey.forValueExtractor(extractor), (k, v) -> {
-            Exceptions.raiseIf(v != null, ValueExtractorDeclarationException::new,
-                "Multiple context-level %ss specified for %s", f -> f.args(ValueExtractor.class.getSimpleName(), k));
-            return extractor;
-        });
+    public ValueExtractors createChild(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+        return new ValueExtractors(this, onDuplicateContainerElementKey);
     }
 
-    public Map<ContainerElementKey, ValueExtractor<?>> getLocalValueExtractors() {
-        return valueExtractors.optional().map(Collections::unmodifiableMap).orElseGet(Collections::emptyMap);
+    public void add(ValueExtractor<?> extractor) {
+        Validate.notNull(extractor);
+        final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
+        if (key == null) {
+            Exceptions.raise(IllegalStateException::new, "Computed null %s for %s",
+                ContainerElementKey.class.getSimpleName(), extractor);
+        }
+        final Map<ContainerElementKey, ValueExtractor<?>> m = valueExtractors.get();
+        if (onDuplicateContainerElementKey == OnDuplicateContainerElementKey.EXCEPTION) {
+            synchronized (this) {
+                if (m.containsKey(key)) {
+                    Exceptions.raise(ValueExtractorDeclarationException::new,
+                        "Multiple context-level %ss specified for %s", ValueExtractor.class.getSimpleName(), key);
+                }
+                m.put(key, extractor);
+            }
+        } else {
+            m.put(key, extractor);
+        }
     }
 
     public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() {

http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java b/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
index 4796de3..fbc885b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
+++ b/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
@@ -39,6 +39,12 @@ public class Lazy<T> implements Supplier<T> {
         return this;
     }
 
+    public synchronized Lazy<T> reset(T value) {
+        this.value = value;
+        this.init = null;
+        return this;
+    }
+
     @Override
     public T get() {
         if (init != null) {


[2/2] bval git commit: defer CV class selection until validation time as is proper per specification; runtime type dependent

Posted by mb...@apache.org.
defer CV class selection until validation time as is proper per specification; runtime type dependent


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/7b915ca2
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/7b915ca2
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/7b915ca2

Branch: refs/heads/bv2
Commit: 7b915ca2af3ecb8f2e9021d2fc6374f649f1d599
Parents: b2b1b24
Author: Matt Benson <mb...@apache.org>
Authored: Mon Mar 26 18:53:51 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Mon Mar 26 18:53:51 2018 -0500

----------------------------------------------------------------------
 .../apache/bval/jsr/ApacheFactoryContext.java   |   4 +
 .../ComputeConstraintValidatorClass.java        | 207 ------------------
 .../apache/bval/jsr/descriptor/ConstraintD.java |  52 -----
 .../job/ComputeConstraintValidatorClass.java    | 213 +++++++++++++++++++
 .../apache/bval/jsr/job/ValidateParameters.java |   6 +
 .../org/apache/bval/jsr/job/ValidationJob.java  |  38 +++-
 .../jsr/valueextraction/ValueExtractors.java    |   1 -
 7 files changed, 259 insertions(+), 262 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java
index c470e36..7093ad9 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java
@@ -179,4 +179,8 @@ public class ApacheFactoryContext implements ValidatorContext {
     public GroupsComputer getGroupsComputer() {
         return groupsComputer.get();
     }
+
+    public ConstraintCached getConstraintsCache() {
+        return factory.getConstraintsCache();
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java
deleted file mode 100644
index 820a1d8..0000000
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- *  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.descriptor;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.WildcardType;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.validation.ConstraintDefinitionException;
-import javax.validation.ConstraintValidator;
-import javax.validation.UnexpectedTypeException;
-import javax.validation.constraintvalidation.ValidationTarget;
-
-import org.apache.bval.jsr.ApacheValidatorFactory;
-import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
-import org.apache.bval.util.Exceptions;
-import org.apache.bval.util.Validate;
-import org.apache.bval.util.reflection.Reflection;
-import org.apache.bval.util.reflection.Reflection.Interfaces;
-import org.apache.bval.util.reflection.TypeUtils;
-import org.apache.commons.weaver.privilizer.Privilizing;
-import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-
-@Privilizing(@CallTo(Reflection.class))
-class ComputeConstraintValidatorClass<A extends Annotation>
-    implements Supplier<Class<? extends ConstraintValidator<A, ?>>> {
-
-    private static class TypeWrapper {
-        final Class<?> componentType;
-        final int arrayDepth;
-
-        TypeWrapper(Class<?> type) {
-            Class<?> c = type;
-            int d = 0;
-            while (Object[].class.isAssignableFrom(c)) {
-                d++;
-                c = c.getComponentType();
-            }
-            this.componentType = c;
-            this.arrayDepth = d;
-        }
-
-        Class<?> unwrapArrayComponentType(Class<?> t) {
-            Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new,
-                "%s not assignable from %s", t, componentType);
-            if (arrayDepth == 0) {
-                return t;
-            }
-            return Array.newInstance(t, new int[arrayDepth]).getClass();
-        }
-    }
-
-    private static final String CV = ConstraintValidator.class.getSimpleName();
-    private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build();
-
-    private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) {
-        final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class)
-            .get(ConstraintValidator.class.getTypeParameters()[1]);
-        if (!isSupported(result)) {
-            Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported",
-                result, CV, validatorType.getName());
-        }
-        return TypeUtils.getRawType(result, null);
-    }
-
-    private static boolean isSupported(Type validatedType) {
-        if (validatedType instanceof Class<?>) {
-            return true;
-        }
-        if (validatedType instanceof ParameterizedType) {
-            return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments())
-                .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED));
-        }
-        return false;
-    }
-
-    private final ApacheValidatorFactory validatorFactory;
-    private final Class<?> validatedType;
-    private final ValidationTarget validationTarget;
-    private final A constraint;
-    private final boolean composed;
-
-    ComputeConstraintValidatorClass(ApacheValidatorFactory validatorFactory, ValidationTarget validationTarget,
-        A constraint, Class<?> validatedType) {
-        super();
-        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
-        this.validationTarget = Validate.notNull(validationTarget, "validationTarget");
-        this.constraint = Validate.notNull(constraint, "constraint");
-        this.validatedType = Validate.notNull(validatedType, "validatedType");
-        this.composed = validatorFactory.getAnnotationsManager().isComposed(constraint);
-    }
-
-    @Override
-    public Class<? extends ConstraintValidator<A, ?>> get() {
-        @SuppressWarnings("unchecked")
-        final Class<A> constraintType = (Class<A>) constraint.annotationType();
-        return findValidator(validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType));
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) {
-        switch (validationTarget) {
-        case PARAMETERS:
-            return findCrossParameterValidator(infos);
-        case ANNOTATED_ELEMENT:
-            return findAnnotatedElementValidator(infos);
-        default:
-            return null;
-        }
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator(
-        Set<ConstraintValidatorInfo<A>> infos) {
-
-        final Set<ConstraintValidatorInfo<A>> set =
-            infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS))
-                .collect(Collectors.toSet());
-
-        @SuppressWarnings("unchecked")
-        final Class<A> constraintType = (Class<A>) constraint.annotationType();
-
-        final int size = set.size();
-        Exceptions.raiseIf(size > 1 || !composed && set.isEmpty(), ConstraintDefinitionException::new,
-            "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType);
-
-        final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType();
-        if (!TypeUtils.isAssignable(Object[].class, getValidatedType(result))) {
-            Exceptions.raise(ConstraintDefinitionException::new,
-                "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName());
-        }
-        return result;
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator(
-        Set<ConstraintValidatorInfo<A>> infos) {
-
-        final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream()
-            .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT))
-            .map(ConstraintValidatorInfo::getType).collect(
-                Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity(), (v1, v2) -> {
-                    Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new,
-                        "Detected collision of constraint and target type between %s and %s", v1, v2);
-                    return v1;
-                }));
-
-        final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>();
-
-        walkHierarchy().filter(validators::containsKey).forEach(type -> {
-            // if we haven't already found a candidate whose validated type
-            // is a subtype of the current evaluated type, save:
-            if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) {
-                candidates.put(type, validators.get(type));
-            }
-        });
-        final String cond;
-        switch (candidates.size()) {
-        case 1:
-            @SuppressWarnings("unchecked")
-            final Class<? extends ConstraintValidator<A, ?>> result =
-                (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next();
-            return result;
-        case 0:
-            if (composed) {
-                return null;
-            }
-            cond = "No compliant";
-            break;
-        default:
-            cond = "> 1 maximally specific";
-            break;
-        }
-        throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond,
-            constraint.annotationType().getName(), CV, TypeUtils.toString(validatedType));
-    }
-
-    // account for validated array types by unwrapping and rewrapping component
-    // type hierarchy:
-    private Stream<Class<?>> walkHierarchy() {
-        final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType));
-        Stream.Builder<Class<?>> hierarchy = Stream.builder();
-        Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy);
-        return hierarchy.build().map(w::unwrapArrayComponentType);
-    }
-}

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
index 16007c5..bd8c1f8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
@@ -35,29 +35,23 @@ import javax.validation.ConstraintTarget;
 import javax.validation.ConstraintValidator;
 import javax.validation.Payload;
 import javax.validation.ReportAsSingleViolation;
-import javax.validation.UnexpectedTypeException;
 import javax.validation.ValidationException;
 import javax.validation.groups.Default;
 import javax.validation.metadata.ConstraintDescriptor;
 import javax.validation.metadata.Scope;
 import javax.validation.metadata.ValidateUnwrappedValue;
-import javax.validation.valueextraction.UnwrapByDefault;
 import javax.validation.valueextraction.Unwrapping;
 import javax.validation.valueextraction.Unwrapping.Skip;
 import javax.validation.valueextraction.Unwrapping.Unwrap;
-import javax.validation.valueextraction.ValueExtractor;
 
 import org.apache.bval.jsr.ApacheValidatorFactory;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes.Worker;
-import org.apache.bval.jsr.metadata.ContainerElementKey;
 import org.apache.bval.jsr.metadata.Meta;
 import org.apache.bval.jsr.util.AnnotationsManager;
 import org.apache.bval.jsr.util.ToUnmodifiable;
-import org.apache.bval.jsr.valueextraction.ValueExtractors;
 import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.Validate;
-import org.apache.bval.util.reflection.TypeUtils;
 
 public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A> {
     private enum Optionality {
@@ -77,8 +71,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
     private final Meta<?> meta;
 
     private final Set<Class<? extends Payload>> payload;
-    private final Class<?> validatedType;
-
     private final Set<Class<?>> groups;
     private final boolean reportAsSingle;
     private final ValidateUnwrappedValue valueUnwrapping;
@@ -86,7 +78,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
 
     private final Set<ConstraintDescriptor<?>> composingConstraints;
     private final List<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClasses;
-    private final Class<? extends ConstraintValidator<A, ?>> constraintValidatorClass;
 
     public ConstraintD(A annotation, Scope scope, Meta<?> meta, ApacheValidatorFactory validatorFactory) {
         this.annotation = Validate.notNull(annotation, "annotation");
@@ -94,8 +85,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
         this.meta = Validate.notNull(meta, "meta");
 
         payload = computePayload();
-        validatedType = computeValidatedType(validatorFactory);
-
         groups = computeGroups();
         reportAsSingle = annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
         valueUnwrapping = computeValidateUnwrappedValue();
@@ -104,8 +93,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
         Validate.notNull(validatorFactory, "validatorFactory");
         composingConstraints = computeComposingConstraints(validatorFactory);
         constraintValidatorClasses = computeConstraintValidatorClasses(validatorFactory);
-        constraintValidatorClass = new ComputeConstraintValidatorClass<>(validatorFactory, meta.getValidationTarget(),
-            annotation, validatedType).get();
     }
 
     @Override
@@ -179,14 +166,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
         return meta.getElementType();
     }
 
-    public Class<?> getValidatedType() {
-        return validatedType;
-    }
-
-    public Class<? extends ConstraintValidator<A, ?>> getConstraintValidatorClass() {
-        return constraintValidatorClass;
-    }
-
     private <T> T read(ConstraintAnnotationAttributes attr) {
         return read(attr, Optionality.OPTIONAL);
     }
@@ -245,35 +224,4 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
         }
         return result;
     }
-
-    private Class<?> computeValidatedType(ApacheValidatorFactory validatorFactory) {
-        final Class<?> rawType = TypeUtils.getRawType(meta.getType(), null);
-
-        if (rawType == null) {
-            Exceptions.raise(UnexpectedTypeException::new, "Could not calculate validated type from %s",
-                meta.getType());
-        }
-        if (payload.contains(Unwrapping.Skip.class)) {
-            return rawType;
-        }
-        final ValueExtractor<?> valueExtractor =
-            validatorFactory.getValueExtractors().find(new ContainerElementKey(meta.getAnnotatedType(), null));
-
-        final boolean unwrap = payload.contains(Unwrapping.Unwrap.class);
-
-        if (valueExtractor == null) {
-            if (unwrap) {
-                Exceptions.raise(ConstraintDeclarationException::new, "No compatible %s found for %s",
-                    ValueExtractor.class.getSimpleName(), meta.getType());
-            }
-        } else {
-            @SuppressWarnings("unchecked")
-            final Class<? extends ValueExtractor<?>> extractorClass =
-                (Class<? extends ValueExtractor<?>>) valueExtractor.getClass();
-            if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) {
-                return ValueExtractors.getExtractedType(valueExtractor, meta.getType());
-            }
-        }
-        return rawType;
-    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
new file mode 100644
index 0000000..d9c0b56
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
@@ -0,0 +1,213 @@
+/*
+ *  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.job;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintValidator;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.jsr.ConstraintCached;
+import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.bval.util.reflection.Reflection.Interfaces;
+import org.apache.bval.util.reflection.TypeUtils;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+class ComputeConstraintValidatorClass<A extends Annotation>
+    implements Supplier<Class<? extends ConstraintValidator<A, ?>>> {
+
+    private static class TypeWrapper {
+        final Class<?> componentType;
+        final int arrayDepth;
+
+        TypeWrapper(Class<?> type) {
+            Class<?> c = type;
+            int d = 0;
+            while (Object[].class.isAssignableFrom(c)) {
+                d++;
+                c = c.getComponentType();
+            }
+            this.componentType = c;
+            this.arrayDepth = d;
+        }
+
+        Class<?> unwrapArrayComponentType(Class<?> t) {
+            Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new,
+                "%s not assignable from %s", t, componentType);
+            if (arrayDepth == 0) {
+                return t;
+            }
+            return Array.newInstance(t, new int[arrayDepth]).getClass();
+        }
+    }
+
+    private static final String CV = ConstraintValidator.class.getSimpleName();
+    private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build();
+
+    private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) {
+        final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class)
+            .get(ConstraintValidator.class.getTypeParameters()[1]);
+        if (!isSupported(result)) {
+            Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported",
+                result, CV, validatorType.getName());
+        }
+        return TypeUtils.getRawType(result, null);
+    }
+
+    private static boolean isSupported(Type validatedType) {
+        if (validatedType instanceof Class<?>) {
+            return true;
+        }
+        if (validatedType instanceof ParameterizedType) {
+            return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments())
+                .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED));
+        }
+        return false;
+    }
+
+    private final ConstraintCached constraintsCache;
+    private final ConstraintD<?> descriptor;
+    private final ValidationTarget validationTarget;
+    private final Class<?> validatedType;
+
+    ComputeConstraintValidatorClass(ConstraintCached constraintsCache, ConstraintD<A> descriptor,
+        ValidationTarget validationTarget, Class<?> validatedType) {
+        super();
+        this.constraintsCache = Validate.notNull(constraintsCache, "constraintsCache");
+        this.descriptor = Validate.notNull(descriptor, "descriptor");
+        this.validationTarget = Validate.notNull(validationTarget, "validationTarget");
+        this.validatedType = Validate.notNull(validatedType, "validatedType");
+    }
+
+    @Override
+    public Class<? extends ConstraintValidator<A, ?>> get() {
+        @SuppressWarnings("unchecked")
+        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();
+        return findValidator(constraintsCache.getConstraintValidatorInfo(constraintType));
+    }
+
+    private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) {
+        switch (validationTarget) {
+        case PARAMETERS:
+            return findCrossParameterValidator(infos);
+        case ANNOTATED_ELEMENT:
+            return findAnnotatedElementValidator(infos);
+        default:
+            return null;
+        }
+    }
+
+    private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator(
+        Set<ConstraintValidatorInfo<A>> infos) {
+
+        final Set<ConstraintValidatorInfo<A>> set =
+            infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS))
+                .collect(Collectors.toSet());
+
+        @SuppressWarnings("unchecked")
+        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();
+
+        final int size = set.size();
+        Exceptions.raiseIf(size > 1 || !isComposed() && set.isEmpty(), ConstraintDefinitionException::new,
+            "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType);
+
+        final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType();
+        if (!TypeUtils.isAssignable(Object[].class, getValidatedType(result))) {
+            Exceptions.raise(ConstraintDefinitionException::new,
+                "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName());
+        }
+        return result;
+    }
+
+    private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator(
+        Set<ConstraintValidatorInfo<A>> infos) {
+
+        final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream()
+            .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT))
+            .map(ConstraintValidatorInfo::getType).collect(
+                Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity(), (v1, v2) -> {
+                    Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new,
+                        "Detected collision of constraint and target type between %s and %s", v1, v2);
+                    return v1;
+                }));
+
+        final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>();
+
+        walkHierarchy().filter(validators::containsKey).forEach(type -> {
+            // if we haven't already found a candidate whose validated type
+            // is a subtype of the current evaluated type, save:
+            if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) {
+                candidates.put(type, validators.get(type));
+            }
+        });
+        final String cond;
+        switch (candidates.size()) {
+        case 1:
+            @SuppressWarnings("unchecked")
+            final Class<? extends ConstraintValidator<A, ?>> result =
+                (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next();
+            return result;
+        case 0:
+            if (isComposed()) {
+                return null;
+            }
+            cond = "No compliant";
+            break;
+        default:
+            cond = "> 1 maximally specific";
+            break;
+        }
+        throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond,
+            descriptor.getAnnotation().annotationType().getName(), CV, TypeUtils.toString(validatedType));
+    }
+
+    // account for validated array types by unwrapping and rewrapping component
+    // type hierarchy:
+    private Stream<Class<?>> walkHierarchy() {
+        final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType));
+        Stream.Builder<Class<?>> hierarchy = Stream.builder();
+        Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy);
+        if (validatedType.isInterface()) {
+            hierarchy.accept(Object.class);
+        }
+        return hierarchy.build().map(w::unwrapArrayComponentType);
+    }
+
+    private boolean isComposed() {
+        return !descriptor.getComposingConstraints().isEmpty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
index d4f9668..0e2b30c 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
@@ -29,6 +29,7 @@ import java.util.stream.IntStream;
 import javax.validation.ConstraintViolation;
 import javax.validation.ParameterNameProvider;
 import javax.validation.Path;
+import javax.validation.constraintvalidation.ValidationTarget;
 import javax.validation.metadata.ExecutableDescriptor;
 
 import org.apache.bval.jsr.ApacheFactoryContext;
@@ -120,6 +121,11 @@ public abstract class ValidateParameters<E extends Executable, T> extends Valida
         }
 
         @Override
+        protected ValidationTarget getValidationTarget() {
+            return ValidationTarget.PARAMETERS;
+        }
+
+        @Override
         void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
             executableDescriptor.getParameterDescriptors().stream()
                 .map(pd -> new SproutFrame<ParameterD<?>>(this, (ParameterD<?>) pd, parameter(pd.getIndex())))

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
index 0501a8a..d2be23a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
@@ -48,12 +48,14 @@ import javax.validation.Path;
 import javax.validation.TraversableResolver;
 import javax.validation.UnexpectedTypeException;
 import javax.validation.ValidationException;
+import javax.validation.constraintvalidation.ValidationTarget;
 import javax.validation.groups.Default;
 import javax.validation.metadata.CascadableDescriptor;
 import javax.validation.metadata.ContainerDescriptor;
 import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
 import javax.validation.metadata.PropertyDescriptor;
 import javax.validation.metadata.ValidateUnwrappedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
 import javax.validation.valueextraction.ValueExtractor;
 
 import org.apache.bval.jsr.ApacheFactoryContext;
@@ -93,6 +95,10 @@ public abstract class ValidationJob<T> {
             this.context = Validate.notNull(context, "context");
         }
 
+        protected ValidationTarget getValidationTarget() {
+            return ValidationTarget.ANNOTATED_ELEMENT;
+        }
+
         final ValidationJob<T> getJob() {
             return ValidationJob.this;
         }
@@ -230,7 +236,8 @@ public abstract class ValidationJob<T> {
         @SuppressWarnings({ "rawtypes" })
         private ConstraintValidator getConstraintValidator(ConstraintD<?> constraint) {
             final Class<? extends ConstraintValidator> constraintValidatorClass =
-                constraint.getConstraintValidatorClass();
+                new ComputeConstraintValidatorClass<>(validatorContext.getConstraintsCache(), constraint,
+                    getValidationTarget(), computeValidatedType(constraint)).get();
 
             if (constraintValidatorClass == null) {
                 if (constraint.getComposingConstraints().isEmpty()) {
@@ -254,7 +261,34 @@ public abstract class ValidationJob<T> {
             return constraintValidator;
         }
 
-        protected Stream<Class<?>> expand(Class<?> group) {
+        private Class<?> computeValidatedType(ConstraintD<?> constraint) {
+            final Class<?> elementClass = descriptor.getElementClass();
+
+            if (constraint.getValueUnwrapping() == ValidateUnwrappedValue.SKIP) {
+                return elementClass;
+            }
+            final ValueExtractor<?> valueExtractor =
+                validatorContext.getValueExtractors().find(new ContainerElementKey(elementClass, null));
+
+            final boolean unwrap = constraint.getValueUnwrapping() == ValidateUnwrappedValue.UNWRAP;
+
+            if (valueExtractor == null) {
+                if (unwrap) {
+                    Exceptions.raise(ConstraintDeclarationException::new, "No compatible %s found for %s",
+                        ValueExtractor.class.getSimpleName(), elementClass);
+                }
+            } else {
+                @SuppressWarnings("unchecked")
+                final Class<? extends ValueExtractor<?>> extractorClass =
+                    (Class<? extends ValueExtractor<?>>) valueExtractor.getClass();
+                if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) {
+                    return ValueExtractors.getExtractedType(valueExtractor, elementClass);
+                }
+            }
+            return elementClass;
+        }
+
+        private Stream<Class<?>> expand(Class<?> group) {
             if (Default.class.equals(group)) {
                 final List<Class<?>> groupSequence = descriptor.getGroupSequence();
                 if (groupSequence != null) {

http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
index a99cd3d..ca45701 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
@@ -90,7 +90,6 @@ public class ValueExtractors {
     }
 
     public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) {
-        
         final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
         Type result = key.getAnnotatedType().getType();
         if (result instanceof WildcardType && key.getTypeArgumentIndex() != null) {