Advanced Configuration Topics

This section discusses several advanced topics related to Helidon configuration.

Advanced Config Sources and Config Parsers

Environment Variables Config Source

The config system supports using environment variables as a config source, and is enabled by default. Since environment variable names are normally restricted to alphanumeric characters and underscore, this config source adds aliases that enable setting or overriding config entries with dotted and/or hyphenated keys.

The mapping makes it possible to set or override a config entry with a key of "foo.bar" using an environment variable named "FOO_BAR" and "foo.bar-baz" using "FOO_BAR_dash_BAZ".

One use case for this mapping is config overrides in containers, where passing environment variables directly or via Kubernetes Secrets/ConfigMaps is common. Scripts that solve the mapping problem by explicitly converting variables to system properties can also be simplified.

Aliases are produced for any environment variable name that matches all of the following:

  1. does not begin or end with a '_' character
  2. does not contain "__"
  3. contains one or more '_' characters

For each such name, two aliases are added with the names mapped as follows:

  1. Replace any "_dash_" or "_DASH_" substrings with "-", e.g. "APP_PAGE_dash_SIZE" becomes "APP_PAGE-SIZE".
  2. Replace '_' with '.' and add as an alias, e.g. "APP_GREETING" is added as "APP.GREETING" and "APP_PAGE-SIZE" is added as "APP.PAGE-SIZE". This mapping is added primarily to support mixed case config keys such as "app.someCamelCaseKey".
  3. Convert the result of step 2 to lowercase and add as an alias, e.g. "APP.GREETING" is added as "app.greeting" and "APP.PAGE-SIZE" is added as "app.page-size".

Directory Config Source

The config system supports using a file system directory as a config source. Each non-directory file in the directory becomes a config entry: the file name is the key and the contents of that file are used as the corresponding config String value.

The following example shows, for example, one way to load Kubernetes secrets mounted on the pod’s filesystem.

If the directory conf/secrets contains these two files

File secrets/username
jose
Copied
File secrets/password
^ery$ecretP&ssword
Copied

your application can load this as configuration as follows:

Using directory config source
Config secrets = Config.builder(
        ConfigSources.directory("conf/secrets")) 
        .disableEnvironmentVariablesSource()     
        .disableSystemPropertiesSource()         
        .build();

assert secrets.get("username")                   
        .asString()
        .get()
        .equals("jose");
assert secrets.get("password")                   
        .asString()
        .get()
        .equals("^ery$ecretP&ssword");
Copied
  • Loads all files from the conf/secrets directory.
  • No need to use environment variables or system properties as sources in building the Config.
  • The loaded config maps the key username to the value jose…​
  • …​and the key password to ^ery$ecretP&ssword.

Remember that your application can process the contents of a given file as configuration. See the config sources section and the ConfigSources.file JavaDoc.

In-memory Config Sources

The config system provides several ways to create a Config tree from data already in memory. See the ConfigSources javadoc for further details. The numerous variants of the from method construct ConfigSource or Builder<ConfigSource> instances.

Subtree of Another Config

Config anotherConfig = Config.create(classpath("application.conf"));

Config config = Config.create(
        ConfigSources.create(anotherConfig.get("data")));
Copied

Properties Object

Config config = Config.create(
        ConfigSources.create(System.getProperties()).build());                
Copied

String of a Given Media Type

Config config = Config.create(
        ConfigSources.create("app.greeting = Hi", "text/x-java-properties")); 
Copied

Map

Config config = Config.crate(
        ConfigSources.create(Map.of("app.page-size", "20"))
            .lax()             
            .build());         
Copied

ad hoc Config Nodes

Config config = Config.create(
        ConfigSources.create(ObjectNode.builder()
                                   .addList("app.basic-range", ListNode.builder()
                                           .addValue("-20")
                                           .addValue("20")
                                           .build())
                                   .build()));
Copied
  • ConfigSources.create variants for Properties or Map arguments return a ConfigSources.MapBuilder instance.
  • A similar create variant accepts a Readable instead of a String.
  • MapBuilder by default throws an exception if a key appears more than once in the map. The lax() method relaxes this; the config system logs a warning instead.

Multi-Source Configs and Composite Config Sources

Although the examples above use a single source, you can build a single Config from multiple sources.

Handling Key Collisions

Prefixed Config Sources

Sometimes you might want to create a single config tree from multiple sources but in a way that keeps the config from different sources in different subtrees.

