DATACMNS-518 - Avoid potential infinite loops in PreferredConstructor.

We now synchronize the (seldom) writes to the isPropertyParameterCache HashMap in PreferredConstructor via a ReadWriteLock. We could as well have used a ConcurrentHashMap here without the need for manual locking but this would potentially waste memory for a mostly read-only data structure. We also anticipate potential multiple writes for the same property.

Original pull request: #86.
This commit is contained in:
Thomas Darimont
2014-06-12 12:44:29 +02:00
committed by Oliver Gierke
parent a885b57dbf
commit 2586344218

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
@@ -36,6 +38,7 @@ import org.springframework.util.StringUtils;
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Thomas Darimont
*/
public class PreferredConstructor<T, P extends PersistentProperty<P>> {
@@ -43,6 +46,10 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
private final List<Parameter<Object, P>> parameters;
private final Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new HashMap<PersistentProperty<?>, Boolean>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock read = lock.readLock();
private final Lock write = lock.writeLock();
/**
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
*
@@ -117,21 +124,36 @@ public class PreferredConstructor<T, P extends PersistentProperty<P>> {
Assert.notNull(property);
Boolean cached = isPropertyParameterCache.get(property);
try {
if (cached != null) {
return cached;
}
read.lock();
Boolean cached = isPropertyParameterCache.get(property);
for (Parameter<?, P> parameter : parameters) {
if (parameter.maps(property)) {
isPropertyParameterCache.put(property, true);
return true;
if (cached != null) {
return cached;
}
} finally {
read.unlock();
}
isPropertyParameterCache.put(property, false);
return false;
try {
write.lock();
for (Parameter<?, P> parameter : parameters) {
if (parameter.maps(property)) {
isPropertyParameterCache.put(property, true);
return true;
}
}
isPropertyParameterCache.put(property, false);
return false;
} finally {
write.unlock();
}
}
/**