Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Keep a reference to the full interceptor chain to call restart
Browse files Browse the repository at this point in the history
radcortez committed Jul 31, 2024
1 parent 7ef6aa6 commit 8a47216
Showing 3 changed files with 174 additions and 40 deletions.
38 changes: 19 additions & 19 deletions implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@

import io.smallrye.common.annotation.Experimental;
import io.smallrye.config.SmallRyeConfigBuilder.InterceptorWithPriority;
import io.smallrye.config.SmallRyeConfigSourceInterceptorContext.InterceptorChain;
import io.smallrye.config._private.ConfigLogging;
import io.smallrye.config._private.ConfigMessages;
import io.smallrye.config.common.utils.StringUtil;
@@ -747,10 +748,6 @@ public List<String> getProfiles() {
return configSources.getProfiles();
}

ConfigSourceInterceptorContext interceptorChain() {
return configSources.interceptorChain;
}

private static class ConfigSources implements Serializable {
private static final long serialVersionUID = 3483018375584151712L;

@@ -780,21 +777,23 @@ private static class ConfigSources implements Serializable {
List<InterceptorWithPriority> interceptorWithPriorities = buildInterceptors(builder);

// Create the initial chain with initial sources and all interceptors
SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
current = new SmallRyeConfigSourceInterceptorContext(negativeSources, current, config);
InterceptorChain chain = new InterceptorChain();
SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, chain);

current = new SmallRyeConfigSourceInterceptorContext(negativeSources, current, chain);
for (InterceptorWithPriority interceptorWithPriority : interceptorWithPriorities) {
if (interceptorWithPriority.getPriority() < 0) {
ConfigSourceInterceptor interceptor = interceptorWithPriority.getInterceptor(current);
negativeInterceptors.add(interceptor);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, chain);
}
}
current = new SmallRyeConfigSourceInterceptorContext(positiveSources, current, config);
current = new SmallRyeConfigSourceInterceptorContext(positiveSources, current, chain);
for (InterceptorWithPriority interceptorWithPriority : interceptorWithPriorities) {
if (interceptorWithPriority.getPriority() >= 0) {
ConfigSourceInterceptor interceptor = interceptorWithPriority.getInterceptor(current);
positiveInterceptors.add(interceptor);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, chain);
}
}

@@ -807,16 +806,16 @@ private static class ConfigSources implements Serializable {
// Rebuild the chain with the late sources and new instances of the interceptors
// The new instance will ensure that we get rid of references to factories and other stuff and keep only
// the resolved final source or interceptor to use.
current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, chain);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities, true),
current, config);
current, chain);
for (ConfigSourceInterceptor interceptor : negativeInterceptors) {
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, chain);
}
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities, false),
current, config);
current, chain);
for (ConfigSourceInterceptor interceptor : positiveInterceptors) {
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, chain);
}

this.profiles = profiles;
@@ -913,16 +912,17 @@ private static List<ConfigSourceWithPriority> mapLateSources(
Collections.reverse(currentSources);

// Rebuild the chain with the profiles sources, so profiles values are also available in factories
ConfigSourceInterceptorContext context = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
InterceptorChain chain = new InterceptorChain();
ConfigSourceInterceptorContext context = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, chain);
context = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(currentSources, true), context,
config);
chain);
for (ConfigSourceInterceptor interceptor : negativeInterceptors) {
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context, config);
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context, chain);
}
context = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(currentSources, false), context,
config);
chain);
for (ConfigSourceInterceptor interceptor : positiveInterceptors) {
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context, config);
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context, chain);
}

// Init remaining sources, coming from SmallRyeConfig
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package io.smallrye.config;

import java.io.Serializable;
import java.util.Iterator;
import java.util.function.Supplier;

class SmallRyeConfigSourceInterceptorContext implements ConfigSourceInterceptorContext {
private static final long serialVersionUID = 6654406739008729337L;

private final ConfigSourceInterceptor interceptor;
private final ConfigSourceInterceptorContext next;
private final SmallRyeConfig config;
private final InterceptorChain chain;

private static final ThreadLocal<RecursionCount> rcHolder = ThreadLocal.withInitial(RecursionCount::new);

SmallRyeConfigSourceInterceptorContext(
final ConfigSourceInterceptor interceptor,
final ConfigSourceInterceptorContext next,
final SmallRyeConfig config) {
final InterceptorChain chain) {
this.interceptor = interceptor;
this.next = next;
this.config = config;
this.chain = chain.setChain(this);
}

@Override
@@ -30,7 +32,7 @@ public ConfigValue restart(final String name) {
RecursionCount rc = rcHolder.get();
rc.increment();
try {
return config.interceptorChain().proceed(name);
return chain.get().proceed(name);
} finally {
if (rc.decrement()) {
// avoid leaking if the thread is cached
@@ -44,6 +46,22 @@ public Iterator<String> iterateNames() {
return interceptor.iterateNames(next);
}

static class InterceptorChain implements Supplier<ConfigSourceInterceptorContext>, Serializable {
private static final long serialVersionUID = 7387475787257736307L;

private ConfigSourceInterceptorContext chain;

@Override
public ConfigSourceInterceptorContext get() {
return chain;
}

public InterceptorChain setChain(final ConfigSourceInterceptorContext chain) {
this.chain = chain;
return this;
}
}

static final class RecursionCount {
int count;

Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package io.smallrye.config;

import static io.smallrye.config.KeyValuesConfigSource.config;
import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_PROFILE;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import jakarta.annotation.Priority;

import org.eclipse.microprofile.config.spi.ConfigSource;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -24,59 +32,59 @@ class ConfigSourceInterceptorTest {
void interceptor() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "1234", "override", "4567"))
.withSources(config("my.prop", "1234", "override", "4567"))
.withInterceptors(new LoggingConfigSourceInterceptor(),
new OverridingConfigSourceInterceptor())
.build();

final String value = config.getValue("my.prop", String.class);
String value = config.getValue("my.prop", String.class);
Assertions.assertEquals("4567", value);
}

@Test
void priority() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "1234"))
.withSources(config("my.prop", "1234"))
.withInterceptors(new LowerPriorityConfigSourceInterceptor(),
new HighPriorityConfigSourceInterceptor())
.build();

final String value = config.getValue("my.prop", String.class);
String value = config.getValue("my.prop", String.class);
Assertions.assertEquals("higher", value);
}

