apply constraints cache size to constraint attributes cache used for annotation 
proxies


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

Branch: refs/heads/master
Commit: 1d54c14c920188f09abfe8aad294824f9d9f0a66
Parents: 3735154
Author: Matt Benson <[email protected]>
Authored: Wed Oct 17 17:17:55 2018 -0500
Committer: Matt Benson <[email protected]>
Committed: Wed Oct 17 17:17:55 2018 -0500

----------------------------------------------------------------------
 .../apache/bval/jsr/ApacheValidatorFactory.java |   8 +-
 .../bval/jsr/descriptor/MetadataReader.java     |   5 +-
 .../bval/jsr/metadata/MetadataSource.java       |   4 +
 .../apache/bval/jsr/metadata/XmlBuilder.java    |  16 +-
 .../apache/bval/jsr/util/AnnotationProxy.java   | 169 ++++++++++++
 .../bval/jsr/util/AnnotationProxyBuilder.java   | 235 +++++++++++++++++
 .../bval/jsr/util/AnnotationsManager.java       |  24 +-
 .../apache/bval/jsr/xml/AnnotationProxy.java    | 169 ------------
 .../bval/jsr/xml/AnnotationProxyBuilder.java    | 257 -------------------
 .../bval/jsr/xml/ValidationMappingParser.java   |  17 +-
 10 files changed, 461 insertions(+), 443 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/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 481b501..89caf63 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
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 
 import javax.validation.ClockProvider;
@@ -368,6 +369,11 @@ public class ApacheValidatorFactory implements 
ValidatorFactory, Cloneable {
             getMetadataBuilders().registerCustomBuilder((Class) t, 
(MetadataBuilder.ForBean) b);
         };
         participantFactory.loadServices(MetadataSource.class)
-            .forEach(ms -> ms.process(configuration, 
getConstraintsCache()::add, addBuilder));
+            .forEach(ms -> {
+                
Optional.of(ms).filter(MetadataSource.FactoryDependent.class::isInstance)
+                    
.map(MetadataSource.FactoryDependent.class::cast).ifPresent(fd -> 
fd.setFactory(this));
+
+                ms.process(configuration, getConstraintsCache()::add, 
addBuilder);
+            });
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
index 0828933..6ef1a86 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
@@ -65,10 +65,10 @@ import org.apache.bval.jsr.metadata.EmptyBuilder;
 import org.apache.bval.jsr.metadata.Meta;
 import org.apache.bval.jsr.metadata.MetadataBuilder;
 import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.jsr.util.AnnotationProxyBuilder;
 import org.apache.bval.jsr.util.AnnotationsManager;
 import org.apache.bval.jsr.util.Methods;
 import org.apache.bval.jsr.util.ToUnmodifiable;
-import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
 import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.ObjectUtils;
 import org.apache.bval.util.Validate;
