BVAL-172 more thread safety in our caches
diff --git a/bval-jsr/pom.xml b/bval-jsr/pom.xml
index 1405487..b74bf1b 100644
--- a/bval-jsr/pom.xml
+++ b/bval-jsr/pom.xml
@@ -37,6 +37,33 @@
<jaxb.version>2.2.6</jaxb.version>
</properties>
<profiles>
+
+ <profile>
+ <id>java11</id>
+ <activation>
+ <jdk>11</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ <version>2.3.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-core</artifactId>
+ <version>2.3.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-base</artifactId>
+ <version>11</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
<!--
default profile using geronimo-validation_2.0_spec.jar active when
property "ri" is not present.
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
index 9622b9e..7af0488 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
@@ -23,9 +23,7 @@
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;
@@ -99,8 +97,8 @@
}
}
- private final Map<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo =
- new HashMap<>();
+ private final ConcurrentMap<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo =
+ new ConcurrentHashMap<>();
private final ConcurrentMap<ConstraintD<?>, ConstraintValidator<?, ?>> validators =
new ConcurrentHashMap<>();
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
index 117e870..ebb3c33 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
@@ -39,10 +39,14 @@
protected CascadableContainerD(MetadataReader.ForContainer<E> reader, P parent) {
super(reader, parent);
- cascaded = reader.isCascaded();
- groupConversions = reader.getGroupConversions();
- containerElementTypes = reader.getContainerElementTypes(this).stream().filter(DescriptorManager::isConstrained)
- .collect(ToUnmodifiable.set());
+ synchronized (reader.meta.getHost()) { // cache is not thread safe for runtime perf so ensure we lock properly
+ cascaded = reader.isCascaded();
+ groupConversions = reader.getGroupConversions();
+ containerElementTypes = reader.getContainerElementTypes(this)
+ .stream()
+ .filter(DescriptorManager::isConstrained)
+ .collect(ToUnmodifiable.set());
+ }
}
@Override
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
index da84490..e9209fb 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
@@ -334,8 +334,10 @@
return (MetadataBuilder.ForBean<T>) delegates.get(0);
}
// pretend:
- return delegates.stream().<MetadataBuilder.ForBean<T>> map(MetadataBuilder.ForBean.class::cast)
- .collect(compose());
+ // note: stream split for java 11 compilation
+ final Stream<MetadataBuilder.ForBean<T>> forBeanStream = delegates.stream()
+ .map(MetadataBuilder.ForBean.class::cast);
+ return forBeanStream.collect(compose());
}
@Override
@@ -345,7 +347,7 @@
@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).orElse(meta);
+ .map(HierarchyDelegate::getHierarchyElement).map(Meta.class::cast).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));
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
index ee9a139..cfa4c48 100644
--- 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
@@ -59,7 +59,9 @@
*/
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);
+ synchronized (annotationType) { // cache is not thread safe generally
+ this.methods = Validate.notNull(cache, "cache").computeIfAbsent(annotationType, Reflection::getDeclaredMethods);
+ }
}
/**
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
index 48fcd7d..112ddf2 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
@@ -35,7 +35,7 @@
}
@Override
- protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { // todo: synchronize?
return super.removeEldestEntry(eldest) || size() >= maximumCapacity;
}
}