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 iscommon
-
dev.prop
value isdev
-
my.prop
value is5678
-
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 iscommon
-
dev.prop
value isdev
-
my.prop
value is0
-
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.
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.