The config system lets you assign a prefix to all keys from a given source using the ConfigSources.prefixed method. The following example shows two YAML files as config sources and the code to load each with a different prefix into a single Config tree:

File app.conf
greeting = "Hello"
page-size = 20
basic-range = [ -20, 20 ]
Copied
File data.conf
providers: [
    {
        name = "Provider1"
        class = "this.is.my.Provider1"
    },
    {
        name = "Provider2"
        class = "this.is.my.Provider2"
    }
]
Copied
Using prefixed config source
Config config = Config.create(
        ConfigSources.prefixed("app",                    
                               classpath("app.conf")),   
        ConfigSources.prefixed("data",                   
                               classpath("data.conf"))); 

assert config.get("app.greeting")                        
        .asString()
        .get()
        .equals("Hello");

assert config.get("data.providers.0.name")               
        .asString()
        .get()
        .equals("Provider1");
Copied
  • Specifies the prefix app for the associated source.
  • Supplier<ConfigSource> for the file app.conf loaded from the current classpath.
  • Specifies the prefix data for the associated source.
  • Supplier<ConfigSource> for the file app.conf loaded from the current classpath.
  • Key app.greeting combines the app prefix and the original key greeting from the app.conf source.
  • Key data.providers.0.name combines the data prefix and the original key providers.0.name property from data.conf source.

This technique can be useful, for example, if multiple sources contain keys that might overlap; assigning different prefixes to the keys from different sources gives your application a way to access all config elements distinctly even if their keys would otherwise conflict.

Merging Strategies

The ConfigSources.create(Supplier<ConfigSource>…​) and ConfigSources.create(List<Supplier<ConfigSource>…​) methods return a CompositeBuilder. By default, earlier sources in the list have higher priority than later ones, meaning that if the same key appears in two or more sources the source earlier in the list prevails.

Each CompositeConfigSource's merging strategy actually controls this behavior. The config system provides the FallbackMergingStrategy which implements the default, "first wins" algorithm. You can write your own implementation of ConfigSources.MergingStrategy and use it instead to provide a different algorithm.

Composite config source example
Config config = Config.create(                                               
        ConfigSources.create(file("conf/dev.properties").optional(),         
                           file("conf/config.properties").optional())        
                .add(classpath("application.properties"))                    
                .mergingStrategy(ConfigSources.MergingStrategy.fallback())); 
Copied
  • Creates a new Config instance from a single composite config source.
  • Method ConfigSources.create(sources…​) returns CompositeBuilder instance initialized with two sources (from dev.properties and config.properties files).
  • Adds third config source (application.properties on classpath) to the same CompositeBuilder.
  • Specifies the merging strategy. This example uses the default fallback merging strategy.

How Config Chooses Parsers

As the config sources and parsers section describes, these two work together to read and translate configuration data from some external form into the corresponding in-memory config tree.

Although most applications are explicit about the config sources they use in building a Config, the config system often has to figure out what parser to use. It does so by:

  1. determining, the best that it can, the media type of the source, and
  2. locating a parser that can translate that media type.

Identifying the Media Type

By Inference

Most applications let the config system try to infer the media type of the config source.

By default config source implementations use the io.helidon.common.media.type.MediaTypes API to infer the source media type from the source, typically (but not always) based on the file type portion of the file path. Helidon media type module has a predefined set of mappings as configured in common/media-type/src/main/resources/io/helidon/common/media/type/default-media-types.properties, including the Config supported formats: .properties, .yaml, .json and .conf. To handle other formats you can implement and register your own io.helidon.common.media.type.spi.MediaTypeDetector Java Service implementations. (Typically you would also write and register a config parser to translate that format; see Locating a Parser below.).

By Application Directive

Your application can specify what media type to use in interpreting a config source. Use this if your application knows the media type but the system might not be able to infer it correctly, either because no type detector would recognize it or because there might be more than one inferred media type.

Specify mediaType for config source
Config config = Config.create(classpath("props")                             
                                    .mediaType("text/x-java-properties")); 
Copied
  • The config system cannot infer the media type because there is no file type in the path props.
  • The developer knows the file is in Java Properties format so specifies the media type explicitly.

Note that a file type detector could be written to also inspect the contents of the file to infer the media type. The detectors provided by Helidon only inspect the suffix in the name of the file.

Locating a Parser

By Inference from media-type

Each config parser reports which media types it handles. Once the config system has determined a source’s media type, it searches the config parsers associated with the config builder for one that recognizes that media type. It then uses that parser to translate the config in the source into the in-memory config tree.

