Config

Indexed Properties

In Microprofile Config, a config value which contains unescaped commas may be converted to Collection. This works for simple cases, but it becomes cumbersome and limited for more advanced cases.

Indexed Properties provide a way to use indexes in config property names to map specific elements in a Collection type. Since the indexed element is part of the property name and not contained in the value, this can also be used to map complex object types as Collection elements. Consider:

# MicroProfile Config - Collection Values
my.collection=dog,cat,turtle

# SmallRye Config - Indexed Property
my.indexed.collection[0]=dog
my.indexed.collection[1]=cat
my.indexed.collection[2]=turtle

The indexed property syntax uses the property name and square brackets with an index in between.

A call to Config#getValues("my.collection", String.class), will automatically create and convert a List<String> that contains the values dog, cat and turtle. A call to Config#getValues("my.indexed.collection", String.class) returns the exact same result. For compatibility reasons, if SmallRye Config finds the same property name in their indexed and unindexed format, the unindexed value takes priority.

The indexed property is sorted by their index before being added to the target Collection. Any gaps contained in the indexes do not resolve to the target Collection, which means that the Collection result will store all values in a continued fashion and without gaps.

Map Support

SmallRye Config allows injecting multiple configuration parameters as a Map using the standard annotations (@ConfigProperty and @ConfigProperties) which can be very helpful especially with a dynamic configuration.

For example, let’s say that I want to keep in my configuration the custom reason phrases to return to the end user according to the http status code. In that particular use case, the configuration could then be something like the following properties file:

server.reasons.200=My custom reason phrase for OK
server.reasons.201=My custom reason phrase for Created
...

The previous configuration could be injected directly into your bean using the standard annotations as next:

With @ConfigProperty

@ApplicationScoped
public class ConfigBean {

    @Inject
    @ConfigProperty(name = "server.reasons", defaultValue = "200=OK;201=Created") (1)
    Map<Integer, String> reasons; (2)

}
1 Provide the name of the parent configuration property from the annotation @ConfigProperty. Provide also the default values to use in case no direct sub properties could be found, using the syntax <key1>=<value1>;<key2>=<value2>…​, so here the default values are "OK" for 200 and "Created" for 201.
2 Provide the expected type of Map, here the keys will automatically be converted into Integers, and the values into Strings.

With @ConfigProperties

@ConfigProperties(prefix = "server") (1)
public class Config {

    Map<Integer, String> reasons; (2)
}
1 Provide the prefix of the name of the parent configuration property from the annotation @ConfigProperties, here the prefix is server.
2 Provide the suffix of the name of the parent configuration, and the expected type of Map, here the keys will automatically be converted into Integers, and the values into Strings.
Only the direct sub properties will be converted into a Map and injected into the target bean, the rest will be ignored. In other words, in the previous example, a property whose name is reasons.200.a would be ignored as not considered as a direct sub property.
The property will be considered as missing if no direct sub properties could be found.

It is also possible to do the exact same thing programmatically by calling the non-standard method SmallRyeConfig#getValues("server.reasons", Integer.class, String.class) if the property is mandatory otherwise by calling the method SmallRyeConfig#getOptionalValues("server.reasons", Integer.class, String.class).

Profiles

SmallRye Config supports multiple configurations with the same name and selects them via a profile property.

To be able to set properties with the same name, each property needs to be prefixed with a percentage sign % followed by the profile name and a dot . in the syntax %{profile-name}.config.name:

# Regular property
my.prop=1234
# Profile property
%dev.my.prop=5678

To use the profile dev, the configuration smallrye.config.profile=dev has to be set into any valid ConfigSource.

Any lookup to the my.prop configuration name will first search by the active profile name %dev.my.prop and then fallback to my.prop if no value is present.

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

Ordinals

Profile lookups are only valid if the ConfigSource has a higher ordinal than a lookup to the regular configuration name. Consider:

# Source main.properties
config_ordinal=1000
# Regular property
my.prop=1234
# Source profile.properties
config_ordinal=100
# Regular property
%dev.my.prop=5678

Even with the profile dev active, the lookup value for my.prop is 1234. This prevents lower ordinal sources to set a profile property value that cannot be overridden unless the profile property is also overridden.

Multiple Profiles

Multiple Profiles may be active at the same time. The configuration smallrye.config.profile accepts a comma-separated list of profile names: smallrye.config.profile=common,dev. Both common and dev are separate profiles.

When multiple profiles are active, the rules for profile configuration are exactly the same. If two profiles define the same configuration, then the last listed profile has priority. Consider:

smallrye.config.profile=common,dev

my.prop=1234
%common.my.prop=0
%dev.my.prop=5678

%common.commom.prop=common
%dev.dev.prop=dev
%test.test.prop=test

Then

  • common.prop value is common

  • dev.prop value is dev

  • my.prop value is 5678

  • test.prop does not have a value

Parent Profile

A Parent Profile adds one level of hierarchy to the current profile. The configuration smallrye.config.profile.parent accepts a single profile name.

When the Parent Profile is active, if a property cannot be found in the current active Profile, the config lookup fallbacks to the Parent Profile. Consider:

smallrye.config.profile=dev
smallrye.config.profile.parent=common

my.prop=1234
%common.my.prop=0
%dev.my.prop=5678

%common.commom.prop=common
%dev.dev.prop=dev
%test.test.prop=test

Then

  • common.prop value is common

  • dev.prop value is dev

  • my.prop value is 0

  • test.prop does not have a value

Locations

Additionally, to the default config locations specified by the MicroProfile Config specification, SmallRye Config provides a way to scan additional locations for configuration properties files.

The smallrye.config.locations configuration property accepts multiple locations separated by a comma , and each must represent a valid URI. The supported URI schemes are:

  • file or directory

  • classpath resource

  • jar resource

  • http resource

Each URI scheme loads all discovered resources in a ConfigSource. All loaded sources use the same ordinal of the source that found the smallrye.config.locations configuration property. For instance, if smallrye.config.locations is set as a system property, then all loaded sources have their ordinals set to 400 (system properties use 400 as their ordinal). The ordinal may be overridden directly in the resource by setting the config_ordinal property. Sources are sorted first by their ordinal, then by location order, and finally by loading order.

If a profile is active, and the URI represents a single resource (for instance a file), then resources that match the active profile are also loaded. The profile resource name must follow the pattern: {location}-{profile}. A profile resource is only loaded if the unprofiled resource is also available in the same location. This is to keep a consistent loading order and pair all the resources together.

Profile resources are not taken into account if the location is a directory since there is no reliable way to discover which file is the main resource. Properties that use the profile prefix syntax %profile. will work as expected.

Examples

All properties files from a directory:

# loads all files from a relative path
smallrye.config.locations=./src/main/resources/

# loads all files from an absolute path
smallrye.config.locations=/user/local/config

For relative paths, the JVM user.dir property defines the current directory.

A specific file :

smallrye.config.locations=./src/main/resources/additional.properties

If a profile dev is active, and an additional-dev.properties file exists, this will also be loaded.

All additional.properties files from the classpath:

smallrye.config.locations=additional.properties

If a profile prod is active, and an additional-prod.properties resources exists next to the additional.properties resource, this will also be loaded.

The resources.properties file from a specific jar:

smallrye.config.locations=jar:file:///user/local/app/lib/resources-.jar!/resources.properties

If a profile test is active, and an additional-test.properties respource exists, this will also be loaded.

The config.properties file from a web server:

smallrye.config.locations=http://localhost:8080/config/config.

Secret Keys

When configuration properties contain passwords or other kinds of secrets, Smallrye Config can hide them to prevent accidental exposure of such values.

This is no way a replacement for securing secrets. Proper security mechanisms must still be used to secure secrets. However, there is still the basic problem that passwords and secrets are generally encoded simply as strings.

Secret Keys provides a way to "lock" the configuration so that secrets do not appear unless explicitly enabled.

Configuration

Secret Keys requires the list of Config property names that must be hidden. This can be supplied in SmallRyeConfigBuilder#withSecretKeys and initialized with SmallRyeConfigFactory. From this point forward, any config name retrieved from the Config instance that matches the Secret Keys will throw a SecurityException.

Secret Keys is implemented by a SmallRye Config interceptor, so in order for it to work, the SecretKeysConfigSourceInterceptor needs to be added to the Config, either by registering the interceptor alone, or by registing the default interceptors via SmallRyeConfigBuilder.withSecretKeys#addDefaultInterceptors.

Unlock Keys

Access to the Secret Keys, is available via the APIs io.smallrye.config.SecretKeys#doUnlocked(java.lang.Runnable) and io.smallrye.config.SecretKeys#doUnlocked(java.util.function.Supplier<T>).

String secretValue = SecretKeys.doUnlocked(() -> {
    config.getValue("secret", String.class);
});

Secret Keyes are only unlocked in the context of doUnlocked. Once the execution completes, the secrets become locked again.