@Test
void serviceLoader() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop.loader", "1234"))
.withSources(config("my.prop.loader", "1234"))
.addDiscoveredInterceptors()
.build();

final String value = config.getValue("my.prop.loader", String.class);
String value = config.getValue("my.prop.loader", String.class);
Assertions.assertEquals("loader", value);
}

@Test
void serviceLoaderAndPriorities() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop.loader", "1234"))
.withSources(config("my.prop.loader", "1234"))
.addDiscoveredInterceptors()
.withInterceptors(new LowerPriorityConfigSourceInterceptor(),
new HighPriorityConfigSourceInterceptor())
.build();

final String value = config.getValue("my.prop.loader", String.class);
String value = config.getValue("my.prop.loader", String.class);
Assertions.assertEquals("higher", value);
}

@Test
void defaultInterceptors() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "1",
.withSources(config("my.prop", "1",
"%prof.my.prop", "${%prof.my.prop.profile}",
"%prof.my.prop.profile", "2",
SMALLRYE_CONFIG_PROFILE, "prof"))
@@ -90,7 +98,7 @@ void defaultInterceptors() {
void notFailExpansionInactive() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "${expansion}",
.withSources(config("my.prop", "${expansion}",
"%prof.my.prop", "${%prof.my.prop.profile}",
"%prof.my.prop.profile", "2",
SMALLRYE_CONFIG_PROFILE, "prof"))
@@ -105,7 +113,7 @@ void names() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.addDefaultInterceptors()
.withSources(KeyValuesConfigSource.config("my.prop", "1",
.withSources(config("my.prop", "1",
"%prof.my.prop", "${%prof.my.prop.profile}",
"%prof.my.prop.profile", "2",
SMALLRYE_CONFIG_PROFILE, "prof"))
@@ -122,7 +130,7 @@ void names() {
void replaceNames() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.withSources(KeyValuesConfigSource.config("my.prop", "1"))
.withSources(config("my.prop", "1"))
.withInterceptors(new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
@@ -148,7 +156,7 @@ void expandActiveProfile() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.addDefaultInterceptors()
.withSources(KeyValuesConfigSource.config(
.withSources(config(
"app.http.port", "8081",
"%dev.app.http.port", "8082",
"%test.app.http.port", "8083",
@@ -165,15 +173,123 @@ void priorityInParentClass() {
assertEquals(1, interceptorWithPriority.getPriority());
}

@Test
void restart() {
List<Integer> counter = new ArrayList<>();
counter.add(0);

SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.withSources(config("final", "final"))
.withInterceptorFactories(new ConfigSourceInterceptorFactory() {
@Override
public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) {
return new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
counter.set(0, counter.get(0) + 1);
return context.proceed(name);
}
};
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(DEFAULT_PRIORITY + 100);
}
})
.withInterceptors(new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
if (name.equals("restart")) {
return context.restart("final");
}
return context.proceed(name);
}
})
.build();

assertEquals("final", config.getRawValue("restart"));
assertEquals(2, counter.get(0));
assertEquals("final", config.getConfigValue("final").getName());
assertEquals("final", config.getConfigValue("restart").getName());
}

@Test
void restartNotInitialized() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.withSources(config("final", "final"))
.withInterceptors(new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
if (name.equals("restart")) {
return context.restart("final");
}
return context.proceed(name);
}
})
.withSources(new ConfigSourceFactory() {
@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(new ConfigSourceContext.ConfigSourceContextConfigSource(context))
.withMapping(Restart.class)
.withMappingIgnore("*")
.build();
return emptyList();
}
})
.build();

assertEquals("final", config.getRawValue("restart"));
}

@ConfigMapping
interface Restart {
Optional<String> restart();
}

@Test
void supplier() {
Value value = new Value();

Supplier<Value> first = new Supplier<>() {
@Override
public Value get() {
return value;
}
};

Supplier<Value> second = new Supplier<>() {
@Override
public Value get() {
return value;
}
};

System.out.println(first.get().value);
System.out.println(second.get().value);

value.value = "something else";

System.out.println(first.get().value);
System.out.println(second.get().value);
}

public static class Value {
String value = "value";
}

private static class LoggingConfigSourceInterceptor implements ConfigSourceInterceptor {
private static final Logger LOG = Logger.getLogger("io.smallrye.config");

@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
final ConfigValue configValue = context.proceed(name);
final String key = configValue.getName();
final String value = configValue.getValue();
final String configSource = configValue.getConfigSourceName();
ConfigValue configValue = context.proceed(name);
String key = configValue.getName();
String value = configValue.getValue();
String configSource = configValue.getConfigSourceName();

LOG.infov("The key {0} was loaded from {1} with the value {2}", key, configSource, value);

0 comments on commit 8a47216

Please sign in to comment.