Skip to content

Commit

Permalink
Introduce restart method for interceptors (#1082)
Browse files Browse the repository at this point in the history
* Introduce `restart` method for interceptors

First step in solving #1081

* Add recursion limiter
  • Loading branch information
dmlloyd authored Jan 10, 2024
1 parent cc5010c commit 8647a69
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,25 @@ public interface ConfigSourceInterceptorContext extends Serializable {
/**
* Proceeds to the next interceptor in the chain.
*
* @param name the configuration name to lookup. Can be the original key.
* @param name the configuration name to look up (can be the original key)
* @return a {@link ConfigValue} with information about the name, value, config source and ordinal, or {@code null}
* if the value isn't present.
*/
ConfigValue proceed(String name);

/**
* Proceeds to the next interceptor in the chain.
* Re-calls the first interceptor in the chain.
* If the original name is given, then it is possible to cause a recursive loop, so care must be taken.
* This method is intended to be used by relocating and other compatibility-related interceptors.
*
* @return an Iterator of Strings with configuration names.
* @param name the configuration name to look up (can be the original key)
* @return a {@link ConfigValue} with information about the name, value, config source and ordinal, or {@code null}
* if the value isn't present.
*/
ConfigValue restart(String name);

/**
* {@return an iterator over the configuration names known to this interceptor.
*/
Iterator<String> iterateNames();

Expand Down
32 changes: 19 additions & 13 deletions implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public class SmallRyeConfig implements Config, Serializable {
SmallRyeConfig(SmallRyeConfigBuilder builder) {
// This needs to be executed before everything else to make sure that defaults from mappings are available to all sources
ConfigMappingProvider mappingProvider = builder.getMappingsBuilder().build();
this.configSources = new ConfigSources(builder);
this.configSources = new ConfigSources(builder, this);
this.converters = buildConverters(builder);
this.configValidator = builder.getValidator();
this.mappings = new ConcurrentHashMap<>(mappingProvider.mapConfiguration(this));
Expand Down Expand Up @@ -672,6 +672,10 @@ public ConfigSource getDefaultValues() {
return configSources.defaultValues;
}

ConfigSourceInterceptorContext interceptorChain() {
return configSources.interceptorChain;
}

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

Expand All @@ -686,7 +690,7 @@ private static class ConfigSources implements Serializable {
* that this constructor must be used when the Config object is being initialized, because interceptors also
* require initialization.
*/
ConfigSources(final SmallRyeConfigBuilder builder) {
ConfigSources(final SmallRyeConfigBuilder builder, final SmallRyeConfig config) {
// Add all sources except for ConfigurableConfigSource types. These are initialized later
List<ConfigSource> sources = buildSources(builder);
// Add the default values sources separately, so we can keep a reference to it and add mappings defaults
Expand All @@ -698,27 +702,29 @@ 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);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(mapSources(sources)), current);
SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(mapSources(sources)), current,
config);
for (InterceptorWithPriority interceptorWithPriority : interceptorWithPriorities) {
ConfigSourceInterceptor interceptor = interceptorWithPriority.getInterceptor(current);
interceptors.add(interceptor);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
}

// Init all late sources
List<String> profiles = getProfiles(interceptors);
List<ConfigSourceWithPriority> sourcesWithPriorities = mapLateSources(sources, interceptors, current, profiles,
builder);
builder, config);
List<ConfigSource> configSources = getSources(sourcesWithPriorities);

// 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);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities), current);
current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities), current,
config);
for (ConfigSourceInterceptor interceptor : interceptors) {
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current, config);
}

this.profiles = profiles;
Expand Down Expand Up @@ -785,7 +791,7 @@ private static List<ConfigSourceWithPriority> mapLateSources(
final List<ConfigSourceInterceptor> interceptors,
final ConfigSourceInterceptorContext current,
final List<String> profiles,
final SmallRyeConfigBuilder builder) {
final SmallRyeConfigBuilder builder, final SmallRyeConfig config) {

ConfigSourceWithPriority.resetLoadPriority();
List<ConfigSourceWithPriority> currentSources = new ArrayList<>();
Expand All @@ -806,10 +812,10 @@ 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);
context = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(currentSources), context);
ConfigSourceInterceptorContext context = new SmallRyeConfigSourceInterceptorContext(EMPTY, null, config);
context = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(currentSources), context, config);
for (ConfigSourceInterceptor interceptor : interceptors) {
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context);
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context, config);
}

// Init remaining sources, coming from SmallRyeConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,35 @@ class SmallRyeConfigSourceInterceptorContext implements ConfigSourceInterceptorC

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

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

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

@Override
public ConfigValue proceed(final String name) {
return interceptor.getValue(next, name);
}

@Override
public ConfigValue restart(final String name) {
RecursionCount rc = rcHolder.get();
rc.increment();
try {
return config.interceptorChain().proceed(name);
} finally {
rc.decrement();
}
}

@Override
public Iterator<String> iterateNames() {
return interceptor.iterateNames(next);
Expand All @@ -29,4 +45,20 @@ public Iterator<String> iterateNames() {
public Iterator<ConfigValue> iterateValues() {
return interceptor.iterateValues(next);
}

static final class RecursionCount {
int count;

void increment() {
int old = count;
if (old == 20) {
throw new IllegalStateException("Too many recursive interceptor actions");
}
count = old + 1;
}

void decrement() {
count--;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ public ConfigValue proceed(final String name) {
return interceptors.get(position++).getValue(this, name);
}

public ConfigValue restart(final String name) {
int old = position;
position = 0;
try {
return proceed(name);
} finally {
position = old;
}
}

@Override
public Iterator<String> iterateNames() {
return null;
Expand All @@ -41,10 +51,13 @@ public Iterator<ConfigValue> iterateValues() {
return null;
}
};

return context.proceed(name);
}

public ConfigValue restart(final String name) {
throw new UnsupportedOperationException();
}

@Override
public Iterator<String> iterateNames() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public ConfigValue proceed(final String name) {
return null;
}

public ConfigValue restart(final String name) {
return null;
}

@Override
public Iterator<String> iterateNames() {
return null;
Expand Down Expand Up @@ -197,6 +201,10 @@ public ConfigValue proceed(final String name) {
return null;
}

public ConfigValue restart(final String name) {
return null;
}

@Override
public Iterator<String> iterateNames() {
return null;
Expand Down

0 comments on commit 8647a69

Please sign in to comment.