The application can add one or more parsers to a Config.Builder using the addParser method. This makes the parser available for use by the config sources associated with that builder, but does not directly tie a given parser to a given source. The builder uses media-type matching to select one of the parsers registered with the builder for each source.

If the config system cannot locate a parser that matches the media type of a source, it throws a ConfigException when trying to prepare the configuration.

By Application Directive

Your application can specify which parser to use for a config source. The AbstractParsableConfigSource.Builder class exposes the parser method, which accepts the ConfigParser to be used for that source. Several methods on ConfigSources such as classpath, directory, and file return this builder class.

Generally try to rely on media-type matching rather than specifying a given parser for a given source in the application. This keeps your application more flexible, both by insulating it from implementation classes and by letting it easily take advantage of improvements in or alternatives to the parsers available for a given media type.

Specify parser for config source
Config config = Config.create(classpath("props")                            
                                    .parser(ConfigParsers.properties())); 
Copied
  • The config system cannot infer the media type because there is no file type in the path props.
  • The developer knows the file is in Java Properties format so specifies the properties parser explicitly.

Parsing a Config Value as Config

A config value node might contain an entire config document in String form, but in a format different from the containing document. Your application can tell the config system to parse such a node as config in a different format and replace the String value node in the original tree with the config tree that results from parsing that String.

In this example, a YAML document contains a JSON document as a leaf.

YAML file with included JSON formated property
secrets:
    username: "jose"
    password: "^ery$ecretP&ssword"

app: >                             
    {
        "greeting": "Hello",
        "page-size": 20,
        "basic-range": [ -20, 20 ]
    }
Copied
  • The property app is itself formatted as a JSON document.

Specify Key-to-media-type Mapping

Specify JSON as media type for node
Config config = Config.create(
        classpath("application.yaml")
                .mediaTypeMapping(                          
                        key -> "app".equals(key.toString()) 
                                ? "application/json"
                                : null));

assert config.get("secrets.username").asString()            
        .get().equals("jose");
assert config.get("secrets.password").asString()            
        .get().equals("^ery$ecretP&ssword");

assert config.get("app").type() == Type.OBJECT;             

assert config.get("app.greeting")                           
        .asString().get().equals("Hello");
assert config.get("app.page-size")                          
        .asInt().get() == 20;
assert config.get("app.basic-range.0")                      
        .asInt().get() == -20;
assert config.get("app.basic-range.1")                      
        .asInt().get() == 20;
Copied
  • The source builder’s mediaTypeMapping method accepts a function which returns the appropriate media types (if any) for config keys.
  • The function says to treat the app property value as a JSON document and leave other nodes unchanged.
  • Other properties are loaded as expected.
  • Property app is now an structured object node.

Because the function passed to mediaTypeMapping identifies the app node as a JSON document, the config system selects the config parser that is registered with the builder which also handles the JSON media type.

Also, note that the config system replaces the original String value node with an object node resulting from parsing that String value as JSON.

Specify Key-to-parser Mapping

Alternatively, your application could map config keys to the specific parsers you want to use for parsing those keys' values.

Specify JSON formatted property' parser instance
Config config = Config.create(
        ConfigSources.classpath("application.yaml")
                .parserMapping(                                           
                        key -> "app".equals(key.toString())               
                                ? Optional.of(HoconConfigParser.create())
                                : Optional.empty()));
Copied
  • Uses the parserMapping method to map keys to parser instances.
  • Tells the config system to use the HOCON parser for translating the String value of the app key. (HOCON is a superset of JSON.)

As before, the config system replaces the value node in the containing config tree with the config tree resulting from the additional parse.

Loading Config using Meta-configuration

This section is now available in config profiles, which make meta-configuration obsolete - you can achieve the same with less configuration. A profile file is a meta-configuration file selected by the rules defined in the link above.

Instead of including code in your application to construct config trees from builders, sources, etc., you can instead prepare meta-configuration in a file that declares the sources to load and their attributes.

You can either specify the meta-config file in your application or allow the config system to search for and load meta-config from a preset list of possible sources.

Loading Config by Specifying a Meta-configuration File

Your application loads the configuration specified by a meta-config file by invoking the MetaConfig.config(Config) method, passing a config object read from the meta-config source as the argument;

