You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2019/05/14 07:21:07 UTC

svn commit: r1859211 - in /tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval: cdi/ jsr/ jsr/descriptor/ jsr/metadata/ jsr/util/

Author: dblevins
Date: Tue May 14 07:21:07 2019
New Revision: 1859211

URL: http://svn.apache.org/viewvc?rev=1859211&view=rev
Log:
Back up to BVal revision 8b683b91d2b236f322245be7a68b63c3bfbcdbf0
Subsequent revision introduces a potential regression
Details in https://issues.apache.org/jira/browse/BVAL-175

Added:
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
Modified:
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java
    tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java Tue May 14 07:21:07 2019
@@ -18,22 +18,18 @@
  */
 package org.apache.bval.cdi;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.stream.Stream;
 
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
-import javax.enterprise.inject.spi.AfterDeploymentValidation;
-import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.AnnotatedCallable;
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
@@ -45,14 +41,14 @@ import javax.enterprise.inject.spi.Proce
 import javax.enterprise.inject.spi.ProcessBean;
 import javax.validation.BootstrapConfiguration;
 import javax.validation.Configuration;
-import javax.validation.Constraint;
-import javax.validation.Valid;
 import javax.validation.Validation;
 import javax.validation.ValidationException;
 import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
 import javax.validation.executable.ExecutableType;
 import javax.validation.executable.ValidateOnExecution;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.MethodType;
 
 import org.apache.bval.jsr.ConfigurationImpl;
 import org.apache.bval.jsr.util.ExecutableTypes;
@@ -79,13 +75,11 @@ public class BValExtension implements Ex
 
     private final Configuration<?> config;
     private Lazy<ValidatorFactory> factory;
+    private Lazy<Validator> validator;
 
     private Set<ExecutableType> globalExecutableTypes;
     private boolean isExecutableValidationEnabled;
 
-    private final Collection<Class<?>> potentiallyBValAnnotation = new HashSet<>();
-    private final Collection<Class<?>> notBValAnnotation = new HashSet<>();
-
     public BValExtension() { // read the config, could be done in a quicker way but this let us get defaults without duplicating code
         config = Validation.byDefaultProvider().configure();
         try {
@@ -103,6 +97,21 @@ public class BValExtension implements Ex
         }
     }
 
+    // lazily to get a small luck to have CDI in place
+    private void ensureFactoryValidator() {
+        if (validator != null) {
+            return;
+        }
+        if (config instanceof ConfigurationImpl) {
+            // ignore parts of the config relying on CDI since we didn't start yet
+            ((ConfigurationImpl) config).deferBootstrapOverrides();
+        }
+        if (factory == null) {
+            factory = new Lazy<>(config::buildValidatorFactory);
+        }
+        validator = new Lazy<>(() -> factory.get().getValidator());
+    }
+
     public Set<ExecutableType> getGlobalExecutableTypes() {
         return globalExecutableTypes;
     }
@@ -124,27 +133,43 @@ public class BValExtension implements Ex
         }
         final Class<A> javaClass = annotatedType.getJavaClass();
         final int modifiers = javaClass.getModifiers();
