Skip to content

Commit 3b2dd2b

Browse files
authored
Provide a way to enable / disable cache of property names when building the config (#1434)
1 parent 8b622b1 commit 3b2dd2b

File tree

3 files changed

+109
-21
lines changed

3 files changed

+109
-21
lines changed

implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.util.Set;
4747
import java.util.TreeMap;
4848
import java.util.concurrent.ConcurrentHashMap;
49+
import java.util.concurrent.atomic.AtomicReference;
4950
import java.util.function.BiFunction;
5051
import java.util.function.Function;
5152
import java.util.function.IntFunction;
@@ -1617,10 +1618,12 @@ public <T> T getConfigMapping(final Class<T> type, final String prefix) {
16171618
* {@inheritDoc}
16181619
*
16191620
* This implementation caches the list of property names collected when {@link SmallRyeConfig} is built via
1620-
* {@link SmallRyeConfigBuilder#build()}.
1621+
* {@link SmallRyeConfigBuilder#build()}. The cache may be disabled with
1622+
* {@link SmallRyeConfigBuilder#isCachePropertyNames()}.
16211623
*
16221624
* @return the cached names of all configured keys of the underlying configuration
16231625
* @see SmallRyeConfig#getLatestPropertyNames()
1626+
* @see SmallRyeConfigBuilder#isCachePropertyNames()
16241627
*/
16251628
@Override
16261629
public Iterable<String> getPropertyNames() {
@@ -1936,7 +1939,7 @@ public Iterator<String> get() {
19361939
this.sources = configSources;
19371940
this.defaultValues = defaultValues;
19381941
this.interceptorChain = current;
1939-
this.propertyNames = new PropertyNames(current, builder.getSecretKeys());
1942+
this.propertyNames = new PropertyNames(current, builder.getSecretKeys(), builder.isCachePropertyNames());
19401943
}
19411944

19421945
private static List<ConfigSource> buildSources(final SmallRyeConfigBuilder builder) {
@@ -2118,33 +2121,36 @@ private static class PropertyNames implements Serializable {
21182121

21192122
private final SmallRyeConfigSourceInterceptorContext interceptorChain;
21202123
private final Set<PropertyName> secretKeys;
2124+
private final boolean cachePropertyNames;
21212125

2122-
private final Set<String> names = new HashSet<>();
2123-
private final Set<String> secretNames = new HashSet<>();
2124-
private final Map<String, Map<Integer, String>> indexed = new HashMap<>();
2126+
private final AtomicReference<Names> names = new AtomicReference<>(Names.empty());
21252127

2126-
public PropertyNames(final SmallRyeConfigSourceInterceptorContext interceptorChain,
2127-
final Set<PropertyName> secretKeys) {
2128+
public PropertyNames(
2129+
final SmallRyeConfigSourceInterceptorContext interceptorChain,
2130+
final Set<PropertyName> secretKeys,
2131+
final boolean cachePropertyNames) {
21282132
this.interceptorChain = interceptorChain;
21292133
this.secretKeys = secretKeys;
2134+
this.cachePropertyNames = cachePropertyNames;
21302135
}
21312136

21322137
Iterable<String> get() {
2133-
if (names.isEmpty() && secretNames.isEmpty()) {
2138+
if (!cachePropertyNames || names.get().isEmpty()) {
21342139
return latest();
21352140
}
2136-
return new Names();
2141+
return new NamesIterable(names.get());
21372142
}
21382143

21392144
Map<String, Map<Integer, String>> indexed() {
21402145
// ensure populated
21412146
get();
2142-
return indexed;
2147+
return names.get().indexed();
21432148
}
21442149

21452150
Iterable<String> latest() {
2146-
names.clear();
2147-
secretNames.clear();
2151+
Set<String> names = new HashSet<>();
2152+
Set<String> secretNames = new HashSet<>();
2153+
Map<String, Map<Integer, String>> indexed = new HashMap<>();
21482154
Iterator<String> namesIterator = interceptorChain.iterateNames();
21492155
while (namesIterator.hasNext()) {
21502156
String name = namesIterator.next();
@@ -2179,32 +2185,53 @@ public String apply(final Integer key, final String value) {
21792185
}
21802186
}
21812187
names.remove(ConfigSource.CONFIG_ORDINAL);
2182-
return new Names();
2188+
Names all = new Names(names, secretNames, indexed);
2189+
if (cachePropertyNames) {
2190+
this.names.compareAndSet(this.names.get(), all);
2191+
return new NamesIterable(this.names.get());
2192+
} else {
2193+
return new NamesIterable(all);
2194+
}
2195+
}
2196+
2197+
private record Names(
2198+
Set<String> names,
2199+
Set<String> secretNames,
2200+
Map<String, Map<Integer, String>> indexed) {
2201+
2202+
boolean isEmpty() {
2203+
return names.isEmpty() && secretNames.isEmpty() && indexed.isEmpty();
2204+
}
2205+
2206+
static Names empty() {
2207+
return new Names(Collections.emptySet(), Collections.emptySet(), Collections.emptyMap());
2208+
}
21832209
}
21842210

2185-
private class Names implements Iterable<String> {
2186-
private final Iterator<Set<String>> names;
2211+
private static class NamesIterable implements Iterable<String> {
2212+
private final Iterator<Set<String>> namesIterators;
21872213

2188-
public Names() {
2214+
public NamesIterable(final Names names) {
21892215
if (SecretKeys.isLocked()) {
2190-
this.names = List.of(PropertyNames.this.names).iterator();
2216+
this.namesIterators = List.of(names.names()).iterator();
21912217
} else {
2192-
this.names = List.of(PropertyNames.this.names, PropertyNames.this.secretNames).iterator();
2218+
this.namesIterators = List.of(names.names(), names.secretNames()).iterator();
21932219
}
21942220
}
21952221

21962222
@Override
2223+
@SuppressWarnings("NullableProblems")
21972224
public Iterator<String> iterator() {
21982225
return new Iterator<>() {
2199-
Iterator<String> current = names.next().iterator();
2226+
Iterator<String> current = namesIterators.next().iterator();
22002227

22012228
@Override
22022229
public boolean hasNext() {
22032230
if (current.hasNext()) {
22042231
return true;
22052232
} else {
2206-
if (names.hasNext()) {
2207-
current = names.next().iterator();
2233+
if (namesIterators.hasNext()) {
2234+
current = namesIterators.next().iterator();
22082235
return current.hasNext();
22092236
} else {
22102237
return false;

implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public class SmallRyeConfigBuilder implements ConfigBuilder {
8686
private boolean addDiscoveredInterceptors = false;
8787
private boolean addDiscoveredSecretKeysHandlers = false;
8888
private boolean addDiscoveredValidator = false;
89+
private boolean cachePropertyNames = true;
8990

9091
public SmallRyeConfigBuilder addDiscoveredCustomizers() {
9192
addDiscoveredCustomizers = true;
@@ -708,6 +709,10 @@ public boolean isAddDiscoveredValidator() {
708709
return addDiscoveredValidator;
709710
}
710711

712+
public boolean isCachePropertyNames() {
713+
return cachePropertyNames;
714+
}
715+
711716
public SmallRyeConfigBuilder setAddDefaultSources(final boolean addDefaultSources) {
712717
this.addDefaultSources = addDefaultSources;
713718
return this;
@@ -753,6 +758,11 @@ public SmallRyeConfigBuilder setAddDiscoveredValidator(final boolean addDiscover
753758
return this;
754759
}
755760

761+
public SmallRyeConfigBuilder setCachePropertyNames(boolean cachePropertyNames) {
762+
this.cachePropertyNames = cachePropertyNames;
763+
return this;
764+
}
765+
756766
@Override
757767
public SmallRyeConfig build() {
758768
if (addDiscoveredCustomizers) {

implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,4 +805,55 @@ public String getName() {
805805
assertEquals("1234", wrappedConfig.getConfigValue("my.prop").getValue());
806806
assertEquals("1234", wrappedConfig.getConfigValue("%prod.my.prop").getValue());
807807
}
808+
809+
@Test
810+
void propertyNames() {
811+
Map<String, String> configSource = new HashMap<>();
812+
configSource.put("one", "1");
813+
SmallRyeConfig config = new SmallRyeConfigBuilder()
814+
.withSources(new MapBackedConfigSource("map", configSource) {
815+
})
816+
.build();
817+
818+
Set<String> propertyNames = stream(config.getPropertyNames().spliterator(), false).collect(toSet());
819+
assertEquals(1, propertyNames.size());
820+
assertTrue(propertyNames.contains("one"));
821+
822+
configSource.put("two", "2");
823+
propertyNames = stream(config.getPropertyNames().spliterator(), false).collect(toSet());
824+
assertEquals(1, propertyNames.size());
825+
assertTrue(propertyNames.contains("one"));
826+
assertEquals("2", config.getConfigValue("two").getValue());
827+
828+
propertyNames = stream(config.getLatestPropertyNames().spliterator(), false).collect(toSet());
829+
assertEquals(2, propertyNames.size());
830+
assertTrue(propertyNames.contains("one"));
831+
assertTrue(propertyNames.contains("two"));
832+
833+
propertyNames = stream(config.getPropertyNames().spliterator(), false).collect(toSet());
834+
assertEquals(2, propertyNames.size());
835+
assertTrue(propertyNames.contains("one"));
836+
assertTrue(propertyNames.contains("two"));
837+
}
838+
839+
@Test
840+
void propertyNamesNoCache() {
841+
Map<String, String> configSource = new HashMap<>();
842+
configSource.put("one", "1");
843+
SmallRyeConfig config = new SmallRyeConfigBuilder()
844+
.setCachePropertyNames(false)
845+
.withSources(new MapBackedConfigSource("map", configSource) {
846+
})
847+
.build();
848+
849+
Set<String> propertyNames = stream(config.getPropertyNames().spliterator(), false).collect(toSet());
850+
assertEquals(1, propertyNames.size());
851+
assertTrue(propertyNames.contains("one"));
852+
853+
configSource.put("two", "2");
854+
propertyNames = stream(config.getPropertyNames().spliterator(), false).collect(toSet());
855+
assertEquals(2, propertyNames.size());
856+
assertTrue(propertyNames.contains("one"));
857+
assertTrue(propertyNames.contains("two"));
858+
}
808859
}

0 commit comments

Comments
 (0)