If you desire a Config.Builder instead of a fully built Config instance, you can use the Config.Builder.config(Config) method to update the builder with meta configuration. Your application can further fine-tune this builder before using it to construct a Config tree. The config system interprets the meta-config as directions for how to build a config tree, rather than as the config data itself.

Loading Config from an Implicit Meta-configuration File

The introduction section shows how to use Config.create() to load config from one of several possible default config files. That same method also searches for one of several possible default meta-config files from which to load config sources to be used for the default config.

The Config.create() method determines the default configuration from the following search:

  1. Attempt to load meta-config from at most one of the following files, first on the file system in current directory, then on classpath, checked in this order:
    1. meta-config.yaml - meta configuration file in YAML format
    2. meta-config.conf - meta configuration file in HOCON format
    3. meta-config.json - meta configuration file in JSON format
    4. meta-config.properties - meta configuration file in Java Properties format
  2. Otherwise, load config from:
    1. environment variables, and
    2. Java system properties, and
    3. at most one of the following, checking in this order:
      1. application.yaml - configuration file in YAML format
      2. application.conf - configuration file in HOCON format
      3. application.json - configuration file in JSON format
      4. application.properties - configuration file in Java Properties format

Remember that the config system will check for these default meta-config and config files only if the classpath includes the corresponding parsers. The introduction section on built-in formats section describes this further.

See javadoc Config.Builder.config(Config).

Meta-configuration File Format

This section is now available in config profile format

Loading Config using Meta-configuration

Here is how your application can use meta-configuration in a particular resource on the classpath to load a Config tree:

Loading Config using Meta-configuration
Config metaConfig = Config.create(classpath("config-meta-all.conf")); 
Config config = MetaConfig.config(metaConfig); 
Copied
  • We create a meta configuration from our meta configuration source
  • This method populates the config sources from all the actual sources declared in the meta-configuration.

Configuration Key

As described in the hierarchical features section each config node (except the root) has a non-null key. Here is the formal definition of what keys can be:

The ABNF syntax of config key
config-key = *1( key-token *( "." key-token ) )
 key-token = *( unescaped / escaped )
 unescaped = %x00-2D / %x2F-7D / %x7F-10FFFF
           ; %x2E ('.') and %x7E ('~') are excluded from 'unescaped'
   escaped = "~" ( "0" / "1" )
           ; representing '~' and '.', respectively
Copied

Important

To emphasize, the dot character (“.”) has special meaning as a name separator in keys. To include a dot as a character in a key escape it as “~1”. To include a tilda escape it as “~0”.

For example, the following configuration file contains two object nodes with names oracle and oracle.com.

Example application.json with dot character in key
{
    "oracle" : {
        "com" : true,
        "cz" : false
    },
    "oracle.com" : {
        "secured" : true
    }
}
Copied
Working with configuration with dot character in node name
Config config = Config.create(classpath("application.json"));

// node `oracle`
assert config.get("oracle.com").asBoolean().get() == true;                   
assert config.get("oracle").get("com").asBoolean().get() == true;            
assert config.get("oracle.com").type() == Type.VALUE;                        
assert config.get("oracle.com").name().equals("com");                        
// node `oracle.com`
assert config.get("oracle~1com.secured").asBoolean().get() == true;          
assert config.get(Key.escapeName("oracle.com"))                              
        .get("secured").asBoolean().get() == true;
assert config.get(Key.escapeName("oracle.com")).type() == Type.OBJECT;       
assert config.get(Key.escapeName("oracle.com")).name().equals("oracle.com"); 
Copied
  • Work with the first oracle object as usual. As always you can use the fully-qualified key oracle.com or chain get(key) calls to access the com property value.
  • Config node "oracle" / "com" is a leaf node (has type VALUE)…​
  • …​ and has the name com (the last token in its key).
  • The second object has name oracle.com. The code must escape the dot in the node’s name using oracle~1com.
  • Or, use the utility method Config.Key.escapeName(name) to escape dots or tildes that might be in the node’s name, in this example in oracle.com.
  • The config node "oracle.com" has type OBJECT…​
  • …​and name "oracle.com".

Filter, Overrides, and Token Substitution

When your application retrieves a config value, the config system can transform it before returning the value, according to filters, overrides, and tokens. The config system provides some built-in instances of these you can use, and you can add your own as described in the sections which describe filters and overrides.

Your application can add filters and overrides explicitly to a config builder and the config system by default uses the Java service loader mechanism to locate all available filters and overrides and add them automatically to all config builders (unless your code disables that behavior for a given builder).

Filters

Each filter accepts a key and the value as defined in the source, and returns the value to be used. The filter can leave the value unchanged or alter it, as it sees fit.

The built-in value-resolving filter enables the token substitution described below.

See the ConfigFilter JavaDoc for more information.

Overrides

The overrides feature allows you to create an external document containing key/value pairs which replace the value otherwise returned for the name, and then add that document as an override source to a config builder.

There are some key differences between overrides and filters.

  • Because overrides are loaded from sources those sources can change while your application runs and so the overrides they that prescribe can change.

  • The override document can use wildcards in key expressions.

  • Overrides can affect only keys that already exist in the original source; filters can supply values even if the key is absent from the config source.

Each override entry consists of a Java properties-format definition. The key is an expression (which can use wildcards) to match config keys read from the current config sources, and the override value is the new value for any key matching the key expression from that entry. Order is important. The config system tests every key expression/value pair one by one in the order they appear in the overrides sources. Once the config system finds an override entry in which the key expression matches the configuration key, the system returns that entry’s value for the key being processed.

See the OverrideSource JavaDoc for more detail.

Tokens

A token reference is a key token starting with $, optionally enclosed between { and }, i.e. $ref, ${ref}. Even a key composed of more than one token can be referenced in another key, i.e. ${env.ref}.

As an example use case, you can use token references to declare the default values (see resolving-tokens.yaml below), while the references may be resolved in another config source, which identifies a current environment (see env.yaml examples below). You can then use the same overrides for different environments, say test and prod. The configuration in each environment is then overridden with a different values using wildcards (see overrides.properties below).

Initialize Config with Override Definition from overrides.properties file
Config config = Config.builder()
        .overrides(OverrideSources.file("conf/overrides.properties")) 
        .sources(file("conf/env.yaml"),                               
                 classpath("resolving-tokens.yaml"))                  
        .build();
Copied
  • Loads overrides from the specified file.
  • A deployment-specific environment configuration file.
  • A default configuration containing token references that are resolved using the environment-specific override.

You can disable key and value token replacement separately as the following example shows.

Disabling Key and Value Token Replacement
Config config = Config.builder()
        .disableKeyResolving()
        .disableValueResolving()
        // other Config builder settings
        .build();
Copied

Executors for Asynchronous Config Activity

Various parts of the config system work asychronously:

  • polling strategies to detect changes to config sources,

  • publishers to notify your application when such changes occur,

  • Config instances which subscribe to and respond to change notifications for their underlying sources, and

  • retry policies (which might wait between retries).

Each of these uses an executor to perform its work. The config system provides default executors, but your application can specify different ones if necessary.

Executors for Polling Strategy

The two methods PollingStrategies.regular(Duration) and PollingStrategies.watch(Path) return builders for their respective strategies. Both builders expose the executor method which your application can invoke, passing a java.util.concurrent.ScheduledExecutorService instance it wants used for the polling work. By default each polling strategy instance uses a separate thread pool executor.

The following example shares the same executor for two different polling strategy instances.

Customize polling strategy executors
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 

Config config = Config.create(
        ConfigSources.file("conf/dev.properties")
                .pollingStrategy(
                        PollingStrategies.regular(Duration.ofSeconds(2)) 
                                .executor(executor)),                    
        ConfigSources.create("conf/config.properties")
                .pollingStrategy(
                        path -> PollingStrategies.watch(path)            
                                .executor(executor)));                   
Copied
  • Prepares a thread pool executor with core pool size set 2 to be shared by all polling strategies.
  • Selects the built-in periodic polling strategy.
  • Tells the config system to use the specific executor to poll the dev.properties config source.
  • Uses the Java filesystem WatchService to monitor the specified path.
  • Tells the config system to use the same executor to monitor the path.

Publishers for Source Change Events

Recall that when a polling strategy detects a change in a source, it informs interested parties of the changes. By default each Config.Builder arranges for the resulting Config tree to use a shared executor that reuses available threads from a pool, creating new threads as needed. The same executor is used for actually reloading the source.

Your application can invoke the polling strategy builder’s changesExecutor method to tell the builder to use a different Executor. (As an aside, your application can also control the size of the buffer used for holding source change events by invoking the builder’s changesMaxBuffer method. The default is 256.)

Customize config and override sources' executors
Executor executor = Executors.newCachedThreadPool();               