-        if (!javaClass.isInterface() && !javaClass.isAnonymousClass() && !Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) {
+        if (!javaClass.isInterface() && !Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) {
             try {
-                if (hasValidation(annotatedType)
-                    || hasValidationAnnotation(annotatedType.getMethods())
-                    || hasValidationAnnotation(annotatedType.getConstructors())
-                    || Stream.concat(annotatedType.getMethods().stream(), annotatedType.getConstructors().stream())
-                        .flatMap(it -> it.getParameters().stream())
-                        .anyMatch(this::hasValidation)) {
-                    pat.setAnnotatedType(new BValAnnotatedType<>(annotatedType));
+                ensureFactoryValidator();
+                try {
+                    final BeanDescriptor classConstraints = validator.get().getConstraintsForClass(javaClass);
+
+                    final boolean validConstructors = globalExecutableTypes.contains(ExecutableType.CONSTRUCTORS)
+                        && !classConstraints.getConstrainedConstructors().isEmpty();
+                    final boolean validBusinessMethods =
+                        globalExecutableTypes.contains(ExecutableType.NON_GETTER_METHODS)
+                            && !classConstraints.getConstrainedMethods(MethodType.NON_GETTER).isEmpty();
+                    final boolean validGetterMethods = globalExecutableTypes.contains(ExecutableType.GETTER_METHODS)
+                        && !classConstraints.getConstrainedMethods(MethodType.GETTER).isEmpty();
+
+                    if (annotatedType.isAnnotationPresent(ValidateOnExecution.class)
+                        || hasValidationAnnotation(annotatedType.getMethods())
+                        || hasValidationAnnotation(annotatedType.getConstructors()) || validConstructors
+                        || validBusinessMethods || validGetterMethods) {
+                        pat.setAnnotatedType(new BValAnnotatedType<>(annotatedType));
+                    }
+                } catch (final NoClassDefFoundError ncdfe) {
+                    // skip
                 }
             } catch (final Exception e) {
                 if (e instanceof ValidationException) {
                     throw e;
                 }
                 LOGGER.log(Level.INFO, e.getMessage());
-            } catch (final NoClassDefFoundError ncdfe) {
-                // skip
             }
         }
     }
 
+    private static <A> boolean hasValidationAnnotation(
+        final Collection<? extends AnnotatedCallable<? super A>> methods) {
+        return methods.stream().anyMatch(m -> m.isAnnotationPresent(ValidateOnExecution.class));
+    }
+
     public <A> void processBean(final @Observes ProcessBean<A> processBeanEvent) {
         if (validatorFound && validatorFactoryFound) {
             return;
@@ -194,73 +219,6 @@ public class BValExtension implements Ex
         }
     }
 
-    public void afterStart(@Observes final AfterDeploymentValidation clearEvent) {
-        potentiallyBValAnnotation.clear();
-        notBValAnnotation.clear();
-    }
-
-    private boolean hasValidationAnnotation(final Collection<? extends Annotated> annotateds) {
-        return annotateds.stream().anyMatch(this::hasValidation);
-    }
-
-    private boolean hasValidation(final Annotated m) {
-        return m.getAnnotations().stream()
-                .anyMatch(it -> {
-                    final Class<? extends Annotation> type = it.annotationType();
-                    if (type == ValidateOnExecution.class || type == Valid.class) {
-                        return true;
-                    }
-                    if (isSkippedAnnotation(type)) {
-                        return false;
-                    }
-                    if (type.getName().startsWith("javax.validation.constraints")) {
-                        return true;
-                    }
-                    if (notBValAnnotation.contains(type)) { // more likely so faster first
-                        return false;
-                    }
-                    if (potentiallyBValAnnotation.contains(type)) {
-                        return true;
-                    }
-                    cacheIsBvalAnnotation(type);
-                    return potentiallyBValAnnotation.contains(type);
-                });
-    }
-
-    private boolean isSkippedAnnotation(final Class<? extends Annotation> type) {
-        if (type.getName().startsWith("java.")) {
-            return true;
-        }
-        if (type.getName().startsWith("javax.enterprise.")) {
-            return true;
-        }
-        if (type.getName().startsWith("javax.inject.")) {
-            return true;
-        }
-        return false;
-    }
-
-    private void cacheIsBvalAnnotation(final Class<? extends Annotation> type) {
-        if (flattenAnnotations(type, new HashSet<>()).anyMatch(it -> it == Constraint.class)) {
-            potentiallyBValAnnotation.add(type);
-        } else {
-            notBValAnnotation.add(type);
-        }
-    }
-
-    private Stream<Class<?>> flattenAnnotations(final Class<? extends Annotation> type, final Set<Class<?>> seen) {
-        seen.add(type);
-        return Stream.of(type)
-                     .flatMap(it -> Stream.concat(
-                             Stream.of(it),
-                             Stream.of(it.getAnnotations())
-                                   .map(Annotation::annotationType)
-                                   .distinct()
-                                   .filter(a -> !isSkippedAnnotation(a))
-                                   .filter(seen::add)
-                                   .flatMap(a -> flattenAnnotations(a, seen))));
-    }
-
     /**
      * Request that an instance of the specified type be provided by the container.
      * @param clazz

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java Tue May 14 07:21:07 2019
@@ -46,6 +46,11 @@ public interface ApacheValidatorConfigur
         String VALIDATOR_FACTORY_CLASSNAME = "apache.bval.validator-factory-classname";
 
         /**
+         * Size to use for caching of constraint-related information. Default is {@code 50}.
+         */
+        String CONSTRAINTS_CACHE_SIZE = "apache.bval.constraints-cache-size";
+
+        /**
          * Specifies whether EL evaluation is permitted in non-default message
          * templates. By default this feature is disabled; if you enable it you
          * should ensure that no constraint validator builds violations using

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java Tue May 14 07:21:07 2019
@@ -179,6 +179,7 @@ public class ConfigurationImpl implement
             this.provider = aProvider;
             this.providerResolver = null;
         }
+        initializePropertyDefaults();
     }
 
     /**
@@ -465,6 +466,10 @@ public class ConfigurationImpl implement
         }
     }
 
+    private void initializePropertyDefaults() {
+        properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50));
+    }
+
     private ValidationProvider<?> findProvider() {
         if (providerClass == null) {
             return providerResolver.getValidationProviders().get(0);

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java Tue May 14 07:21:07 2019
@@ -23,7 +23,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -97,8 +99,8 @@ public class ConstraintCached {
         }
     }
 
-    private final ConcurrentMap<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo =
-        new ConcurrentHashMap<>();
+    private final Map<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo =
+        new HashMap<>();
     private final ConcurrentMap<ConstraintD<?>, ConstraintValidator<?, ?>> validators =
         new ConcurrentHashMap<>();
 

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java Tue May 14 07:21:07 2019
@@ -41,10 +41,8 @@ public abstract class CascadableContaine
         super(reader, parent);
         cascaded = reader.isCascaded();
         groupConversions = reader.getGroupConversions();
-        containerElementTypes = reader.getContainerElementTypes(this)
-                                      .stream()
-                                      .filter(DescriptorManager::isConstrained)
-                                      .collect(ToUnmodifiable.set());
+        containerElementTypes = reader.getContainerElementTypes(this).stream().filter(DescriptorManager::isConstrained)
+                .collect(ToUnmodifiable.set());
     }
 
     @Override

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java Tue May 14 07:21:07 2019
@@ -334,10 +334,8 @@ public class HierarchyBuilder extends Co
             return (MetadataBuilder.ForBean<T>) delegates.get(0);
         }
         // pretend:
-        // note: stream split for java 11 compilation
-        final Stream<MetadataBuilder.ForBean<T>> forBeanStream = delegates.stream()
-                .map(MetadataBuilder.ForBean.class::cast);
-        return forBeanStream.collect(compose());
+        return delegates.stream().<MetadataBuilder.ForBean<T>> map(MetadataBuilder.ForBean.class::cast)
+            .collect(compose());
     }
 
     @Override
@@ -347,7 +345,7 @@ public class HierarchyBuilder extends Co
         @SuppressWarnings("unchecked")
         final Function<MetadataBuilder.ForElement<E>, Meta<E>> keyMapper =
             d -> Optional.of(d).filter(HierarchyDelegate.class::isInstance).map(HierarchyDelegate.class::cast)
-                .map(HierarchyDelegate::getHierarchyElement).map(Meta.class::cast).orElse(meta);
+                .map(HierarchyDelegate::getHierarchyElement).orElse(meta);
 
         return composite.delegates.stream().collect(Collectors.toMap(keyMapper, d -> d.getDeclaredConstraints(meta),
             (u, v) -> Stream.of(u, v).flatMap(Stream::of).toArray(Annotation[]::new), LinkedHashMap::new));

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationProxyBuilder.java Tue May 14 07:21:07 2019
@@ -24,7 +24,6 @@ import java.lang.reflect.Proxy;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.ConcurrentMap;
 
 import javax.enterprise.util.AnnotationLiteral;
 import javax.validation.ConstraintTarget;
@@ -58,7 +57,7 @@ public final class AnnotationProxyBuilde
      * @param annotationType
      * @param cache
      */
-    AnnotationProxyBuilder(final Class<A> annotationType, ConcurrentMap<Class<?>, Method[]> 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);
     }
