diff --git a/doc/modules/ROOT/pages/interceptors/interceptors.adoc b/doc/modules/ROOT/pages/interceptors/interceptors.adoc index 7592bcb46..66531e776 100644 --- a/doc/modules/ROOT/pages/interceptors/interceptors.adoc +++ b/doc/modules/ROOT/pages/interceptors/interceptors.adoc @@ -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]] === ExpressionConfigSourceInterceptor @@ -68,3 +69,23 @@ 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. + +Only one profile can be active in any given time. diff --git a/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptor.java b/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptor.java index 818c8571e..e540f6ae2 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptor.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptor.java @@ -2,6 +2,8 @@ import java.io.Serializable; +import org.eclipse.microprofile.config.Config; + /** * The ConfigSourceInterceptor allows you to intercept the resolution of a configuration key name before the * configuration value is resolved by the Config. @@ -18,6 +20,15 @@ * order may be non deterministic. */ public interface ConfigSourceInterceptor extends Serializable { + /** + * This method initializes the interceptor, with access to the current Config object. This allows to + * configure the interceptor using configurations retrieved from the Config. + * + * @param config the current {@code Config} associated to the interceptor. + */ + default void initialize(Config config) { + } + /** * Single method to intercept the resolution of a configuration key. Calling * {@link ConfigSourceInterceptorContext#proceed(String)} will proceed executing the interceptor chain. The chain diff --git a/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java b/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java new file mode 100644 index 000000000..b915b176d --- /dev/null +++ b/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java @@ -0,0 +1,39 @@ +package io.smallrye.config; + +import javax.annotation.Priority; + +import org.eclipse.microprofile.config.Config; + +@Priority(600) +public class ProfileConfigSourceInterceptor implements ConfigSourceInterceptor { + public static final String SMALLRYE_PROFILE = "smallrye.config.profile"; + + private String profile; + + public ProfileConfigSourceInterceptor() { + + } + + public ProfileConfigSourceInterceptor(final String profile) { + this.profile = profile; + } + + @Override + public void initialize(final Config config) { + if (profile == null) { + profile = config.getOptionalValue(SMALLRYE_PROFILE, String.class).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) { + return profileValue; + } + } + + return context.proceed(name); + } +} diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java index d7f84e939..66217d231 100644 --- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java +++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java @@ -71,8 +71,11 @@ public int compare(ConfigSource o1, ConfigSource o2) { SmallRyeConfig(SmallRyeConfigBuilder builder) { this.configSourcesRef = buildConfigSources(builder); - this.interceptorChain = buildInterceptorChain(builder); this.converters = buildConverters(builder); + + final List interceptors = buildInterceptors(builder); + this.interceptorChain = buildInterceptorChain(interceptors); + initInterceptors(interceptors); } @Deprecated @@ -80,7 +83,7 @@ protected SmallRyeConfig(List configSources, Map(Collections.unmodifiableList(configSources)); this.converters = new ConcurrentHashMap<>(Converters.ALL_CONVERTERS); this.converters.putAll(converters); - this.interceptorChain = buildInterceptorChain(new SmallRyeConfigBuilder()); + this.interceptorChain = buildInterceptorChain(new ArrayList<>()); } private AtomicReference> buildConfigSources(final SmallRyeConfigBuilder builder) { @@ -124,7 +127,7 @@ private Map> buildConverters(final SmallRyeConfigBuilder buil return converters; } - private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfigBuilder builder) { + private List buildInterceptors(final SmallRyeConfigBuilder builder) { final List interceptors = new ArrayList<>(builder.getInterceptors()); if (builder.isAddDiscoveredInterceptors()) { interceptors.addAll(builder.discoverInterceptors()); @@ -139,6 +142,10 @@ private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfi return Integer.compare(p2, p1); }); + return interceptors; + } + + private ConfigSourceInterceptorContext buildInterceptorChain(final List interceptors) { SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext( (ConfigSourceInterceptor) (context, name) -> { for (ConfigSource configSource : getConfigSources()) { @@ -170,6 +177,12 @@ private ConfigSourceInterceptorContext buildInterceptorChain(final SmallRyeConfi return current; } + private void initInterceptors(final List interceptors) { + for (ConfigSourceInterceptor interceptor : interceptors) { + interceptor.initialize(this); + } + } + // no @Override public > C getValues(String name, Class itemClass, IntFunction collectionFactory) { return getValues(name, getConverter(itemClass), collectionFactory); diff --git a/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java b/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java new file mode 100644 index 000000000..7d685c7b0 --- /dev/null +++ b/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java @@ -0,0 +1,48 @@ +package io.smallrye.config; + +import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE; +import static org.junit.Assert.assertEquals; + +import org.eclipse.microprofile.config.Config; +import org.junit.Test; + +public class ProfileConfigSourceInterceptorTest { + @Test + public void profile() { + final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_PROFILE, "prof"); + + assertEquals("2", config.getValue("my.prop", String.class)); + } + + @Test + public void fallback() { + final Config config = buildConfig("my.prop", "1", SMALLRYE_PROFILE, "prof"); + + assertEquals("1", config.getValue("my.prop", String.class)); + } + + @Test + public void expressions() { + final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "${my.prop}", SMALLRYE_PROFILE, "prof"); + + assertEquals("1", config.getValue("my.prop", String.class)); + } + + @Test + public void profileExpressions() { + final Config config = buildConfig("my.prop", "1", + "%prof.my.prop", "${%prof.my.prop.profile}", + "%prof.my.prop.profile", "2", + SMALLRYE_PROFILE, "prof"); + + assertEquals("2", config.getValue("my.prop", String.class)); + } + + private static Config buildConfig(String... keyValues) { + return new SmallRyeConfigBuilder() + .addDefaultSources() + .withSources(KeyValuesConfigSource.config(keyValues)) + .withInterceptors(new ProfileConfigSourceInterceptor(), new ExpressionConfigSourceInterceptor()) + .build(); + } +}