Skip to content

Commit

Permalink
Keep a reference to the full interceptor chain to call restart
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Jul 31, 2024
1 parent 1550541 commit cdf0770
Show file tree
Hide file tree
Showing 3 changed files with 148 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
Expand Up @@ -58,6 +58,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;
Expand Down Expand Up @@ -763,10 +764,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;

Expand Down Expand Up @@ -796,21 +793,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);
}
}

Expand All @@ -823,16 +822,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;
Expand Down Expand Up @@ -929,16 +928,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
Expand Down
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
Expand All @@ -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
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
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.Iterator;
import java.util.Optional;
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;
Expand All @@ -24,59 +29,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"))
Expand All @@ -90,7 +95,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"))
Expand All @@ -105,7 +110,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"))
Expand All @@ -122,7 +127,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) {
Expand All @@ -148,7 +153,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",
Expand All @@ -165,15 +170,100 @@ void priorityInParentClass() {
assertEquals(1, interceptorWithPriority.getPriority());
}

@Test
void restart() {
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);
}
})
.build();

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

@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);

Expand Down

0 comments on commit cdf0770

Please sign in to comment.