@@ -72,7 +71,7 @@ public final class AnnotationProxyBuilde
      * @param cache
      */
     @SuppressWarnings("unchecked")
-    AnnotationProxyBuilder(A annot, ConcurrentMap<Class<?>, Method[]> cache) {
+    AnnotationProxyBuilder(A annot, Map<Class<?>, Method[]> cache) {
         this((Class<A>) annot.annotationType(), cache);
         elements.putAll(AnnotationsManager.readAttributes(annot));
     }

Modified: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java?rev=1859211&r1=1859210&r2=1859211&view=diff
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java (original)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java Tue May 14 07:21:07 2019
@@ -36,8 +36,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -53,6 +51,7 @@ import javax.validation.ValidationExcept
 import javax.validation.constraintvalidation.ValidationTarget;
 
 import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.ConfigurationImpl;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes;
 import org.apache.bval.jsr.ConstraintAnnotationAttributes.Worker;
 import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
@@ -308,14 +307,24 @@ public class AnnotationsManager {
     }
 
     private final ApacheValidatorFactory validatorFactory;
-    private final ConcurrentMap<Class<?>, Composition> compositions;
-    private final ConcurrentMap<Class<?>, Method[]> constraintAttributes;
+    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);
-        compositions = new ConcurrentHashMap<>();
-        constraintAttributes = new ConcurrentHashMap<>();
+        final String cacheSize =
+            validatorFactory.getProperties().get(ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+        final int sz;
+        try {
+            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) {
@@ -409,12 +418,12 @@ public class AnnotationsManager {
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     public <A extends Annotation> AnnotationProxyBuilder<A> buildProxyFor(Class<A> type) {
-        return new AnnotationProxyBuilder<>(type, constraintAttributes);
+        return new AnnotationProxyBuilder<>(type, (Map) constraintAttributes);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     public <A extends Annotation> AnnotationProxyBuilder<A> buildProxyFor(A instance) {
-        return new AnnotationProxyBuilder<>(instance, constraintAttributes);
+        return new AnnotationProxyBuilder<>(instance, (Map) constraintAttributes);
     }
 
     private Composition getComposition(Class<? extends Annotation> annotationType) {

Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java?rev=1859211&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java Tue May 14 07:21:07 2019
@@ -0,0 +1,41 @@
+/*
+ * 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.util.LinkedHashMap;
+import java.util.Map;
+
+public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+    private static final long serialVersionUID = 1L;
+
+    private final int maximumCapacity;
+
+    public LRUCache(int maximumCapacity) {
+        super(16, 0.75f, true);
+        if (maximumCapacity < 1) {
+            throw new IllegalArgumentException("maximumCapacity must be > 0");
+        }
+        this.maximumCapacity = maximumCapacity;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+        return super.removeEldestEntry(eldest) || size() >= maximumCapacity;
+    }
+}