Skip to content

Commit

Permalink
Fixes #274. Added interceptor to activate a Configuration Profile.
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez authored Apr 14, 2020
1 parent 91b20f3 commit cbad4a8
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 59 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,37 @@
package io.smallrye.config;

import java.util.OptionalInt;

/**
* 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 {
/**
* The default priority value, {@code 100}.
*/
int DEFAULT_PRIORITY = 100;

/**
* 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);

/**
* Returns the interceptor priority. This is required, because the interceptor priority needs to be sorted
* before doing initialization.
*
* @return the priority value.
*/
default OptionalInt getPriority() {
return OptionalInt.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.smallrye.config;

import java.util.Comparator;
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 static final Comparator<ConfigValue> CONFIG_SOURCE_COMPARATOR = (o1, o2) -> {
int res = Integer.compare(o2.getConfigSourceOrdinal(), o1.getConfigSourceOrdinal());
if (res != 0) {
return res;
}

if (o1.getConfigSourceName() != null && o2.getConfigSourceName() != null) {
return o2.getConfigSourceName().compareTo(o1.getConfigSourceName());
} else {
return res;
}
};

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);
if (originalValue != null && CONFIG_SOURCE_COMPARATOR.compare(profileValue, originalValue) > 0) {
return originalValue;
} else {
return profileValue;
}
}
}

return context.proceed(name);
}
}
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,15 @@ 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());
}
if (builder.isAddDefaultInterceptors()) {
interceptors.addAll(builder.getDefaultInterceptors());
}

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 @@ -164,7 +160,7 @@ private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfi
}, null);

for (int i = interceptors.size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(interceptors.get(i), current);
current = new SmallRyeConfigSourceInterceptorContext(interceptors.get(i).getInterceptor(current), current);
}

return current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
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,9 +50,10 @@ 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 addDefaultInterceptors = false;
private boolean addDiscoveredSources = false;
private boolean addDiscoveredConverters = false;
private boolean addDiscoveredInterceptors = false;
Expand Down Expand Up @@ -93,11 +98,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 All @@ -120,6 +130,30 @@ List<ConfigSource> getDefaultSources() {
return defaultSources;
}

public SmallRyeConfigBuilder addDefaultInterceptors() {
this.addDefaultInterceptors = true;
return this;
}

List<InterceptorWithPriority> getDefaultInterceptors() {
final List<InterceptorWithPriority> interceptors = new ArrayList<>();

interceptors.add(new InterceptorWithPriority(new ExpressionConfigSourceInterceptor()));
interceptors.add(new InterceptorWithPriority(new ConfigSourceInterceptorFactory() {
@Override
public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) {
return new ProfileConfigSourceInterceptor(context);
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(600);
}
}));

return interceptors;
}

@Override
public SmallRyeConfigBuilder forClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
Expand All @@ -138,7 +172,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,14 +239,18 @@ Map<Type, ConverterWithPriority> getConverters() {
return converters;
}

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

boolean isAddDefaultSources() {
return addDefaultSources;
}

boolean isAddDefaultInterceptors() {
return addDefaultInterceptors;
}

boolean isAddDiscoveredSources() {
return addDiscoveredSources;
}
Expand Down Expand Up @@ -245,4 +285,46 @@ int getPriority() {
return priority;
}
}

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

private InterceptorWithPriority(ConfigSourceInterceptor interceptor) {
this(new ConfigSourceInterceptorFactory() {
@Override
public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) {
return interceptor;
}

@Override
public OptionalInt getPriority() {
final OptionalInt priority = ConfigSourceInterceptorFactory.super.getPriority();
if (priority.isPresent()) {
return priority;
}

final Integer annotationPriorityOrDefault = Optional
.ofNullable(interceptor.getClass().getAnnotation(Priority.class))
.map(Priority::value)
.orElse(ConfigSourceInterceptorFactory.DEFAULT_PRIORITY);

return OptionalInt.of(annotationPriorityOrDefault);
}
});
}

private InterceptorWithPriority(ConfigSourceInterceptorFactory factory) {
this.factory = factory;
this.priority = factory.getPriority().orElse(ConfigSourceInterceptorFactory.DEFAULT_PRIORITY);
}

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

public int getPriority() {
return priority;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.smallrye.config;

import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE;
import static org.junit.Assert.assertEquals;

import javax.annotation.Priority;

import org.jboss.logging.Logger;
Expand Down Expand Up @@ -37,28 +40,42 @@ 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);
}

@Test
public void defaultInterceptors() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config("my.prop", "1",
"%prof.my.prop", "${%prof.my.prop.profile}",
"%prof.my.prop.profile", "2",
SMALLRYE_PROFILE, "prof"))
.addDefaultInterceptors()
.build();

assertEquals("2", config.getValue("my.prop", String.class));
}

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

Expand Down
Loading

0 comments on commit cbad4a8

Please sign in to comment.