Skip to content

Commit

Permalink
Fixes smallrye#274. Added interceptor to activate a Configuration Pro…
Browse files Browse the repository at this point in the history
…file.
  • Loading branch information
radcortez committed Apr 7, 2020
1 parent 9937dbc commit 31ce722
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 60 deletions.
29 changes: 29 additions & 0 deletions doc/modules/ROOT/pages/interceptors/interceptors.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interceptors are registered with the same priority, then their execution order m
SmallRye Config provides the following built-in Interceptors:

* <<expression-interceptor>>
* <<profile-interceptor>>

[[expression-interceptor]]
=== ExpressionConfigSourceInterceptor
Expand Down Expand Up @@ -68,3 +69,31 @@ If an expression cannot be expanded and no default is supplied a `NoSuchElementE

The `ExpressionConfigSourceInterceptor` is not registered by default. You need to register it via the ServiceLoader
mechanism with your application.

[[profile-interceptor]]
=== ProfileConfigSourceInterceptor

The `ProfileConfigSourceInterceptor` allows you to have multiple configurations with the same name and select them via
a profile property.

To be able to set properties with the same name, you need to prefix each property with `%` followed by the profile name
and a dot:

[source,properties]
----
my.prop=1234
%dev.my.prop=5678
----

Lookup is always done using the `my.prop` property name. If you want to use the profile `dev`, you need to set the
configuration `smallrye.config.profile=dev` into any valid ConfigSource.

When looking up the property `my.prop` with the `dev` profile active the value is `5678`.

Only one profile can be active in any given time.

==== Config Priority over Profiles

A ConfigSource with a highest priority, that defines `my.prop` will take priority over another low priority
ConfigSource that defines `%dev.my.prop`. This allows you to override profiles properties regardless of the active
profile.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.smallrye.config;

/**
* This ConfigSourceInterceptorFactory allows to initialize a {@link ConfigSourceInterceptor}, with access to the
* current {@link ConfigSourceInterceptorContext}.
*
* Interceptors in the chain are initialized in priority order and the current
* {@link ConfigSourceInterceptorContext} contains the current interceptor, plus all other interceptors already
* initialized.
*/
public interface ConfigSourceInterceptorFactory {
/**
* Gets the {@link ConfigSourceInterceptor} from the ConfigSourceInterceptorFactory. Implementations of this
* method must provide the instance of the {@link ConfigSourceInterceptor} to add into the Config Interceptor Chain.
*
* @param context the current {@link ConfigSourceInterceptorContext} with the interceptors already initialized.
* @return the {@link ConfigSourceInterceptor} to add to Config Interceptor Chain and initialize.
*/
ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context);

/**
* Gets the Class of the Interceptor.
*
* This is required, because the interceptor priority needs to be sorted before doing initialization.
*
* @return the Class of the {@link ConfigSourceInterceptor}
*/
Class<? extends ConfigSourceInterceptor> getInterceptorClass();

final class Delegate implements ConfigSourceInterceptorFactory {
private final ConfigSourceInterceptor interceptor;

Delegate(final ConfigSourceInterceptor interceptor) {
this.interceptor = interceptor;
}

@Override
public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) {
return interceptor;
}

@Override
public Class<? extends ConfigSourceInterceptor> getInterceptorClass() {
return interceptor.getClass();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.smallrye.config;

import java.util.Optional;

import javax.annotation.Priority;

@Priority(600)
public class ProfileConfigSourceInterceptor implements ConfigSourceInterceptor {
public static final String SMALLRYE_PROFILE = "smallrye.config.profile";

private final String profile;

public ProfileConfigSourceInterceptor(final String profile) {
this.profile = profile;
}

public ProfileConfigSourceInterceptor(final ConfigSourceInterceptorContext context) {
this(context, SMALLRYE_PROFILE);
}

public ProfileConfigSourceInterceptor(
final ConfigSourceInterceptorContext context,
final String profileConfigName) {
this.profile = Optional.ofNullable(context.proceed(profileConfigName)).map(ConfigValue::getValue).orElse(null);
}

@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
if (profile != null) {
final ConfigValue profileValue = context.proceed("%" + profile + "." + name);
if (profileValue != null) {
final ConfigValue originalValue = context.proceed(name);
return originalValue.getConfigSourceOrdinal() > profileValue.getConfigSourceOrdinal() ? originalValue
: profileValue;
}
}

return context.proceed(name);
}
}
35 changes: 22 additions & 13 deletions implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
import java.util.function.IntFunction;
import java.util.function.UnaryOperator;

import javax.annotation.Priority;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;

import io.smallrye.config.SmallRyeConfigBuilder.InterceptorWithPriority;

/**
* @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2017 Red Hat inc.
*/
Expand Down Expand Up @@ -125,19 +125,12 @@ private Map<Type, Converter<?>> buildConverters(final SmallRyeConfigBuilder buil
}

private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfigBuilder builder) {
final List<ConfigSourceInterceptor> interceptors = new ArrayList<>(builder.getInterceptors());
final List<InterceptorWithPriority> interceptors = new ArrayList<>(builder.getInterceptors());
if (builder.isAddDiscoveredInterceptors()) {
interceptors.addAll(builder.discoverInterceptors());
}

interceptors.sort((o1, o2) -> {
final Integer p1 = Optional.ofNullable(o1.getClass().getAnnotation(Priority.class)).map(Priority::value)
.orElse(100);
final Integer p2 = Optional.ofNullable(o2.getClass().getAnnotation(Priority.class)).map(Priority::value)
.orElse(100);

return Integer.compare(p2, p1);
});
interceptors.sort(Comparator.comparingInt(InterceptorWithPriority::getPriority).reversed());

SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(
(ConfigSourceInterceptor) (context, name) -> {
Expand All @@ -163,13 +156,29 @@ private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfi
return null;
}, null);

for (int i = interceptors.size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(interceptors.get(i), current);
List<ConfigSourceInterceptor> configSourceInterceptors = initInterceptors(interceptors, current);
for (int i = configSourceInterceptors.size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(configSourceInterceptors.get(i), current);
}

return current;
}

private List<ConfigSourceInterceptor> initInterceptors(final List<InterceptorWithPriority> interceptors,
final SmallRyeConfigSourceInterceptorContext context) {
final List<ConfigSourceInterceptor> configSourceInterceptors = new ArrayList<>();

SmallRyeConfigSourceInterceptorContext current = context;
for (InterceptorWithPriority interceptor : interceptors) {
for (ConfigSourceInterceptor sourceInterceptor : configSourceInterceptors) {
current = new SmallRyeConfigSourceInterceptorContext(sourceInterceptor, current);
}
configSourceInterceptors.add(interceptor.getInterceptor(current));
}

return configSourceInterceptors;
}

// no @Override
public <T, C extends Collection<T>> C getValues(String name, Class<T> itemClass, IntFunction<C> collectionFactory) {
return getValues(name, getConverter(itemClass), collectionFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Priority;

Expand All @@ -46,7 +49,7 @@ public class SmallRyeConfigBuilder implements ConfigBuilder {
private List<ConfigSource> sources = new ArrayList<>();
private Function<ConfigSource, ConfigSource> sourceWrappers = UnaryOperator.identity();
private Map<Type, ConverterWithPriority> converters = new HashMap<>();
private List<ConfigSourceInterceptor> interceptors = new ArrayList<>();
private List<InterceptorWithPriority> interceptors = new ArrayList<>();
private ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
private boolean addDefaultSources = false;
private boolean addDiscoveredSources = false;
Expand Down Expand Up @@ -93,11 +96,16 @@ List<Converter> discoverConverters() {
return discoveredConverters;
}

List<ConfigSourceInterceptor> discoverInterceptors() {
List<ConfigSourceInterceptor> interceptors = new ArrayList<>();
List<InterceptorWithPriority> discoverInterceptors() {
List<InterceptorWithPriority> interceptors = new ArrayList<>();
ServiceLoader<ConfigSourceInterceptor> interceptorLoader = ServiceLoader.load(ConfigSourceInterceptor.class,
classLoader);
interceptorLoader.forEach(interceptors::add);
interceptorLoader.forEach(interceptor -> interceptors.add(new InterceptorWithPriority(interceptor)));

ServiceLoader<ConfigSourceInterceptorFactory> interceptorFactoryLoader = ServiceLoader
.load(ConfigSourceInterceptorFactory.class, classLoader);
interceptorFactoryLoader.forEach(interceptor -> interceptors.add(new InterceptorWithPriority(interceptor)));

return interceptors;
}

Expand Down Expand Up @@ -138,7 +146,9 @@ public SmallRyeConfigBuilder withSources(Collection<ConfigSource> configSources)
}

public SmallRyeConfigBuilder withInterceptors(ConfigSourceInterceptor... interceptors) {
Collections.addAll(this.interceptors, interceptors);
this.interceptors.addAll(Stream.of(interceptors)
.map(InterceptorWithPriority::new)
.collect(Collectors.toList()));
return this;
}

Expand Down Expand Up @@ -203,7 +213,7 @@ Map<Type, ConverterWithPriority> getConverters() {
return converters;
}

List<ConfigSourceInterceptor> getInterceptors() {
List<InterceptorWithPriority> getInterceptors() {
return interceptors;
}

Expand Down Expand Up @@ -245,4 +255,28 @@ int getPriority() {
return priority;
}
}

static class InterceptorWithPriority {
private final ConfigSourceInterceptorFactory factory;
private final int priority;

private InterceptorWithPriority(ConfigSourceInterceptor interceptor) {
this(new ConfigSourceInterceptorFactory.Delegate(interceptor));
}

private InterceptorWithPriority(ConfigSourceInterceptorFactory factory) {
this.factory = factory;
this.priority = Optional.ofNullable(factory.getInterceptorClass().getAnnotation(Priority.class))
.map(Priority::value)
.orElse(100);
}

public ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext context) {
return factory.getInterceptor(context);
}

public int getPriority() {
return priority;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,25 @@ public void priority() {
public void serviceLoader() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "1234"))
.withSources(KeyValuesConfigSource.config("my.prop.loader", "1234"))
.addDiscoveredInterceptors()
.build();

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

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

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

Expand Down

This file was deleted.

Loading

0 comments on commit 31ce722

Please sign in to comment.