commit
791be953fe
|
@ -18,6 +18,7 @@ package org.springframework.boot.context.properties.source;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -44,8 +45,6 @@ import org.springframework.util.ObjectUtils;
|
|||
class SpringIterableConfigurationPropertySource extends SpringConfigurationPropertySource
|
||||
implements IterableConfigurationPropertySource {
|
||||
|
||||
private volatile Object cacheKey;
|
||||
|
||||
private volatile Cache cache;
|
||||
|
||||
SpringIterableConfigurationPropertySource(EnumerablePropertySource<?> propertySource,
|
||||
|
@ -131,13 +130,23 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
|
|||
}
|
||||
|
||||
private Cache getCache() {
|
||||
CacheKey cacheKey = CacheKey.get(getPropertySource());
|
||||
if (ObjectUtils.nullSafeEquals(cacheKey, this.cacheKey)) {
|
||||
return this.cache;
|
||||
CacheKey key = CacheKey.get(getPropertySource());
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
Cache cache = this.cache;
|
||||
try {
|
||||
if (cache != null && cache.hasKeyEqualTo(key)) {
|
||||
return cache;
|
||||
}
|
||||
cache = new Cache(key.copy());
|
||||
this.cache = cache;
|
||||
return cache;
|
||||
}
|
||||
catch (ConcurrentModificationException ex) {
|
||||
// Not fatal at this point, we can continue without a cache
|
||||
return null;
|
||||
}
|
||||
this.cache = new Cache();
|
||||
this.cacheKey = cacheKey.copy();
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -147,10 +156,20 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
|
|||
|
||||
private static class Cache {
|
||||
|
||||
private final CacheKey key;
|
||||
|
||||
private List<ConfigurationPropertyName> names;
|
||||
|
||||
private PropertyMapping[] mappings;
|
||||
|
||||
Cache(CacheKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public boolean hasKeyEqualTo(CacheKey key) {
|
||||
return this.key.equals(key);
|
||||
}
|
||||
|
||||
public List<ConfigurationPropertyName> getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
|
||||
package org.springframework.boot.context.properties.source;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -169,6 +173,21 @@ public class SpringIterableConfigurationPropertySourceTests {
|
|||
assertThat(adapter.stream().count()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concurrentModificationExceptionInvalidatesCache() {
|
||||
// gh-17013
|
||||
ConcurrentModificationThrowingMap<String, Object> map = new ConcurrentModificationThrowingMap<>();
|
||||
map.put("key1", "value1");
|
||||
map.put("key2", "value2");
|
||||
EnumerablePropertySource<?> source = new MapPropertySource("test", map);
|
||||
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
|
||||
source, DefaultPropertyMapper.INSTANCE);
|
||||
assertThat(adapter.stream().count()).isEqualTo(2);
|
||||
map.setThrowException(true);
|
||||
map.put("key3", "value3");
|
||||
assertThat(adapter.stream().count()).isEqualTo(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link PropertySource} that's also an {@link OriginLookup}.
|
||||
*/
|
||||
|
@ -206,4 +225,37 @@ public class SpringIterableConfigurationPropertySourceTests {
|
|||
|
||||
}
|
||||
|
||||
private static class ConcurrentModificationThrowingMap<K, V>
|
||||
extends LinkedHashMap<K, V> {
|
||||
|
||||
private boolean throwException;
|
||||
|
||||
public void setThrowException(boolean throwException) {
|
||||
this.throwException = throwException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return new KeySet(super.keySet());
|
||||
}
|
||||
|
||||
private class KeySet extends LinkedHashSet<K> {
|
||||
|
||||
KeySet(Set<K> keySet) {
|
||||
super(keySet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<K> iterator() {
|
||||
if (ConcurrentModificationThrowingMap.this.throwException) {
|
||||
ConcurrentModificationThrowingMap.this.throwException = false;
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
return super.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue