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:

  1. A system property config.profile
  2. An environment variable HELIDON_CONFIG_PROFILE

There are two ways to define a profile configuration:

  1. Use a config source with a profile specific name
  2. 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):

  1. application-dev.yaml on file system
  2. application-dev.properties on file system
  3. application-dev.yaml on classpath
  4. application-dev.properties on classpath
  5. application.yaml on file system
  6. application.properties on file system
  7. application.yaml on classpath
  8. application.properties on 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):

  1. config-profile-dev.yaml on file system
  2. config-profile-dev.properties on file system
  3. config-profile-dev.yaml on classpath
  4. config-profile-dev.properties on 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 config-profile-dev.yaml
sources:
  - type: "inlined"
    properties:
      app.greeting: "Hello World"
Copied

An example of a profile using environment variables, system properties, classpath, and file configuration:

Config profile config-profile-prod.yaml
sources:
  - type: "environment-variables"
  - type: "system-properties"
  - type: "file"
    properties:
      path: "config/config-prod.yaml"
  - type: "classpath"
    properties:
      resource: "application.yaml"
Copied

Built-in Types

The config system supports these built-in types:

Built-in Types
TypeUseRelated ConfigSources MethodRequired Properties
system-propertiesSystem properties are a config sourceConfigSources.systemProperties()n/a
environment-variablesEnvironment variables are a config sourceConfigSources.environmentVariables()n/a
classpathSpecified resource is used as a config sourceConfigSources.classpath(String)resource - path to the resource to load
fileSpecified file is used as a config sourceConfigSources.file(Path)path - path to the file to load
directoryEach file in directory used as config entry, with key = file name and value = file contentsConfigSources.directory(String)path - path to the directory to use
urlSpecified URL is read as a config sourceConfigSources.url(URL)url - URL from which to load the config
inlinedThe whole configuration tree under properties is added as a configuration source (excluding the properties node)n/an/a
prefixedAssociated config source is loaded with the specified prefixConfigSources.prefixed(String,Supplier)
  • key - key of config element in associated source to load

  • type - associated config source specification

  • properties - as needed to further qualify the associated config source

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.

Profile config-profile.yaml illustrating all built-in sources available on the classpath
caching.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"
Copied

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);
    }
}
Copied

Register it as a java service loader service

File META-INF/services/io.helidon.config.spi.ConfigSourceProvider
io.helidon.examples.MyConfigSourceProvider
Copied

Now you can use the following profile:

sources:
  - type: "system-properties"
  - type: "environment-variables"
  - type: "my-type"
    properties:
        my-property: "some-value"
Copied

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.

Config Profile Support for Built-in Polling Strategies
Strategy TypeUsageProperties
regularPeriodic polling - See PollingStrategies.regular methodinterval (Duration) - indicating how often to poll; e.g., PT15S represents 15 seconds
Config Profile Support for Built-in Change Watchers
TypeUsageProperties
fileFilesystem monitoring - See FileSystemWatcher classinitial-delay-millis - delay between the start of the watcher and first check for changesdelay-millis - how often do we check the watcher service for changes
Config Profile Support for Built-in Retry Policies
Policy TypeUsageProperties
repeatRegularly-scheduled - see RetryPolicies.repeat.

retries (int) - number of retries to perform

Optional:

  • delay (Duration) - initial delay between retries

  • delay-factor (double) - delay is repeatedly multiplied by this each retry to compute the delay for each successive retry

  • call-timeout (Duration) - timeout for a single invocation to load the source

  • overall-timeout (Duration) - total timeout for all retry calls and delays

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:

  1. When config profile is defined, only sources from the profile are added (aligned with SE Imperative programming model)
  2. When not using a config profile, ConfigSource services are discovered from the service registry, i.e. you can create a custom config source as a @Service.Singleton
  3. 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)
  4. You can also define a ConfigSourceProvider as 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());
    }
}
Copied