@@ -123,7 +123,8 @@ class MetadataReader {
                 }
             }
             if (mustRewrite) {
-                final AnnotationProxyBuilder<A> builder = new 
AnnotationProxyBuilder<A>(constraint);
+                final AnnotationProxyBuilder<A> builder =
+                    
validatorFactory.getAnnotationsManager().buildProxyFor(constraint);
                 builder.setGroups(groups);
                 return builder.createAnnotation();
             }

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataSource.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataSource.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataSource.java
index 44cca43..29cc32b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataSource.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataSource.java
@@ -20,12 +20,16 @@ import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 import javax.validation.ConstraintValidator;
+import javax.validation.ValidatorFactory;
 import javax.validation.spi.ConfigurationState;
 
 /**
  * Service interface for user metadata customizations.
  */
 public interface MetadataSource {
+    interface FactoryDependent extends MetadataSource {
+        void setFactory(ValidatorFactory validatorFactory);
+    }
 
     /**
      * Add {@link ConstraintValidator} mappings and/or metadata builders.

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
index be23e7c..42ce439 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -45,10 +45,11 @@ import javax.validation.ValidationException;
 import javax.validation.groups.Default;
 import javax.xml.bind.JAXBElement;
 
+import org.apache.bval.jsr.ApacheValidatorFactory;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes;
 import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.util.AnnotationProxyBuilder;
 import org.apache.bval.jsr.util.ToUnmodifiable;
-import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
 import org.apache.bval.jsr.xml.AnnotationType;
 import org.apache.bval.jsr.xml.BeanType;
 import org.apache.bval.jsr.xml.ClassType;
@@ -475,13 +476,14 @@ public class XmlBuilder {
         return lazy.get();
     }
 
+    private final ApacheValidatorFactory validatorFactory;
     private final ConstraintMappingsType constraintMappings;
     private final Version version;
 
-    public XmlBuilder(ConstraintMappingsType constraintMappings) {
+    public XmlBuilder(ApacheValidatorFactory validatorFactory, 
ConstraintMappingsType constraintMappings) {
         super();
-        this.constraintMappings = constraintMappings;
-        Validate.notNull(constraintMappings, "constraintMappings");
+        this.validatorFactory = Validate.notNull(validatorFactory, 
"validatorFactory");
+        this.constraintMappings = Validate.notNull(constraintMappings, 
"constraintMappings");
         this.version = Version.of(constraintMappings);
         new MappingValidator(constraintMappings, 
this::resolveClass).validateMappings();
     }
@@ -544,7 +546,8 @@ public class XmlBuilder {
 
     private <A extends Annotation, T> A createConstraint(final ConstraintType 
constraint, ConstraintTarget target) {
         final Class<A> annotationClass = this.<A> 
loadClass(toQualifiedClassName(constraint.getAnnotation()));
-        final AnnotationProxyBuilder<A> annoBuilder = new 
AnnotationProxyBuilder<A>(annotationClass);
+        final AnnotationProxyBuilder<A> annoBuilder =
+            
validatorFactory.getAnnotationsManager().buildProxyFor(annotationClass);
 
         if (constraint.getMessage() != null) {
             annoBuilder.setMessage(constraint.getMessage());
@@ -676,7 +679,8 @@ public class XmlBuilder {
     }
 
     private <A extends Annotation> Annotation createAnnotation(AnnotationType 
annotationType, Class<A> returnType) {
-        final AnnotationProxyBuilder<A> metaAnnotation = new 
AnnotationProxyBuilder<>(returnType);
+        final AnnotationProxyBuilder<A> metaAnnotation =
+            validatorFactory.getAnnotationsManager().buildProxyFor(returnType);
         for (ElementType elementType : annotationType.getElement()) {
             final String name = elementType.getName();
             metaAnnotation.setValue(name, getElementValue(elementType, 
getAnnotationParameterType(returnType, name)));

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxy.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxy.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxy.java
new file mode 100644
index 0000000..1d21189
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxy.java
@@ -0,0 +1,169 @@
+/*
+ *  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.util;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import javax.validation.Valid;
+
+import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.reflection.Reflection;
+
+/**
+ * Description: <br/>
+ * InvocationHandler implementation of <code>Annotation</code> that pretends 
it is a "real" source code annotation.
+ * <p/>
+ */
+class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+    
+    private Signature EQUALS = new Signature("equals", Object.class);
+
+    private final Class<? extends Annotation> annotationType;
+    private final SortedMap<String, Object> values;
+
+    /**
+     * Create a new AnnotationProxy instance.
+     * 
+     * @param <A>
+     * @param descriptor
+     */
+    <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> 
descriptor) {
+        this.annotationType = descriptor.getType();
+        values = new TreeMap<>();
+        int processedValuesFromDescriptor = 0;
+        for (final Method m : descriptor.getMethods()) {
+            if (descriptor.contains(m.getName())) {
+                values.put(m.getName(), descriptor.getValue(m.getName()));
+                processedValuesFromDescriptor++;
+            } else {
+                if (m.getDefaultValue() == null) {
+                    Exceptions.raise(IllegalArgumentException::new, "No value 
provided for %s", m.getName());
+                }
+                values.put(m.getName(), m.getDefaultValue());
+            }
+        }
+        Exceptions.raiseUnless(processedValuesFromDescriptor == 
descriptor.size() || Valid.class.equals(annotationType),
+            IllegalArgumentException::new, "Trying to instantiate %s with 
unknown parameters.",
+            f -> f.args(annotationType.getName()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws 
Throwable {
+        if (values.containsKey(method.getName())) {
+            return values.get(method.getName());
+        }
+        if (EQUALS.equals(Signature.of(method))) {
+            return equalTo(args[0]);
+        }
+        return method.invoke(this, args);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Class<? extends Annotation> annotationType() {
+        return annotationType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return values.entrySet().stream()
+            .map(e -> String.format("%s=%s", e.getKey(), 
StringUtils.valueOf(e.getValue())))
+            .collect(Collectors.joining(", ", String.format("@%s(", 
annotationType().getName()), ")"));
+    }
+
+    @Override
+    public int hashCode() {
+        return values.entrySet().stream().mapToInt(e -> {
+            return (127 * e.getKey().hashCode()) ^ 
ObjectUtils.hashCode(e.getValue());
+        }).sum();
+    }
+
+    private boolean equalTo(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof Annotation) {
+            final Annotation other = (Annotation) obj;
+            return other.annotationType().equals(annotationType)
+                && values.entrySet().stream().allMatch(e -> 
memberEquals(other, e.getKey(), e.getValue()));
+        }
+        return false;
+    }
+
+    private boolean memberEquals(Annotation other, String name, Object value) {
+        final Method member = Reflection.getDeclaredMethod(annotationType, 
name);
+        final Object otherValue;
+        try {
+            otherValue = member.invoke(other);
+        } catch (IllegalAccessException | IllegalArgumentException | 
InvocationTargetException e) {
+            throw new IllegalStateException(e);
+        }
+        Exceptions.raiseIf(otherValue == null || 
!otherValue.getClass().equals(value.getClass()),
+            IllegalStateException::new, "Unexpected value %s for member %s of 
%s", otherValue, name, other);
+
+        if (value instanceof Object[]) {
+            return Arrays.equals((Object[]) value, (Object[]) otherValue);
+        }
+        if (value instanceof byte[]) {
+            return Arrays.equals((byte[]) value, (byte[]) otherValue);
+        }
+        if (value instanceof short[]) {
+            return Arrays.equals((short[]) value, (short[]) otherValue);
+        }
+        if (value instanceof int[]) {
+            return Arrays.equals((int[]) value, (int[]) otherValue);
+        }
+        if (value instanceof char[]) {
+            return Arrays.equals((char[]) value, (char[]) otherValue);
+        }
+        if (value instanceof long[]) {
+            return Arrays.equals((long[]) value, (long[]) otherValue);
+        }
+        if (value instanceof float[]) {
+            return Arrays.equals((float[]) value, (float[]) otherValue);
+        }
+        if (value instanceof double[]) {
+            return Arrays.equals((double[]) value, (double[]) otherValue);
+        }
+        if (value instanceof boolean[]) {
+            return Arrays.equals((boolean[]) value, (boolean[]) otherValue);
+        }
+        return value.equals(otherValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java
new file mode 100644
index 0000000..ee9a139
--- /dev/null
+++ 
b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java
@@ -0,0 +1,235 @@
+/*
+ *  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.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.validation.ConstraintTarget;
+import javax.validation.Payload;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.groups.ConvertGroup;
+
+import org.apache.bval.cdi.EmptyAnnotationLiteral;
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privileged;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+/**
+ * Description: Holds the information and creates an annotation proxy during 
xml
+ * parsing of validation mapping constraints. <br/>
+ */
+@Privilizing(@CallTo(Reflection.class))
+public final class AnnotationProxyBuilder<A extends Annotation> {
+    private final Class<A> type;
+    private final Map<String, Object> elements = new HashMap<>();
+    private final Method[] methods;
+    private boolean changed;
+
+    /**
+     * Create a new AnnotationProxyBuilder instance.
+     *
+     * @param annotationType
+     * @param cache
+     */
+    AnnotationProxyBuilder(final Class<A> annotationType, Map<Class<?>, 
Method[]> cache) {
+        this.type = Validate.notNull(annotationType, "annotationType");
+        this.methods = Validate.notNull(cache, 
"cache").computeIfAbsent(annotationType, Reflection::getDeclaredMethods);
+    }
+
+    /**
+     * Create a builder initially configured to create an annotation equivalent
+     * to {@code annot}.
+     * 
+     * @param annot
+     *            Annotation to be replicated.
+     * @param cache
+     */
+    @SuppressWarnings("unchecked")
+    AnnotationProxyBuilder(A annot, Map<Class<?>, Method[]> cache) {
+        this((Class<A>) annot.annotationType(), cache);
+        elements.putAll(AnnotationsManager.readAttributes(annot));
+    }
+
+    public Method[] getMethods() {
+        return methods;
+    }
+
+    /**
+     * Add an element to the configuration.
+     *
+     * @param elementName
+     * @param value
+     * @return whether any change occurred
+     */
+    public boolean setValue(String elementName, Object value) {
+        final boolean result = !Objects.equals(elements.put(elementName, 
value), value);
+        changed |= result;
+        return result;
+    }
+
+    /**
+     * Get the specified element value from the current configuration.
+     *
+     * @param elementName
+     * @return Object value
+     */
+    public Object getValue(String elementName) {
+        return elements.get(elementName);
+    }
+
+    /**
+     * Learn whether a given element has been configured.
+     *
+     * @param elementName
+     * @return <code>true</code> if an <code>elementName</code> element is 
found
+     *         on this annotation
+     */
+    public boolean contains(String elementName) {
+        return elements.containsKey(elementName);
+    }
+
+    /**
+     * Get the number of configured elements.
+     *
+     * @return int
+     */
+    public int size() {
+        return elements.size();
+    }
+
+    /**
+     * Get the configured Annotation type.
+     *
+     * @return Class<A>
+     */
+    public Class<A> getType() {
+        return type;
+    }
+
+    /**
+     * Configure the well-known JSR303 "message" element.
+     *
+     * @param message
+     * @return
+     */
+    public boolean setMessage(String message) {
+        return 
setValue(ConstraintAnnotationAttributes.MESSAGE.getAttributeName(), message);
+    }
+
+    /**
+     * Configure the well-known JSR303 "groups" element.
+     *
+     * @param groups
+     * @return
+     */
+    public boolean setGroups(Class<?>[] groups) {
+        return 
setValue(ConstraintAnnotationAttributes.GROUPS.getAttributeName(), groups);
+    }
+
+    /**
+     * Configure the well-known JSR303 "payload" element.
+     * 
+     * @param payload
+     * @return
+     */
+    public boolean setPayload(Class<? extends Payload>[] payload) {
+        return 
setValue(ConstraintAnnotationAttributes.PAYLOAD.getAttributeName(), payload);
+    }
+
+    /**
+     * Configure the well-known "validationAppliesTo" element.
+     * 
+     * @param constraintTarget
+     */
+    public boolean setValidationAppliesTo(ConstraintTarget constraintTarget) {
+        return 
setValue(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName(),
 constraintTarget);
+    }
+
+    public boolean isChanged() {
+        return changed;
+    }
+
+    /**
+     * Create the annotation represented by this builder.
+     *
+     * @return {@link Annotation}
+     */
+    public A createAnnotation() {
+        final ClassLoader classLoader = 
Reflection.loaderFromClassOrThread(getType());
+        @SuppressWarnings("unchecked")
+        final Class<A> proxyClass = (Class<A>) 
Proxy.getProxyClass(classLoader, getType());
+        return doCreateAnnotation(proxyClass, new AnnotationProxy(this));
+    }
+
+    @Privileged
+    private A doCreateAnnotation(final Class<A> proxyClass, final 
InvocationHandler handler) {
+        try {
+            final Constructor<A> constructor = 
proxyClass.getConstructor(InvocationHandler.class);
+            final boolean mustUnset = Reflection.setAccessible(constructor, 
true); // java
+                                                                               
    // 8
+            try {
+                return constructor.newInstance(handler);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(constructor, false);
+                }
+            }
+        } catch (Exception e) {
+            throw new ValidationException("Unable to create annotation for 
configured constraint", e);
+        }
+    }
+
+    public static final class ValidAnnotation extends 
EmptyAnnotationLiteral<Valid> implements Valid {
+        private static final long serialVersionUID = 1L;
+
+        public static final ValidAnnotation INSTANCE = new ValidAnnotation();
+    }
+
+    public static final class ConvertGroupAnnotation extends 
AnnotationLiteral<ConvertGroup> implements ConvertGroup {
+        private static final long serialVersionUID = 1L;
+
+        private final Class<?> from;
+        private final Class<?> to;
+
+        public ConvertGroupAnnotation(final Class<?> from, final Class<?> to) {
+            this.from = from;
+            this.to = to;
+        }
+
+        @Override
+        public Class<?> from() {
+            return from;
+        }
+
+        @Override
+        public Class<?> to() {
+            return to;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
index ead0a3b..dd9c28b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
@@ -56,7 +56,6 @@ import org.apache.bval.jsr.ConstraintAnnotationAttributes;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes.Worker;
 import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
 import org.apache.bval.jsr.metadata.Meta;
-import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
 import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.Lazy;
 import org.apache.bval.util.ObjectUtils;
@@ -107,8 +106,8 @@ public class AnnotationsManager {
         }
     }
 
-    private static class Composition {
-        static <A extends Annotation> 
Optional<ConstraintAnnotationAttributes.Worker<A>> validWorker(
+    private class Composition {
+        <A extends Annotation> 
Optional<ConstraintAnnotationAttributes.Worker<A>> validWorker(
             ConstraintAnnotationAttributes attr, Class<A> type) {
             return 
Optional.of(type).map(attr::analyze).filter(Worker::isValid);
         }
@@ -179,7 +178,7 @@ public class AnnotationsManager {
                 final int index =
                     constraintCounts.computeIfAbsent(c.annotationType(), k -> 
new AtomicInteger()).getAndIncrement();
 
-                final AnnotationProxyBuilder<Annotation> proxyBuilder = new 
AnnotationProxyBuilder<>(c);
+                final AnnotationProxyBuilder<Annotation> proxyBuilder = 
buildProxyFor(c);
 
                 proxyBuilder.setGroups(groups);
                 proxyBuilder.setPayload(payload);
@@ -281,7 +280,6 @@ public class AnnotationsManager {
         }
         return constraints.toArray(Annotation[]::new);
     }
-    
 
     private static Optional<AnnotatedElement> substitute(AnnotatedElement e) {
         if (e instanceof Parameter) {
@@ -310,19 +308,23 @@ public class AnnotationsManager {
 
     private final ApacheValidatorFactory validatorFactory;
     private final LRUCache<Class<? extends Annotation>, Composition> 
compositions;
+    private final LRUCache<Class<? extends Annotation>, Method[]> 
constraintAttributes;
 
     public AnnotationsManager(ApacheValidatorFactory validatorFactory) {
         super();
         this.validatorFactory = Validate.notNull(validatorFactory);
         final String cacheSize =
             
validatorFactory.getProperties().get(ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+        final int sz;
         try {
-            compositions = new LRUCache<>(Integer.parseInt(cacheSize));
+            sz = Integer.parseInt(cacheSize);
         } catch (NumberFormatException e) {
             throw Exceptions.create(IllegalStateException::new, e,
                 "Cannot parse value %s for configuration property %s", 
cacheSize,
                 ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
         }
+        compositions = new LRUCache<>(sz);
+        constraintAttributes = new LRUCache<>(sz);
     }
 
     public void validateConstraintDefinition(Class<? extends Annotation> type) 
{
@@ -414,6 +416,16 @@ public class AnnotationsManager {
             .collect(Collectors.toCollection(() -> 
EnumSet.noneOf(ValidationTarget.class)));
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <A extends Annotation> AnnotationProxyBuilder<A> 
buildProxyFor(Class<A> type) {
+        return new AnnotationProxyBuilder<>(type, (Map) constraintAttributes);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <A extends Annotation> AnnotationProxyBuilder<A> buildProxyFor(A 
instance) {
+        return new AnnotationProxyBuilder<>(instance, (Map) 
constraintAttributes);
+    }
+
     private Composition getComposition(Class<? extends Annotation> 
annotationType) {
         return compositions.computeIfAbsent(annotationType, ct -> {
             final Set<ValidationTarget> composedTargets = 
supportedTargets(annotationType);

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
deleted file mode 100644
index 44d67b8..0000000
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
+++ /dev/null
@@ -1,169 +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.xml;
-
-import java.io.Serializable;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-
-import javax.validation.Valid;
-
-import org.apache.bval.jsr.metadata.Signature;
-import org.apache.bval.util.Exceptions;
-import org.apache.bval.util.ObjectUtils;
-import org.apache.bval.util.StringUtils;
-import org.apache.bval.util.reflection.Reflection;
-
-/**
- * Description: <br/>
- * InvocationHandler implementation of <code>Annotation</code> that pretends 
it is a "real" source code annotation.
- * <p/>
- */
-class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
-
-    /** Serialization version */
-    private static final long serialVersionUID = 1L;
-    
-    private Signature EQUALS = new Signature("equals", Object.class);
-
-    private final Class<? extends Annotation> annotationType;
-    private final SortedMap<String, Object> values;
-
-    /**
-     * Create a new AnnotationProxy instance.
-     * 
-     * @param <A>
-     * @param descriptor
-     */
-    <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> 
descriptor) {
-        this.annotationType = descriptor.getType();
-        values = new TreeMap<>();
-        int processedValuesFromDescriptor = 0;
-        for (final Method m : descriptor.getMethods()) {
-            if (descriptor.contains(m.getName())) {
-                values.put(m.getName(), descriptor.getValue(m.getName()));
-                processedValuesFromDescriptor++;
-            } else {
-                if (m.getDefaultValue() == null) {
-                    Exceptions.raise(IllegalArgumentException::new, "No value 
provided for %s", m.getName());
-                }
-                values.put(m.getName(), m.getDefaultValue());
-            }
-        }
-        Exceptions.raiseUnless(processedValuesFromDescriptor == 
descriptor.size() || Valid.class.equals(annotationType),
-            IllegalArgumentException::new, "Trying to instantiate %s with 
unknown parameters.",
-            f -> f.args(annotationType.getName()));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws 
Throwable {
-        if (values.containsKey(method.getName())) {
-            return values.get(method.getName());
-        }
-        if (EQUALS.equals(Signature.of(method))) {
-            return equalTo(args[0]);
-        }
-        return method.invoke(this, args);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Class<? extends Annotation> annotationType() {
-        return annotationType;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return values.entrySet().stream()
-            .map(e -> String.format("%s=%s", e.getKey(), 
StringUtils.valueOf(e.getValue())))
-            .collect(Collectors.joining(", ", String.format("@%s(", 
annotationType().getName()), ")"));
-    }
-
-    @Override
-    public int hashCode() {
-        return values.entrySet().stream().mapToInt(e -> {
-            return (127 * e.getKey().hashCode()) ^ 
ObjectUtils.hashCode(e.getValue());
-        }).sum();
-    }
-
-    private boolean equalTo(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj instanceof Annotation) {
-            final Annotation other = (Annotation) obj;
-            return other.annotationType().equals(annotationType)
-                && values.entrySet().stream().allMatch(e -> 
memberEquals(other, e.getKey(), e.getValue()));
-        }
-        return false;
-    }
-
-    private boolean memberEquals(Annotation other, String name, Object value) {
-        final Method member = Reflection.getDeclaredMethod(annotationType, 
name);
-        final Object otherValue;
-        try {
-            otherValue = member.invoke(other);
-        } catch (IllegalAccessException | IllegalArgumentException | 
InvocationTargetException e) {
-            throw new IllegalStateException(e);
-        }
-        Exceptions.raiseIf(otherValue == null || 
!otherValue.getClass().equals(value.getClass()),
-            IllegalStateException::new, "Unexpected value %s for member %s of 
%s", otherValue, name, other);
-
-        if (value instanceof Object[]) {
-            return Arrays.equals((Object[]) value, (Object[]) otherValue);
-        }
-        if (value instanceof byte[]) {
-            return Arrays.equals((byte[]) value, (byte[]) otherValue);
-        }
-        if (value instanceof short[]) {
-            return Arrays.equals((short[]) value, (short[]) otherValue);
-        }
-        if (value instanceof int[]) {
-            return Arrays.equals((int[]) value, (int[]) otherValue);
-        }
-        if (value instanceof char[]) {
-            return Arrays.equals((char[]) value, (char[]) otherValue);
-        }
-        if (value instanceof long[]) {
-            return Arrays.equals((long[]) value, (long[]) otherValue);
-        }
-        if (value instanceof float[]) {
-            return Arrays.equals((float[]) value, (float[]) otherValue);
-        }
-        if (value instanceof double[]) {
-            return Arrays.equals((double[]) value, (double[]) otherValue);
-        }
-        if (value instanceof boolean[]) {
-            return Arrays.equals((boolean[]) value, (boolean[]) otherValue);
-        }
-        return value.equals(otherValue);
-    }
-}

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
deleted file mode 100644
index dd27560..0000000
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
+++ /dev/null
@@ -1,257 +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.xml;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.enterprise.util.AnnotationLiteral;
-import javax.validation.ConstraintTarget;
-import javax.validation.Payload;
-import javax.validation.Valid;
-import javax.validation.ValidationException;
-import javax.validation.groups.ConvertGroup;
-
-import org.apache.bval.cdi.EmptyAnnotationLiteral;
-import org.apache.bval.jsr.ConstraintAnnotationAttributes;
-import org.apache.bval.jsr.util.AnnotationsManager;
-import org.apache.bval.util.reflection.Reflection;
-import org.apache.commons.weaver.privilizer.Privileged;
-import org.apache.commons.weaver.privilizer.Privilizing;
-import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-
-/**
- * Description: Holds the information and creates an annotation proxy during 
xml
- * parsing of validation mapping constraints. <br/>
- */
-@Privilizing(@CallTo(Reflection.class))
-public final class AnnotationProxyBuilder<A extends Annotation> {
-    private static final ConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new 
ConcurrentHashMap<>();
-
-    public static <A> Method[] findMethods(final Class<A> annotationType) {
-        // cache only built-in constraints to avoid memory leaks:
-        // TODO use configurable cache size property?
-        if 
(annotationType.getName().startsWith("javax.validation.constraints.")) {
-            return METHODS_CACHE.computeIfAbsent(annotationType, 
Reflection::getDeclaredMethods);
-        }
-        return Reflection.getDeclaredMethods(annotationType);
-    }
-
-    private final Class<A> type;
-    private final Map<String, Object> elements = new HashMap<>();
-    private final Method[] methods;
-    private boolean changed;
-
-    /**
-     * Create a new AnnotationProxyBuilder instance.
-     *
-     * @param annotationType
-     */
-    public AnnotationProxyBuilder(final Class<A> annotationType) {
-        this.type = annotationType;
-        this.methods = findMethods(annotationType);
-    }
-
-    /**
-     * Create a new AnnotationProxyBuilder instance.
-     *
-     * @param annotationType
-     * @param elements
-     */
-    public AnnotationProxyBuilder(Class<A> annotationType, Map<String, Object> 
elements) {
-        this(annotationType);
-        elements.forEach(this.elements::put);
-    }
-
-    /**
-     * Create a builder initially configured to create an annotation equivalent
-     * to {@code annot}.
-     * 
-     * @param annot
-     *            Annotation to be replicated.
-     */
-    @SuppressWarnings("unchecked")
-    public AnnotationProxyBuilder(A annot) {
-        this((Class<A>) annot.annotationType());
-        elements.putAll(AnnotationsManager.readAttributes(annot));
-    }
-
-    public Method[] getMethods() {
-        return methods;
-    }
-
-    /**
-     * Add an element to the configuration.
-     *
-     * @param elementName
-     * @param value
-     * @return whether any change occurred
-     */
-    public boolean setValue(String elementName, Object value) {
-        final boolean result = !Objects.equals(elements.put(elementName, 
value), value);
-        changed |= result;
-        return result;
-    }
-
-    /**
-     * Get the specified element value from the current configuration.
-     *
-     * @param elementName
-     * @return Object value
-     */
-    public Object getValue(String elementName) {
-        return elements.get(elementName);
-    }
-
-    /**
-     * Learn whether a given element has been configured.
-     *
-     * @param elementName
-     * @return <code>true</code> if an <code>elementName</code> element is 
found
-     *         on this annotation
-     */
-    public boolean contains(String elementName) {
-        return elements.containsKey(elementName);
-    }
-
-    /**
-     * Get the number of configured elements.
-     *
-     * @return int
-     */
-    public int size() {
-        return elements.size();
-    }
-
-    /**
-     * Get the configured Annotation type.
-     *
-     * @return Class<A>
-     */
-    public Class<A> getType() {
-        return type;
-    }
-
-    /**
-     * Configure the well-known JSR303 "message" element.
-     *
-     * @param message
-     * @return
-     */
-    public boolean setMessage(String message) {
-        return 
setValue(ConstraintAnnotationAttributes.MESSAGE.getAttributeName(), message);
-    }
-
-    /**
-     * Configure the well-known JSR303 "groups" element.
-     *
-     * @param groups
-     * @return
-     */
-    public boolean setGroups(Class<?>[] groups) {
-        return 
setValue(ConstraintAnnotationAttributes.GROUPS.getAttributeName(), groups);
-    }
-
-    /**
-     * Configure the well-known JSR303 "payload" element.
-     * 
-     * @param payload
-     * @return
-     */
-    public boolean setPayload(Class<? extends Payload>[] payload) {
-        return 
setValue(ConstraintAnnotationAttributes.PAYLOAD.getAttributeName(), payload);
-    }
-
-    /**
-     * Configure the well-known "validationAppliesTo" element.
-     * 
-     * @param constraintTarget
-     */
-    public boolean setValidationAppliesTo(ConstraintTarget constraintTarget) {
-        return 
setValue(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName(),
 constraintTarget);
-    }
-
-    public boolean isChanged() {
-        return changed;
-    }
-
-    /**
-     * Create the annotation represented by this builder.
-     *
-     * @return {@link Annotation}
-     */
-    public A createAnnotation() {
-        final ClassLoader classLoader = 
Reflection.loaderFromClassOrThread(getType());
-        @SuppressWarnings("unchecked")
-        final Class<A> proxyClass = (Class<A>) 
Proxy.getProxyClass(classLoader, getType());
-        return doCreateAnnotation(proxyClass, new AnnotationProxy(this));
-    }
-
-    @Privileged
-    private A doCreateAnnotation(final Class<A> proxyClass, final 
InvocationHandler handler) {
-        try {
-            final Constructor<A> constructor = 
proxyClass.getConstructor(InvocationHandler.class);
-            final boolean mustUnset = Reflection.setAccessible(constructor, 
true); // java
-                                                                               
    // 8
-            try {
-                return constructor.newInstance(handler);
-            } finally {
-                if (mustUnset) {
-                    Reflection.setAccessible(constructor, false);
-                }
-            }
-        } catch (Exception e) {
-            throw new ValidationException("Unable to create annotation for 
configured constraint", e);
-        }
-    }
-
-    public static final class ValidAnnotation extends 
EmptyAnnotationLiteral<Valid> implements Valid {
-        private static final long serialVersionUID = 1L;
-
-        public static final ValidAnnotation INSTANCE = new ValidAnnotation();
-    }
-
-    public static final class ConvertGroupAnnotation extends 
AnnotationLiteral<ConvertGroup> implements ConvertGroup {
-        private static final long serialVersionUID = 1L;
-
-        private final Class<?> from;
-        private final Class<?> to;
-
-        public ConvertGroupAnnotation(final Class<?> from, final Class<?> to) {
-            this.from = from;
-            this.to = to;
-        }
-
-        @Override
-        public Class<?> from() {
-            return from;
-        }
-
-        @Override
-        public Class<?> to() {
-            return to;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/bval/blob/1d54c14c/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
----------------------------------------------------------------------
diff --git 
a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java 
b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
index 59cef76..af80d00 100644
--- 
a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
+++ 
b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
@@ -30,8 +30,10 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
 import javax.validation.spi.ConfigurationState;
 
+import org.apache.bval.jsr.ApacheValidatorFactory;
 import org.apache.bval.jsr.metadata.MetadataBuilder;
 import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean;
 import org.apache.bval.jsr.metadata.MetadataSource;
@@ -39,6 +41,7 @@ import org.apache.bval.jsr.metadata.ValidatorMappingProvider;
 import org.apache.bval.jsr.metadata.XmlBuilder;
 import org.apache.bval.jsr.metadata.XmlValidationMappingProvider;
 import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.commons.weaver.privilizer.Privilizing;
 import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
@@ -48,7 +51,7 @@ import org.xml.sax.InputSource;
  * Uses JAXB to parse constraints.xml based on the validation-mapping XML 
schema.
  */
 @Privilizing(@CallTo(Reflection.class))
-public class ValidationMappingParser implements MetadataSource {
+public class ValidationMappingParser implements 
MetadataSource.FactoryDependent {
     private static final SchemaManager SCHEMA_MANAGER = new 
SchemaManager.Builder()
         .add(XmlBuilder.Version.v10.getId(), 
"http://jboss.org/xml/ns/javax/validation/mapping";,
             "META-INF/validation-mapping-1.0.xsd")
@@ -58,9 +61,18 @@ public class ValidationMappingParser implements 
MetadataSource {
             "META-INF/validation-mapping-2.0.xsd")
         .build();
 
+    private ApacheValidatorFactory validatorFactory;
+
+    @Override
+    public void setFactory(ValidatorFactory validatorFactory) {
+        this.validatorFactory = 
Validate.notNull(validatorFactory).unwrap(ApacheValidatorFactory.class);
+    }
+
     @Override
     public void process(ConfigurationState configurationState,
         Consumer<ValidatorMappingProvider> addValidatorMappingProvider, 
BiConsumer<Class<?>, ForBean<?>> addBuilder) {
+        Validate.validState(validatorFactory != null, "validatorFactory 
unknown");
+
         if (configurationState.isIgnoreXmlConfiguration()) {
             return;
         }
@@ -70,7 +82,8 @@ public class ValidationMappingParser implements 
MetadataSource {
 
             
Optional.of(mapping).map(this::toMappingProvider).ifPresent(addValidatorMappingProvider);
 
-            final Map<Class<?>, MetadataBuilder.ForBean<?>> builders = new 
XmlBuilder(mapping).forBeans();
+            final Map<Class<?>, MetadataBuilder.ForBean<?>> builders =
+                new XmlBuilder(validatorFactory, mapping).forBeans();
             if (Collections.disjoint(beanTypes, builders.keySet())) {
                 builders.forEach(addBuilder::accept);
                 beanTypes.addAll(builders.keySet());

Reply via email to