Mappings#
With SmallRye Config Mappings, it is possible to group multiple configuration properties in a single interface that share the same prefix (or namespace). It supports the following set of features:
- Automatic conversion of the configuration type, including
List
,Set
,Map
,Optional
and primitive types. - Nested Config Mapping groups.
- Configuration Properties Naming Strategies
- Integration with Bean Validation
A Config Mapping requires an interface with minimal metadata configuration annotated with
io.smallrye.config.ConfigMapping
:
The Server
interface is able to map configurations with the name server.host
into the Server#host()
method and
server.port
into Server#port()
method. The configuration property name to lookup is built from the prefix, and the
method name with .
(dot) as the separator.
Warning
If a mapping fails to match a configuration property a NoSuchElementException
is thrown, unless the mapped
element is an Optional
.
Registration#
Registration of Config Mappings is automatic in CDI aware environments with the @ConfigMapping
annotation.
In non-CDI environments, the Config Mapping can be registered via SmallRyeConfigBuilder#withMapping
. In this case,
the @ConfigMapping
is completely optional (but recommendeded to set the prefix).
Retrieval#
A config mapping interface can be injected into any CDI aware bean:
@ApplicationScoped
class BusinessBean {
@Inject
Server server;
public void businessMethod() {
String host = server.host();
}
}
In non-CDI environments, use the API io.smallrye.config.SmallRyeConfig#getConfigMapping
to retrieve the config
mapping instance:
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
Server server = config.getConfigMapping(Server.class);
Info
Config Mapping instances are cached. They are populated when the Config
instance is initialized and their values
are not updated on Config Source changes.
For a Config Mapping to be valid, it needs to match every configuration property name contained in the Config
under
the specified prefix set in @ConfigMapping
. This prevents unknown configuration properties in the Config
. This
behaviour can be disabled with the configuration smallrye.config.mapping.validate-unknown=false
.
Defaults#
The io.smallrye.config.WithDefault
annotation allows to set a default property value into a mapping (and prevent
errors if the configuration value is not available in any ConfigSource
).
No configuration properties are required. The Defaults#foo()
will return the value foo
and Defaults#bar()
will
return the value bar
.
Nested Groups#
A nested mapping provides a way to map sub-groups of configuration properties.
@ConfigMapping(prefix = "server")
public interface Server {
String host();
int port();
Log log();
interface Log {
boolean enabled();
String suffix();
boolean rotate();
}
}
server.host=localhost
server.port=8080
server.log.enabled=true
server.log.suffix=.log
server.log.rotate=false
The method name of a mapping group acts as a sub-prefix in the property name. In this case the matching property to
Server.Log#enabled
is server.log.enabled
.
Overriding property names#
@WithName
#
If a method name and a property name do not match, the io.smallrye.config.WithName
annotation can
override the method name mapping and use the name supplied in the annotation.
@WithParentName
#
The io.smallrye.config.WithParentName
annotation allows configurations mappings to inherit its parent container name,
simplifying the configuration property name required to match the mapping.
@ConfigMapping(prefix = "server")
interface Server {
@WithParentName
ServerHostAndPort hostAndPort();
@WithParentName
ServerInfo info();
}
interface ServerHostAndPort {
String host();
int port();
}
interface ServerInfo {
String name();
}
Without the @WithParentName
the method ServerInfo#name
maps the configuration property server.info.name
. With
@WithParentName
, the Server#info
mapping will inherit the parent name from Server
and ServerInfo#name
maps to
the property server.name
instead.
NamingStrategy#
Method names in camelCase map to kebab-case configuration property names by default.
The mapping strategy can be adjusted by setting namingStrategy
value in the @ConfigMapping
annotation.
@ConfigMapping(prefix = "server", namingStrategy = ConfigMapping.NamingStrategy.VERBATIM)
public interface ServerVerbatimNamingStrategy {
String theHost();
int thePort();
}
The @ConfigMapping
annotation support the following naming stategies:
- KEBAB_CASE - The method name is derived by replacing case changes with a dash to map the configuration property.
- VERBATIM - The method name is used as is to map the configuration property.
- SNAKE_CASE - The method name is derived by replacing case changes with an underscore to map the configuration property.
Conversion#
A config mapping interface support automatic conversions of all types available for conversion in Config
.
@ConfigMapping
public interface SomeTypes {
@WithName("int")
int intPrimitive();
@WithName("int")
Integer intWrapper();
@WithName("long")
long longPrimitive();
@WithName("long")
Long longWrapper();
@WithName("float")
float floatPrimitive();
@WithName("float")
Float floatWrapper();
@WithName("double")
double doublePrimitive();
@WithName("double")
Double doubleWrapper();
@WithName("char")
char charPrimitive();
@WithName("char")
Character charWrapper();
@WithName("boolean")
boolean booleanPrimitive();
@WithName("boolean")
Boolean booleanWrapper();
}
This is also valid for Optional
and friends.
@ConfigMapping
public interface Optionals {
Optional<Server> server();
Optional<String> optional();
@WithName("optional.int")
OptionalInt optionalInt();
interface Server {
String host();
int port();
}
}
In this case, the mapping won’t fail if the configuraton properties values are missing.
@WithConverter
#
The io.smallrye.config.WithConverter
annotation provides a way to set a specific Converter
in a mapping.
@ConfigMapping
public interface Converters {
@WithConverter(FooBarConverter.class)
String foo();
}
public static class FooBarConverter implements Converter<String> {
@Override
public String convert(final String value) {
return "bar";
}
}
A call to Converters.foo()
results in the value bar
.
Collections#
A config mapping is also able to map the collections types List
and Set
:
@ConfigMapping(prefix = "server")
public interface ServerCollections {
Set<Environment> environments();
interface Environment {
String name();
List<App> apps();
interface App {
String name();
List<String> services();
Optional<List<String>> databases();
}
}
}
server.environments[0].name=dev
server.environments[0].apps[0].name=rest
server.environments[0].apps[0].services=bookstore,registration
server.environments[0].apps[0].databases=pg,h2
server.environments[0].apps[1].name=batch
server.environments[0].apps[1].services=stock,warehouse
The List
and Set
mappings can use Indexed Properties to map configuration values in
mapping groups.
Maps#
A config mapping is also able to map a Map
:
@ConfigMapping(prefix = "server")
public interface Server {
String host();
int port();
Map<String, String> form();
Map<String, List<Alias>> aliases();
interface Alias {
String name();
}
}
server.host=localhost
server.port=8080
server.form.index=index.html
server.form.login.page=login.html
server.form.error.page=error.html
server.aliases.localhost[0].name=prod
server.aliases.localhost[1].name=127.0.0.1
server.aliases.\"io.smallrye\"[0].name=smallrye
The configuration property name needs to specify an additional segment to act as the map key. The server.form
matches
the Server#form
Map
and the segments index
, login.page
and error.page
represent the Map
keys.
For collection types, the key requires the indexed format. The configuration name server.aliases.localhost[0].name
maps to the Map<String, List<Alias>> aliases()
member, where localhost
is the Map
key, [0]
is the index of the
List<Alias>
collection where the Alias
element will be stored, containing the name prod
.
Info
They Map
key part in the configuration property name may require quotes to delimit the key.
@WithUnnamedKey
#
The io.smallrye.config.WithUnnamedKey
annotation allows to omit a single map key in the configuration path:
@ConfigMapping(prefix = "server")
public interface Server {
@WithUnnamedKey("localhost")
Map<String, Alias> aliases();
interface Alias {
String name();
}
}
The sever.aliases.name
is an unnamed Map
property, because it does not contain the Map
key to populate the Map
entry. Due to @WithUnnamedKey("localhost")
the Map
key is not required in the configuration path. The key used to
look up the Map entry is given by io.smallrye.config.WithUnnamedKey#value
:
Server server = config.getConfigMapping(Server.class);
Map<String, Alias> localhost = server.aliases.get("localhost");
Warning
If the unnamed key (in this case localhost
) is explicitly set in a property name, the mapping will throw an error.
@WithDefaults
#
The io.smallrye.config.WithDefaults
is a marker annotation to use only in a Map
to return the default value for
the value element on any key lookup:
@ConfigMapping(prefix = "server")
public interface Server {
@WithDefaults
Map<String, Alias> aliases();
interface Alias {
@WithDefault("localhost")
String name();
}
}
A look up to the aliases
Map
with the key localhost
, any
or any other key, returns a Alias
instance, where
Alias.name
is localhost
, because that is the default value. A look up to prod
returns a Alias
instance, where
Alias.name
is prod
because the property is defined in the configuration as server.aliases.prod.name=prod
.
Server server = config.getConfigMapping(Server.class);
Map<String, Alias> localhost = server.aliases.get("localhost");
Map<String, Alias> any = server.aliases.get("any");
Map<String, Alias> any = server.aliases.get("prod");
ToString#
If the config mapping contains a toString
method declaration, the config mapping instance will include a proper
implementation of the toString
method.
Caution
Do not include a toString
declaration in a config mapping with sensitive information.
Validation#
A config mapping may combine annotations from Bean Validation to validate configuration properties values.
@ConfigMapping(prefix = "server")
interface Server {
@Size(min = 2, max = 20)
String host();
@Max(10000)
int port();
}
The application startup fails with a io.smallrye.config.ConfigValidationException
if the configuration properties
values do not follow the contraints defined in Server
.
Info
For validation to work, the smallrye-config-validator
dependency is required in the classpath.