Config config = Config.builder()
        .overrides(
                OverrideSources.file("conf/overrides.properties")
                        .pollingStrategy(PollingStrategies::watch)
                        .changesExecutor(executor)                 
                        .changesMaxBuffer(4))                      
        .sources(
                ConfigSources.file("conf/env.yaml")
                        .pollingStrategy(PollingStrategies::watch)
                        .changesExecutor(executor)                 
                        .changesMaxBuffer(4))                      
        .build();
Copied
  • Prepares a thread pool executor to be shared by selected sources.
  • Tells the builder that the resulting overrides source should use the specified Executor for notifying interested parties of changes and for reloading the override source.
  • Specifies an event buffer size of 4.
  • Uses the same Executor and event buffer size for the config source as for the override source above.

Composite Config Source Executor

When your application supplies multiple sources to a config builder, as with Config.create(Supplier<ConfigSource>…​) and Config.create(List<Supplier<ConfigSource>>), the config system automatically uses a composite config source which aggregates the separate sources but also listens for changes to any of the individual sources so it can delegate the change notification. For this change detection and notification the config system, by default, uses an executor with a dedicated thread pool that is shared across all Config instances.

Your application can invoke the builder’s changesExecutor method to use a different ScheduledExecutorService instance. The builder returned by the from methods mentioned above is a CompositeBuilder which extends Config.Builder.

Because a composite source might yield more numerous change events — because of the multiple underlying sources — your application can specify a debounce timeout for the composite source by invoking the CompositeBuilder.changesDebounce(Duration) method. The composite source aggregates multiple change events within this period into a single event and broadcasts that one instead and reloads the sources at that time, not necessarily in response to every single change in any source. The default is 100 milliseconds.

Customize composite source executors
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);       

Config config = Config.create(
        ConfigSources.create(file("conf/dev.properties")                         
                                   .pollingStrategy(PollingStrategies::watch),
                           file("conf/config.properties")                      
                                   .pollingStrategy(PollingStrategies::watch))
                .changesExecutor(executor)                                     
                .changesMaxBuffer(4)                                           
                .changesDebounce(Duration.ofSeconds(1)));                      
Copied
  • Prepares a thread pool executor.
  • ConfigSources.create(Supplier<ConfigSource>…​) creates and returns a CompositeBuilder based on the two sources.
  • Specifies a particular executor for monitoring and change event notification.
  • Sets the subscriber’s buffer size to 4 events. The composite source discards any events not consumed by a subscriber if it needs to create room for more recent events.
  • Change events will not fire more frequently than once per a second.

Config Custom Executor

A loaded config tree subscribes to change events publishes by its source(s). By default, each Config uses an executor which manages a dedicated thread pool reusing previously-created threads when they are available and creating new threads as needed. All Config instances share the dedicated thread pool.

Your application can specify a non-default Executor for a tree to use for accepting and propagating those events by invoking the changesExecutor method on the Config.Builder. Each source subscriber has a dedicated buffer for holding changes events. This defaults to 256 but you can tailor this value as needed.

Customize config executor
Executor executor = Executors.newCachedThreadPool();        

Config config = Config.create(
        file("conf/config.properties")
                .pollingStrategy(PollingStrategies::watch))
        .changesExecutor(executor)                          
        .changesMaxBuffer(16)                               
        .build();
Copied
  • Prepares a specific thread pool executor.
  • Specifies the executor the Config tree will use to listen for and propagate change events.
  • Sets the event subscriber buffer to 16 events.

Retry Policy Custom Executor

You can control which executor a retry policy should use for its work. The RetryPolicies.repeat(int retries) method returns a RetryPolicies.Builder. Your application can invoke the retry policy builder’s executor method to specify which ScheduledExecutorService instance it should use to schedule and execute delayed retries. By default the config system uses a separate thread pool executor for each retry policy instance.

Customize retry policy executors
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2, myThreadFactory); 

Config config = Config.create(
        ConfigSources.file("conf/dev.properties")
                .optional()                                                               
                .retryPolicy(RetryPolicies.repeat(2)                                      
                        .executor(executor)));                                            
Copied
  • Prepares a thread pool executor with core pool size set to 2 and a custom java.util.concurrent.ThreadFactory.
  • When the source is flagged as optional(), the loading attempt will be repeated as the retry policy defines, but an overall failure will not lead to failing the initial load or preventing the source from being polled if so configured.
  • Uses the built-in repeating implementation of RetryPolicy that can be used with any config source, but typically for ones that might suffer brief, intermittent outages.
  • Specifies the executor to use for loading and retries.