Contents
Overview
Configuration profiles provide a capability to prepare structure of configuration for each environment in advance, and then simply switch between these structures using a system property or an environment variable.
Profile Options
To choose a configuration profile to use at runtime, you can use:
- A system property
config.profile - An environment variable
HELIDON_CONFIG_PROFILE
There are two ways to define a profile configuration:
- Use a config source with a profile specific name
- Use a profile file defining all configuration sources
Configuration profiles can only be used when config is created using the Config.create() method without parameters. If you explicitly configure sources, profiles are ignored.
Profile Config Sources
If a profile is specified, config will load the profile-specific default configuration source before the "main" source.
Let’s consider the selected profile is dev, and we have yaml configuration support on classpath; config will look for the following sources (in this order):
application-dev.yamlon file systemapplication-dev.propertieson file systemapplication-dev.yamlon classpathapplication-dev.propertieson classpathapplication.yamlon file systemapplication.propertieson file systemapplication.yamlon classpathapplication.propertieson classpath
Profile Files
If a profile is specified, config will look for a profile-specific "meta configuration".
Let’s consider the selected profile is dev, and we have yaml configuration support on classpath; config will look for the following profiles (in this order):
config-profile-dev.yamlon file systemconfig-profile-dev.propertieson file systemconfig-profile-dev.yamlon classpathconfig-profile-dev.propertieson classpath
If any of these files is discovered, it would be used to set up the configuration. In case none is found, the config falls back to profile specific config sources.
The structure of the file is described below in profile file format.
In case you need to customize the location of the profile file, you can use the system property io.helidon.config.meta-config. For example if it is configured to config/profile.yaml, config looks for file config/profile-dev.yaml when dev profile is configured.
Profile File Format
Configuration profile provides similar options to the configuration builder. The profile file must contain at least the list of sources from which configuration can be loaded.
The root sources property contains an array (ordered) of objects defining each config source to be used. Each element of the array must contain at least the type property, determining the config source type (such as system-properties, file). It may also contain a properties property with additional configuration of the config source.
An example development profile using "inlined" configuration:
config-profile-dev.yamlsources:
- type: "inlined"
properties:
app.greeting: "Hello World"An example of a profile using environment variables, system properties, classpath, and file configuration:
config-profile-prod.yamlsources:
- type: "environment-variables"
- type: "system-properties"
- type: "file"
properties:
path: "config/config-prod.yaml"
- type: "classpath"
properties:
resource: "application.yaml"Built-in Types
The config system supports these built-in types:
| Type | Use | Related ConfigSources Method | Required Properties |
|---|---|---|---|
system-properties | System properties are a config source | ConfigSources.systemProperties() | n/a |
environment-variables | Environment variables are a config source | ConfigSources.environmentVariables() | n/a |
classpath | Specified resource is used as a config source | ConfigSources.classpath(String) | resource - path to the resource to load |
file | Specified file is used as a config source | ConfigSources.file(Path) | path - path to the file to load |
directory | Each file in directory used as config entry, with key = file name and value = file contents | ConfigSources.directory(String) | path - path to the directory to use |
url | Specified URL is read as a config source | ConfigSources.url(URL) | url - URL from which to load the config |
inlined | The whole configuration tree under properties is added as a configuration source (excluding the properties node) | n/a | n/a |
prefixed | Associated config source is loaded with the specified prefix | ConfigSources.prefixed(String,Supplier) |
|
Except for the system-properties and environment-variables types, the profile properties section for a source can also specify any optional settings for the corresponding config source type. The JavaDoc for the related config source type builders lists the supported properties for each type. (For example, FileConfigSource.Builder.)
Here is an example profile in YAML format. Note how the properties sections are at the same level as the type or class within a sources array entry.
config-profile.yaml illustrating all built-in sources available on the classpathcaching.enabled: false
sources:
- type: "system-properties"
- type: "environment-variables"
- type: "directory"
properties:
path: "conf/secrets"
media-type-mapping:
yaml: "application/x-yaml"
password: "application/base64"
polling-strategy:
type: "regular"
properties:
interval: "PT15S"
- type: "url"
properties:
url: "http://config-service/my-config"
media-type: "application/hocon"
optional: true
retry-policy:
type: "repeat"
properties:
retries: 3
- type: "file"
properties:
optional: true
path: "conf/env.yaml"
change-watcher:
type: "file"
properties:
delay-millis: 5000
- type: "prefixed"
properties:
key: "app"
type: "classpath"
properties:
resource: "app.conf"
- type: "classpath"
properties:
resource: "application.conf"Note that the example shows how your profile can configure optional features such as polling strategies and retry policies for config sources.
Support for Custom Sources
Profiles can be used to set up custom config sources as well as the built-in ones described above.
Implement the ConfigSourceProvider
public class MyConfigSourceProvider implements ConfigSourceProvider {
private static final String TYPE = "my-type";
@Override
public boolean supports(String type) {
return TYPE.equals(type);
}
@Override
public ConfigSource create(String type, Config metaConfig) {
// as we only support one in this implementation, we can just return it
return MyConfigSource.create(metaConfig);
}
@Override
public Set<String> supported() {
return Set.of(TYPE);
}
}Register it as a java service loader service
META-INF/services/io.helidon.config.spi.ConfigSourceProviderio.helidon.examples.MyConfigSourceProviderNow you can use the following profile:
sources:
- type: "system-properties"
- type: "environment-variables"
- type: "my-type"
properties:
my-property: "some-value"Note that it is the io.helidon.config.AbstractConfigSource class that provides support for polling strategies, change watchers, and retry policies. If you create custom config sources that should also offer this support be sure they extend AbstractConfigSource and implement appropriate SPI interfaces (such as io.helidon.config.spi.WatchableSource) to support such features.
Support for Custom Polling Strategies, Change Watchers, and Retry Policies
Your config profile can include the set-up for polling strategies, change watchers, and retry policies if the config source supports them. Declare them in a way similar to how you declare the config sources themselves: by type and with accompanying properties.
| Strategy Type | Usage | Properties |
|---|---|---|
regular | Periodic polling - See PollingStrategies.regular method | interval (Duration) - indicating how often to poll; e.g., PT15S represents 15 seconds |
| Type | Usage | Properties | |
|---|---|---|---|
file | Filesystem monitoring - See FileSystemWatcher class | initial-delay-millis - delay between the start of the watcher and first check for changes | delay-millis - how often do we check the watcher service for changes |
| Policy Type | Usage | Properties |
|---|---|---|
repeat | Regularly-scheduled - see RetryPolicies.repeat. |
Optional:
|
To specify a custom polling strategy or custom retry policy, implement the interface (io.helidon.config.spi.PollingStrategy, io.helidon.config.spi.ChangeWatcher, or io.helidon.config.spi.RetryPolicy), and then implement the provider interface (io.helidon.config.spi.PollingStrategyProvider, io.helidon.config.spi.ChangeWatcherProvider, or io.helidon.config.spi.RetryPolicyProvider) to enable your custom implementations for profiles. You can then use any custom properties - these are provided as a Config instance to the create method of the Provider implementation.
See RetryPolicy, ChangeWatcher, and PollingStrategy JavaDoc sections.
Declarative
When using Helidon Declarative programming model (inversion of control, injection, annotation based), we may still be interested in the profile feature.
The interaction is as follows:
- When config profile is defined, only sources from the profile are added (aligned with SE Imperative programming model)
- When not using a config profile,
ConfigSourceservices are discovered from the service registry, i.e. you can create a custom config source as a@Service.Singleton - If you want to have a config source that works both with a config profile, and with the default config instance, there is a solution (see below)
- You can also define a
ConfigSourceProvideras a registry service (and this will work the same as in SE imperative)
Designing a config source that integrates with profiles and default config
Note that this approach will only work if you get a Config instance from the service registry, or if you inject a Config instance into your service and the service registry creates the instance.
Choose a type that does not conflict with existing config source types, let’s assume my-config-type.
Create a @Service.Singleton service that is named with the chosen type.
Inject a named Optional<io.helidon.config.MetaConfig> into your config source service - this will contain meta-config from the config profile, or it will be empty if a profile is not used.
Create your configuration tree as needed in your custom config source.
Example of such a config source:
@Service.Singleton
@Service.Named(MyProfiledConfigSource.MY_TYPE)
public class MyProfiledConfigSource implements NodeConfigSource {
static final String MY_TYPE = "my-config-type";
private final String value;
MyProfiledConfigSource(@Service.Named(MY_TYPE) Optional<MetaConfig> metaConfig) {
this.value = metaConfig.flatMap(it -> it.metaConfiguration()
.get("app.value2")
.asString()
.asOptional())
.orElse("meta-config-value-not-found");
}
@Override
public Optional<ConfigContent.NodeContent> load() throws ConfigException {
return Optional.of(ConfigContent.NodeContent.builder()
.node(ConfigNode.ObjectNode.builder()
.addValue("app.value2", value)
.build())
.build());
}
}