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;
+ }
+}