All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.config.Config Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * Copyright (c) 2017, 2023 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.config;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import io.helidon.common.GenericType;
import io.helidon.common.config.ConfigException;
import io.helidon.common.config.GlobalConfig;
import io.helidon.config.spi.ConfigFilter;
import io.helidon.config.spi.ConfigMapper;
import io.helidon.config.spi.ConfigMapperProvider;
import io.helidon.config.spi.ConfigParser;
import io.helidon.config.spi.ConfigSource;
import io.helidon.config.spi.MergingStrategy;
import io.helidon.config.spi.OverrideSource;

/**
 * 

Configuration

* Immutable tree-structured configuration. *

Loading Configuration

* Load the default configuration using the {@link Config#create} method. *
{@code
 * Config config = Config.create();
 * }
Use {@link Config.Builder} to construct a new {@code Config} instance * from one or more specific {@link ConfigSource}s using the {@link #builder()}. *

* The application can affect the way the system loads configuration by * implementing interfaces defined in the SPI, by explicitly constructing the * {@link Builder} which assembles the {@code Config}, and by using other * classes provided by the config system that influence loading. *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Some Config SPI Interfaces
Class.MethodApplication-implemented InterfacePurpose
{@link ConfigSources#create}{@link ConfigSource}Loads configuration from a different type of origin. Each * {@code ConfigSource} implementation handles a type of location. Different * instances of a given {@code ConfigSource} implementation represent separate * sources of that location type.
{@link Builder#addParser}{@link ConfigParser}Converts one format of config representation into the corresponding * {@code Config} tree. *
{@link Builder#addFilter}{@link ConfigFilter}Changes the {@code String} representation of each config value from one * {@code String} to another as the {@code Config} tree is built from its * sources.
{@link OverrideSources}Replaces config {@code String} values during loading based on their keys. * Programs provide overrides in Java property file format on the classpath, at * a URL, or in a file, or by invoking {@link OverrideSources#create} and passing * the name-matching expressions and the corresponding replacement value as a * {@code Map}.
{@link Builder#addMapper(Class, Function)}Implements conversion from a {@code Config} node (typically with * children) to an application-specific Java type.
* *

Navigating in a Configuration Tree

* Each loaded configuration is a tree of {@code Config} objects. The * application can access an arbitrary node in the tree by passing its * fully-qualified name to {@link Config#get}: *
{@code
 * Config greeting = config.get("greeting");
 * }
Method {@link #key()} always returns fully-qualified * {@link Config.Key} of a config node. *
{@code
 * assert greeting.key().toString().equals("greeting")
 * }
These are equivalent ways of obtaining the same {@code Config} * instance, and the two assertions will succeed: *
{@code
 * Config name1 = config.get("app.services.svc1.name");
 * Config name2 = config.get("app").get("services.svc1.name");
 * Config name3 = config.get("app.services").get("svc1").get("name");
 * Config name4 = config.get("app").get("services").get("svc1").get("name");
 *
 * assert name4.key().equals(Key.create("app.services.svc1.name"))
 * assert name1 == name2 == name3 == name4
 * }
The {@link #get} method always returns a {@code Config} object, even * if no configuration is present using the corresponding key. The application * can invoke the {@link #type} method to find out the type of the node, * represented by one of the {@link Type} enum values. The {@link #exists} * method tells whether or not the {@code Config} node represents existing * configuration. *
{@code
 * if (!config.get("very.rare.prop42").exists()) {
 *     // node 'very.rare.prop42' does NOT exist
 * }
 * }
The {@link #traverse} method visits all nodes in a subtree. This * example gathers all nodes with keys matching {@code logging.**.level} -- that * is, all nodes within the "logging" subtree that have a key ending in "level" * and also has a single value: *
{@code
 * Map loggingLevels = config.get("logging")  // find "logging" subtree
 *     .traverse()                                           // traverse through logging' nodes
 *     .filter(node -> node.isLeaf())                        // filter leaf values
 *     .filter(node -> node.name().equals("level"))          // filter key suffix '.level'
 *     .collect(Collectors.toMap(Config::key, Config::asString));
 * }
*

* To retrieve children of a config node use * {@link #asNodeList()} *

    *
  • on an {@link Type#OBJECT object} node to get all object members,
  • *
  • on a {@link Type#LIST list} node to get all list elements.
  • *
*

* To get node value, use {@link #as(Class)} to access this config node as a {@link ConfigValue} * *

Converting Configuration Values to Types

*

Explicit Conversion by the Application

* The interpretation of a configuration node, including what datatype to use, * is up to the application. To interpret a node's value as a type other than * {@code String} the application can invoke one of these convenience methods: *
    *
  • {@code as} such as {@code asBoolean, asDouble, asInt}, etc. * which return {@link ConfigValue} representing Java primitive data values ({@code boolean, double, int}, etc.) *

    * The {@link ConfigValue} can be used to access the value or use optional style methods. *

    * The config value provides access to the value in multiple ways. * See {@link ConfigValue} for reference. *

    * Basic usages: *

    {@code
     * // throws a MissingValueException in case the config node does not exist
     * long l1 = config.asLong().get();
     * // returns 42 in case the config node does not exist
     * long l2 = config.asLong().orElse(42L);
     * // invokes the method "timeout(long)" if the value exists
     * config.asLong().ifPresent(this::timeout);
     * }
    *
  • *
  • {@link #as(Class)} to convert the config node to an instance of the specified class, if there is a configured * mapper present that supports the class. *
    {@code
     *   // throws a MissingValueException in case the config node does not exist
     *   // throws a ConfigMappingException in case the config node cannot be converted to Long
     *   long l1 = config.as(Long.class).get();
     *   // returns 42 in case the config node does not exist
     *   // throws a ConfigMappingException in case the config node cannot be converted to Long
     *   long l2 = config.as(Long.class).orElse(42L);
     *   // invokes the method "timeout(long)" if the value exists
     *   // throws a ConfigMappingException in case the config node cannot be converted to Long
     *   config.as(Long.class).ifPresent(this::timeout);
     *   }
    *
  • *
  • {@link #as(Function)} to convert the config node using the function provided. * Let's assume there is a method {@code public static Foo create(Config)} on a class {@code Foo}: *
    {@code
     *  // throws a MissingValueException in case the config node does not exist
     *  // throws a ConfigMappingException in case the config node cannot be converted to Foo
     *  Foo f1 = config.as(Foo::create).get();
     *  // throws a ConfigMappingException in case the config node cannot be converted to Foo
     *  Foo f2 = config.as(Foo::create).orElse(Foo.DEFAULT);
     *  // invokes the method "foo(Foo)" if the value exists
     *  // throws a ConfigMappingException in case the config node cannot be converted to Foo
     *  config.as(Foo::create).ifPresent(this::foo);
     * }
    *
  • *
  • {@link #as(GenericType)} to convert the config node to an instance of the specified generic type, if there is a * configured mapper present that supports the generic type. *
    {@code
     *  // throws a MissingValueException in case the config node does not exist
     *  // throws a ConfigMappingException in case the config node cannot be converted to Map
     *  Map m1 = config.as(new GenericType() {}).get();
     *  // throws a ConfigMappingException in case the config node cannot be converted to Map
     *  Map m1 = config.as(new GenericType() {}).orElseGet(Collections::emptyMap);
     *  // invokes the method "units(Map)" if the value exists
     *  // throws a ConfigMappingException in case the config node cannot be converted to Map
     *  config.as(new GenericType() {}).ifPresent(this::units);
     * }
    *
  • *
* * To deal with application-specific types, the application can provide its own * mapping logic by: *
    *
  • invoking the {@link Config#as(Function)} method variants,
  • *
  • adding custom mapping function implementations using the * {@link Builder#addMapper(Class, Function)} method,
  • *
  • add custom mapping function using the {@link Builder#addStringMapper(Class, Function)}
  • *
  • registering custom mappers using the Java service loader mechanism. (See * {@link ConfigMapperProvider} for details.) *
  • *
* *

* If there is no explicitly registered mapping function in a * {@link Builder} for converting a given type then the config system * throws {@link ConfigMappingException}, unless you use the config beans support, * that can handle classes that fulfill some requirements (see documentation), such as a public constructor, * static "create(Config)" method etc. *

*

Handling Multiple Configuration * Sources

* A {@code Config} instance, including the default {@code Config} returned by * {@link Config#create}, might be associated with multiple {@link ConfigSource}s. The * config system merges these together so that values from config sources with higher {@link io.helidon.common.Weight weight} * have priority over values from config sources with lower weight. */ public interface Config extends io.helidon.common.config.Config { /** * Generic type of configuration. */ GenericType GENERIC_TYPE = GenericType.create(Config.class); /** * Returns empty instance of {@code Config}. * * @return empty instance of {@code Config}. */ static Config empty() { return BuilderImpl.EmptyConfigHolder.EMPTY; } /** * Returns a new default {@link Config} loaded using one of the * configuration files available on the classpath and/or using the runtime * environment. *

* The config system loads the default configuration using a default {@link Builder} * which loads configuration data as described below. In contrast, the application can * control how and from where configuration is loaded by explicitly creating and fine-tuning * one or more {@code Builder} instances itself. *

    *
  1. Meta-configuration *

    * Meta-configuration specifies at least one {@link ConfigSource} or * {@link Config.Builder} from which the system can load configuration. The * config system searches for at most one of the following * meta-configuration locations on the classpath, in this order: *

      *
    1. {@code meta-config.yaml} - meta configuration file in YAML * format
    2. *
    3. {@code meta-config.conf} - meta configuration file in HOCON * format
    4. *
    5. {@code meta-config.json} - meta configuration file in JSON * format
    6. *
    7. {@code meta-config.properties} - meta configuration file in Java * Properties format
    *
  2. *
  3. Configuration *

    * In the absence of meta-configuration the config system loads the default * configuration from all of the following sources: *

      *
    1. {@link ConfigSources#environmentVariables() environment variables};
    2. *
    3. {@link ConfigSources#systemProperties() system properties}
    4. *
    5. at most one of following locations on the classpath, in this order: *
        *
      1. {@code application.yaml} - configuration file in YAML format
      2. *
      3. {@code application.conf} - configuration file in HOCON format
      4. *
      5. {@code application.json} - configuration file in JSON format
      6. *
      7. {@code application.properties} - configuration file in Java * Properties format
      8. *
      *
    6. *
    * The config system uses only the first {@code application.*} location it * finds for which it can locate a {@link ConfigParser} that supports the * corresponding {@link ConfigParser#supportedMediaTypes() media type}. *

    * When creating the default configuration the config system detects parsers * that were loaded using the {@link java.util.ServiceLoader} mechanism or, * if it finds none loaded, a built-in parser provided by the config system. *

  4. *
* Every invocation of this method creates a new {@code Config} instance * which has neither a {@link PollingStrategies#nop() polling strategy} nor * a {@link RetryPolicies#justCall() retry policy}. To set up these and other * behaviors the application should create explicitly a {@code Config.Builder}, * tailor it accordingly, and then use its {@code build} method to create the * {@code Config} instance as desired. * * @return new instance of {@link Config} */ static Config create() { return builder().metaConfig().build(); } /** * Creates a new {@link Config} loaded from environment variables, system * properties, and the specified {@link ConfigSource}s. *

* The resulting configuration uses the following sources, in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables config source}
    * Can disabled by {@link Builder#disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source} * Can disabled by {@link Builder#disableSystemPropertiesSource()}
  4. *
  5. Source(s) specified by user in the method.
  6. *
* See multiple sources for more information. * * @param configSources ordered list of configuration sources * @return new instance of {@link Config} * @see #builder(Supplier[]) * @see Builder#sources(List) * @see Builder#disableEnvironmentVariablesSource() * @see Builder#disableSystemPropertiesSource() */ @SafeVarargs static Config create(Supplier... configSources) { return builder(configSources).build(); } /** * Provides a {@link Builder} for creating a {@link Config} * based on the specified {@link ConfigSource} instances. *

* The resulting configuration uses the following sources, in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables}
    * Can be disabled by invoking {@link Builder#disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source}
    * Can be disabled by invoking {@link Builder#disableSystemPropertiesSource()}
  4. *
  5. source(s) passed in the method invocation
  6. *
* See multiple sources for more information. * * @param configSources ordered list of configuration sources * @return new initialized Builder instance * @see #builder() * @see #create(Supplier[]) * @see Builder#sources(List) * @see Builder#disableEnvironmentVariablesSource() * @see Builder#disableSystemPropertiesSource() */ @SafeVarargs static Builder builder(Supplier... configSources) { return builder().sources(List.of(configSources)); } /** * Provides a {@link Builder} for creating a {@link Config} instance. * * @return new Builder instance */ static Builder builder() { return new BuilderImpl(); } /** * Creates a new {@link Config} loaded from the specified {@link ConfigSource}s. * No other sources will be included. * * @param configSources ordered list of configuration sources * @return new instance of {@link Config} * @see #builder(Supplier[]) */ @SafeVarargs static Config just(Supplier... configSources) { return builder(configSources) .disableEnvironmentVariablesSource() .disableSystemPropertiesSource() .build(); } /** * Either return the registered global config, or create a new config using {@link #create()} and register * it as global. * The instance returned may differ from {@link io.helidon.common.config.GlobalConfig#config()} in case the * global config registered in not an instance of this type. * * @return global config instance, creates one if not yet registered */ static Config global() { if (GlobalConfig.configured()) { io.helidon.common.config.Config global = GlobalConfig.config(); if (global instanceof Config cfg) { return cfg; } return BuilderImpl.GlobalConfigHolder.get(); } Config config = Config.create(); GlobalConfig.config(() -> config, true); return config; } /** * Configure the provided configuration as the global configuration. * This method registers also {@link io.helidon.common.config.GlobalConfig} instance. * * @param config to configure as global */ static void global(Config config) { GlobalConfig.config(() -> config, true); BuilderImpl.GlobalConfigHolder.set(config); } /** * Returns the {@code Context} instance associated with the current * {@code Config} node that allows the application to access the last loaded * instance of the node or to request that the entire configuration be * reloaded. * * @return Context instance associated with specific Config node */ default Context context() { //default implementation does not support changes return new Context() { @Override public Instant timestamp() { return Config.this.timestamp(); } @Override public Config last() { return Config.this; } @Override public Config reload() { return Config.this; } }; } /** * Returns when the configuration tree was created. *

* Each config node of single Config tree returns same timestamp. * * @return timestamp of created instance of whole configuration tree. * @see #context() * @see Context#timestamp() */ Instant timestamp(); /** * Returns the fully-qualified key of the {@code Config} node. *

* The fully-qualified key is a sequence of tokens derived from the name of * each node along the path from the config root to the current node. Tokens * are separated by {@code .} (the dot character). See {@link #name()} for * more information on the format of each token. * * @return current config node key * @see #name() */ @Override Key key(); /** * Returns the last token of the fully-qualified key for the {@code Config} * node. *

* The name of a node is the last token in its fully-qualified key. *

* The exact format of the name depends on the {@code Type} of the * containing node: *

    *
  • from a {@link Type#OBJECT} node the token for a child is the * name of the object member;
  • *
  • from a {@link Type#LIST} node the token for a child is a zero-based * index of the element, an unsigned base-10 integer value * with no leading zeros.
  • *
*

* The ABNF syntax of config key is: *

{@code
     * 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
     * }
* * @return current config node key * @see #key() * @see Key#name() */ @Override default String name() { return key().name(); } /** * Returns the single sub-node for the specified sub-key. *

* The format of the key is described on {@link #key()} method. * * @param key sub-key of requested sub-node * @return config node for specified sub-key, never returns {@code null}. * @see #get(Key) */ @Override default Config get(String key) { Objects.requireNonNull(key, "Key argument is null."); return get(ConfigKeyImpl.of(key)); } @Override Config root(); /** * Returns the single sub-node for the specified sub-key. * * @param key sub-key of requested sub-node * @return config node for specified sub-key, never returns {@code null}. * @see #get(String) */ Config get(Config.Key key); /** * Returns a copy of the {@code Config} node with no parent. *

* The returned node acts as a root node for the subtree below it. Its key * is the empty string; {@code ""}. The original config node is unchanged, * and the original and the copy point to the same children. *

* Consider the following configuration: *

     * app:
     *      name: Example 1
     *      page-size: 20
     * logging:
     *      app.level = INFO
     *      level = WARNING
     * 
* The {@code Config} instances {@code name1} and {@code name2} represents same data and * in fact refer to the same object: *
     * Config name1 = config
     *                  .get("app")
     *                  .get("name");
     * Config name2 = config
     *                  .get("app")
     *                  .detach()               //DETACHED node
     *                  .get("name");
     *
     * assert name1.asString() == "Example 1";
     * assert name2.asString() == "Example 1";  //DETACHED node
     * 
* The only difference is the key each node returns: *
     * assert name1.key() == "app.name";
     * assert name2.key() == "name";            //DETACHED node
     * 
*

* See {@link #asMap()} for example of config detaching. * * @return returns detached Config instance of same config node */ @Override Config detach(); /** * Provides the {@link Type} of the {@code Config} node. * * @return the {@code Type} of the configuration node */ Type type(); /** * Returns {@code true} if the node exists, whether an object, a list, or a * value node. * * @return {@code true} if the node exists */ @Override default boolean exists() { return type().exists(); } /** * Returns {@code true} if this node exists and is a leaf node (has no * children). *

* A leaf node has no nested configuration subtree and has a single value. * * @return {@code true} if the node is existing leaf node, {@code false} * otherwise. */ @Override default boolean isLeaf() { return type().isLeaf(); } /** * Returns {@code true} if this node exists and is Type#Object. * * @return {@code true} if the node exists and is Type#Object, {@code false} * otherwise. */ @Override default boolean isObject() { return (Type.OBJECT == type()); } /** * Returns {@code true} if this node exists and is Type#List. * * @return {@code true} if the node exists and is Type#List, {@code false} * otherwise. */ @Override default boolean isList() { return (Type.LIST == type()); } /** * Returns {@code true} if this configuration node has a direct value. *

* This may be a value node (e.g. a leaf) or object node or a list node * (e.g. a branch with value). The application can invoke methods such as * {@link #as(Class)} on nodes that have value. * * @return {@code true} if the node has direct value, {@code false} otherwise. */ @Override boolean hasValue(); /** * Performs the given action with the config node if node * {@link #exists() exists}, otherwise does nothing. * * @param action the action to be performed if the node exists * @see #exists() * @see #type() */ default void ifExists(Consumer action) { asNode().ifPresent(action); } /** * Iterative deepening depth-first traversal of the node * and its subtree as a {@code Stream}. *

* If the config node does not exist or is a leaf the returned stream is * empty. *

* Depending on the structure of the configuration the returned stream can * deliver a mix of object, list, and leaf value nodes. The stream will * include and traverse through object members and list elements. * * @return stream of deepening depth-first sub-nodes */ default Stream traverse() { return traverse((node) -> true); } /** * Iterative deepening depth-first traversal of the node * and its subtree as a {@code Stream}, qualified by the specified * predicate. *

* If the config node does not exist or is a leaf the returned stream is * empty. *

* Depending on the structure of the configuration the returned stream can * deliver a mix of object, list, and leaf value nodes. The stream will * include and traverse through object members and list elements. *

* The traversal continues as long as the specified {@code predicate} * evaluates to {@code true}. When the predicate evaluates to {@code false} * the node being traversed and its subtree will be excluded from the * returned {@code Stream}. * * @param predicate predicate evaluated on each visited {@code Config} node * to continue or stop visiting the node * @return stream of deepening depth-first sub nodes */ Stream traverse(Predicate predicate); // instance utility /** * Convert a String to a specific type. * This is a helper method to allow for processing of default values that cannot be typed (e.g. in annotations). * * @param type type of the property * @param value String value * @param type * @return instance of the correct type * @throws ConfigMappingException in case the String provided cannot be converted to the type expected * @see Config#as(Class) */ T convert(Class type, String value) throws ConfigMappingException; /** * The mapper used by this config instance. * * @return configuration mapper */ ConfigMapper mapper(); // // accessors // /** * Typed value as a {@link ConfigValue} for a generic type. * If appropriate mapper exists, returns a properly typed generic instance. *

* Example: *

     * {@code
     * ConfigValue> myMapValue = config.as(new GenericType>(){});
     * myMapValue.ifPresent(map -> {
     *      Integer port = map.get("service.port");
     *  }
     * }
     * 
* * @param genericType a (usually anonymous) instance of generic type to prevent type erasure * @param type of the returned value * @return properly typed config value */ ConfigValue as(GenericType genericType); /** * Typed value as a {@link ConfigValue}. * * @param type type class * @param type * @return typed value * @see ConfigValue#map(Function) * @see ConfigValue#supplier() * @see ConfigValue#get() * @see ConfigValue#orElse(Object) */ @Override ConfigValue as(Class type); /** * Typed value as a {@link ConfigValue} created from factory method. * To convert from String, you can use * {@link #asString() config.asString()}{@link ConfigValue#as(Function) .as(Function)}. * * @param mapper method to create an instance from config * @param type * @return typed value */ ConfigValue as(Function mapper); @Override default io.helidon.common.config.ConfigValue map(Function mapper) { return as(mapper::apply); } // shortcut methods /** * Boolean typed value. * * @return typed value */ default ConfigValue asBoolean() { return as(Boolean.class); } /** * String typed value. * * @return typed value */ default ConfigValue asString() { return as(String.class); } /** * Integer typed value. * * @return typed value */ default ConfigValue asInt() { return as(Integer.class); } /** * Long typed value. * * @return typed value */ default ConfigValue asLong() { return as(Long.class); } /** * Double typed value. * * @return typed value */ default ConfigValue asDouble() { return as(Double.class); } /** * Returns list of specified type. * * @param type type class * @param type of list elements * @return a typed list with values * @throws ConfigMappingException in case of problem to map property value. */ @Override ConfigValue> asList(Class type) throws ConfigMappingException; /** * Returns this node as a list converting each list value using the provided mapper. * * @param mapper mapper to convert each list node into a typed value * @param type of list elements * @return a typed list with values * @throws ConfigMappingException in case the mapper fails to map the values */ ConfigValue> asList(Function mapper) throws ConfigMappingException; @Override default io.helidon.common.config.ConfigValue> mapList(Function mapper) throws ConfigException { return asList(mapper::apply); } /** * Returns existing current config node as {@link io.helidon.config.ConfigValue}. * * @return current config node as {@link io.helidon.config.ConfigValue} */ default ConfigValue asNode() { return ConfigValues.create(this, () -> exists() ? Optional.of(this) : Optional.empty(), Config::asNode); } @Override @SuppressWarnings("unchecked") ConfigValue> asNodeList() throws ConfigMappingException; /** * Transform all leaf nodes (values) into Map instance. *

* Fully qualified key of config node is used as a key in returned Map. * {@link #detach() Detach} config node before transforming to Map in case you want to cut * current Config node key prefix. *

* Let's say we work with following configuration: *

     * app:
     *      name: Example 1
     *      page-size: 20
     * logging:
     *      app.level = INFO
     *      level = WARNING
     * 
* Map {@code app1} contains two keys: {@code app.name}, {@code app.page-size}. *
{@code
     * Map app1 = config.get("app").asMap();
     * }
* {@link #detach() Detaching} {@code app} config node returns new Config instance with "reset" local root. *
{@code
     * Map app2 = config.get("app").detach().asMap();
     * }
* Map {@code app2} contains two keys without {@code app} prefix: {@code name}, {@code page-size}. * * @return new Map instance that contains all config leaf node values * @throws MissingValueException in case the node is {@link Type#MISSING}. * @see #traverse() * @see #detach() */ @Override ConfigValue> asMap() throws MissingValueException; // // config changes // /** * Register a {@link Consumer} that is invoked each time a change occurs on whole Config or on a particular Config node. *

* A user can subscribe on root Config node and then will be notified on any change of Configuration. * You can also subscribe on any sub-node, i.e. you will receive notification events just about sub-configuration. * No matter how much the sub-configuration has changed you will receive just one notification event that is associated * with a node you are subscribed on. * If a user subscribes on older instance of Config and ones has already been published the last one is automatically * submitted to new-subscriber. *

* Note: It does not matter what instance version of Config (related to single {@link Builder} initialization) * a user subscribes on. It is enough to subscribe just on single (e.g. on the first) Config instance. * There is no added value to subscribe again on new Config instance. * * @param onChangeConsumer consumer invoked on change */ default void onChange(Consumer onChangeConsumer) { // no-op } /** * Object represents fully-qualified key of config node. *

* Fully-qualified key is list of key tokens separated by {@code .} (dot character). * Depending on context the key token is evaluated one by one: *

    *
  • in {@link Type#OBJECT} node the token represents a name of object member;
  • *
  • in {@link Type#LIST} node the token represents an zero-based index of list element, * an unsigned base-10 integer value, leading zeros are not allowed.
  • *
*

* The ABNF syntax of config key is: *

{@code
     * 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
     * }
* * @see Config#key() */ interface Key extends io.helidon.common.config.Config.Key { /** * Returns instance of Key that represents key of parent config node. *

* If the key represents root config node it throws an exception. * * @return key that represents key of parent config node. * @see #isRoot() * @throws java.lang.IllegalStateException in case you attempt to call this method on a root node */ @Override Key parent(); /** * Create a child key to the current key. * * @param key child key (relative to current key) * @return a new resolved key */ @Override Key child(io.helidon.common.config.Config.Key key); /** * Creates new instance of Key for specified {@code key} literal. *

* Empty literal means root node. * Character dot ('.') has special meaning - it separates fully-qualified key by key tokens (node names). * * @param key formatted fully-qualified key. * @return Key instance representing specified fully-qualified key. */ static Key create(String key) { return ConfigKeyImpl.of(key); } /** * Escape {@code '~'} to {@code ~0} and {@code '.'} to {@code ~1} in specified name. * * @param name name to be escaped * @return escaped name */ static String escapeName(String name) { if (!name.contains("~") && !name.contains(".")) { return name; } StringBuilder sb = new StringBuilder(); char[] chars = name.toCharArray(); for (char ch : chars) { if (ch == '~') { sb.append("~0"); } else if (ch == '.') { sb.append("~1"); } else { sb.append(ch); } } return sb.toString(); } /** * Unescape {@code ~0} to {@code '~'} and {@code ~1} to {@code '.'} in specified escaped name. * * @param escapedName escaped name * @return unescaped name */ static String unescapeName(String escapedName) { return escapedName.replaceAll("~1", ".") .replaceAll("~0", "~"); } } // // enum node Type // /** * Configuration node types. */ enum Type { /** * Config node is an object of named members * ({@link #VALUE values}, {@link #LIST lists} or other objects). */ OBJECT(true, false), /** * Config node is a list of indexed elements * ({@link #VALUE values}, {@link #OBJECT objects} or other lists). */ LIST(true, false), /** * Config node is a leaf {@code String}-based single value, * member of {@link #OBJECT object} or {@link #LIST list} element. */ VALUE(true, true), /** * Config node does not exists. */ MISSING(false, false); private final boolean exists; private final boolean isLeaf; Type(boolean exists, boolean isLeaf) { this.exists = exists; this.isLeaf = isLeaf; } /** * Returns {@code true} if the node exists, either as an object, a list or as a value node. * * @return {@code true} if the node exists */ public boolean exists() { return exists; } /** * Returns {@code true} if this configuration node is existing a value node. *

* Leaf configuration node does not contain any nested configuration sub-trees, * but only a single associated value. * * @return {@code true} if the node is existing leaf node, {@code false} otherwise. */ public boolean isLeaf() { return isLeaf; } } // // interface Builder // /** * Context associated with specific {@link Config} node that allows to access the last loaded instance of the node * or to request reloading of whole configuration. */ interface Context { /** * Returns timestamp of the last loaded configuration. * * @return timestamp of the last loaded configuration. * @see Config#timestamp() */ Instant timestamp(); /** * Returns instance of Config node related to same Config {@link Config#key() key} * as original {@link Config#context() node} used to get Context from. *

* This method uses the last known value of the node, as provided through change support. * * @return the last instance of Config node associated with same key as original node * @see Config#context() */ Config last(); /** * Requests reloading of whole configuration and returns new instance of * Config node related to same Config {@link Config#key() key} * as original {@link Config#context() node} used to get Context from. * * @return the new instance of Config node associated with same key as original node * @see Config.Builder */ Config reload(); } /** * {@link Config} Builder. *

* A factory for a {@code Config} object. *

* The application can set the following characteristics: *

    *
  • {@code overrides} - instance of {@link OverrideSource override source};
  • *
  • {@code sources} - instances of {@link ConfigSource configuration source};
  • *
  • {@code mappers} - ordered list of mapper functions. * It is also possible to {@link #disableMapperServices disable} loading of * {@link ConfigMapperProvider}s as a {@link java.util.ServiceLoader service}.
  • *
  • {@code parsers} - ordered list of {@link ConfigParser configuration content parsers}. * It is also possible to {@link #disableParserServices disable} loading of * {@link ConfigParser}s as a {@link java.util.ServiceLoader service}.
  • *
  • {@code token reference resolving} - a resolving of reference tokens in a key can be {@link #disableKeyResolving() * disabled}
  • *
  • {@code filters} - ordered list of {@link ConfigFilter configuration value filters}. * It is also possible to {@link #disableFilterServices disable} loading of * {@link ConfigFilter}s as a {@link java.util.ServiceLoader service}.
  • *
  • {@code caching} - can be elementary configuration value processed by filter cached?
  • *
*

* In case of {@link ConfigMapperProvider}s, if there is no one that could be used to map appropriate {@code type}, * the mapping attempt throws a {@link ConfigMappingException}. *

* A more sophisticated approach can be achieved using the "config beans" module, that provides reflection access * and mapping for static factory methods, constructors, builder patterns and more. *

* If a {@link ConfigSource} is not specified, following default config source is used. Same as {@link #create()} uses. * It builds composite config source from following sources, checked in order: *

    *
  1. Tries to load configuration from meta one of following meta configuration files on classpath, in order: *
      *
    1. {@code meta-config.yaml} - meta configuration file in YAML format
    2. *
    3. {@code meta-config.conf} - meta configuration file in HOCON format
    4. *
    5. {@code meta-config.json} - meta configuration file in JSON format
    6. *
    7. {@code meta-config.properties} - meta configuration file in Java Properties format
    8. *
    *
  2. *
  3. Otherwise, configuration consists of: *
      *
    1. {@link ConfigSources#environmentVariables() Environment variables};
    2. *
    3. or else {@link ConfigSources#systemProperties() System properties}
    4. *
    5. one of following files on classpath, checked in order: *
        *
      1. {@code application.yaml} - configuration file in YAML format
      2. *
      3. {@code application.conf} - configuration file in HOCON format
      4. *
      5. {@code application.json} - configuration file in JSON format
      6. *
      7. {@code application.properties} - configuration file in Java Properties format
      8. *
      *
    6. *
    *
  4. *
* It uses the first and only one file that exists and there is a {@link ConfigParser} available * that supports appropriate {@link ConfigParser#supportedMediaTypes() media type}. * Available parser means that the parser: *
    *
  1. is loaded as a service using {@link java.util.ServiceLoader};
  2. *
  3. or if it does not exist, a config core built-in parser is used, if exists.
  4. *
* * @see Config#create() * @see ConfigSource * @see ConfigParser * @see ConfigFilter */ interface Builder extends io.helidon.common.Builder { /** * Sets ordered list of {@link ConfigSource} instance to be used as single source of configuration * to be wrapped into {@link Config} API. *

* Configuration sources found earlier in the list are considered to have a higher priority than the latter ones. I.e., * when resolving a value of a key, the sources are consulted in the order they have been provided and as soon as * the value is found in a configuration source, the value immediately is returned without consulting any of the remaining * configuration sources in the prioritized collection. *

* Target source is composed of following sources, in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables config source}
    * Can disabled by {@link #disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source} * Can disabled by {@link #disableSystemPropertiesSource()}
  4. *
  5. Source(s) specified by user in the method.
  6. *
* * @param configSources ordered list of configuration sources * @return an updated builder instance * @see #disableEnvironmentVariablesSource() * @see #disableSystemPropertiesSource() */ Builder sources(List> configSources); /** * Add a config source to the list of sources. * * @param source to add * @return updated builder instance */ Builder addSource(ConfigSource source); /** * Merging Strategy to use when more than one config source is used. * * @param strategy strategy to use, defaults to a strategy where a value for first source wins over values from later * sources * @return updated builder instance */ Builder mergingStrategy(MergingStrategy strategy); /** * Add a single config source to this builder. * * @param source config source to add * @return updated builder instance */ default Builder addSource(Supplier source) { return addSource(source.get()); } /** * Sets a {@link ConfigSource} instance to be used as a source of configuration to be wrapped into {@link Config} API. *

* Target source is composed of {@code configSource} and following sources (unless they are disabled) in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables config source}
    * Can disabled by {@link #disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source} * Can disabled by {@link #disableSystemPropertiesSource()}
  4. *
  5. Source(s) specified by user in the method.
  6. *
* * @param configSource the only config source * @return an updated builder instance * @see Config#create(Supplier...) * @see #sources(List) * @see #disableEnvironmentVariablesSource() * @see #disableSystemPropertiesSource() */ default Builder sources(Supplier configSource) { sources(List.of(configSource)); return this; } /** * Sets an ordered pair of {@link ConfigSource} instances to be used as single source of configuration * to be wrapped into {@link Config} API. *

* Target source is of from {@code configSource} and following sources (unless they are disabled) in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables config source}
    * Can disabled by {@link #disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source} * Can disabled by {@link #disableSystemPropertiesSource()}
  4. *
  5. Source(s) specified by user in the method.
  6. *
* * @param configSource the first config source * @param configSource2 the second config source * @return an updated builder instance * @see Config#create(Supplier...) * @see #sources(List) * @see #disableEnvironmentVariablesSource() * @see #disableSystemPropertiesSource() */ default Builder sources(Supplier configSource, Supplier configSource2) { sources(List.of(configSource, configSource2)); return this; } /** * Sets an ordered trio of {@link ConfigSource} instances to be used as single source of configuration * to be wrapped into {@link Config} API. *

* Target source is composed of config sources parameters and following sources (unless they are disabled) in order: *

    *
  1. {@link ConfigSources#environmentVariables() environment variables config source}
    * Can disabled by {@link #disableEnvironmentVariablesSource()}
  2. *
  3. {@link ConfigSources#systemProperties() system properties config source} * Can disabled by {@link #disableSystemPropertiesSource()}
  4. *
  5. Source(s) specified by user in the method.
  6. *
* * @param configSource the first config source * @param configSource2 the second config source * @param configSource3 the third config source * @return an updated builder instance * @see Config#create(Supplier...) * @see #sources(List) * @see #disableEnvironmentVariablesSource() * @see #disableSystemPropertiesSource() */ default Builder sources(Supplier configSource, Supplier configSource2, Supplier configSource3) { sources(List.of(configSource, configSource2, configSource3)); return this; } /** * Sets the source of an override source. *

* The feature allows user to override existing values with other ones, specified by wildcards. Default values might be * defined with key token references (i.e. {@code $env.$pod.logging.level: INFO}) that might be overridden by a config * source with a higher {@link io.helidon.common.Weight weight} to identify the current environment * (i.e. {@code env: test} and {@code pod: qwerty}). The * overrides are able to redefine values using wildcards (or without them). For example {@code test.*.logging.level = * FINE} overrides {@code logging.level} for all pods in test environment. *

* Override definitions are applied before any {@link ConfigFilter filter}. * * @param overridingSource a source with overriding key patterns and assigned values * @return an updated builder instance */ Builder overrides(Supplier overridingSource); /** * Disables any usage of resolving key tokens. *

* A key can contain tokens starting with {@code $} (i.e. $host.$port), that are resolved by default and tokens are * replaced with a value of the key with the token as a key. * * @return an updated builder instance */ Builder disableKeyResolving(); /** * When key resolving is enabled and a reference cannot be resolved, should we fail, or use the key verbatim. * Defaults to {@code false}, so key resolving does not fail when a reference is missing. * * @param shouldFail whether to fail when key reference cannot be resolved * @return updated builder * @see #disableKeyResolving() */ Builder failOnMissingKeyReference(boolean shouldFail); /** * Disables any usage of resolving value tokens. *

* A value can contain tokens enclosed in {@code ${}} (i.e. ${name}), that are resolved by default and tokens are replaced * with a value of the key with the token as a key. *

* By default, a value resolving filter is added to configuration. When this method is called, the filter will * not be added and value resolving will be disabled * @return an updated builder instance */ Builder disableValueResolving(); /** * When value resolving is enabled and a reference cannot be resolved, should we fail, or use the value verbatim. * Defaults to {@code false}, so value resolving does not fail when a reference is missing. * * @param shouldFail whether to fail when value reference cannot be resolved * @return updated builder * @see #disableValueResolving() */ Builder failOnMissingValueReference(boolean shouldFail); /** * Disables use of {@link ConfigSources#environmentVariables() environment variables config source}. * * @return an updated builder instance * @see ConfigSources#environmentVariables() */ Builder disableEnvironmentVariablesSource(); /** * Disables use of {@link ConfigSources#systemProperties() system properties config source}. * * @return an updated builder instance * @see ConfigSources#systemProperties() */ Builder disableSystemPropertiesSource(); /** * Registers mapping function for specified {@code type}. * The last registration of same {@code type} overwrites previous one. * Programmatically registered mappers have priority over other options. *

* As another option, mappers are loaded automatically as a {@link java.util.ServiceLoader service} * via {@link io.helidon.config.spi.ConfigMapperProvider} SPI unless it is {@link #disableMapperServices() disabled}. *

* And the last option, {@link ConfigMappers built-in mappers} are registered. * * @param type class of type the {@code mapper} is registered for * @param mapper mapping function * @param type the {@code mapper} is registered for * @return an updated builder instance * @see #addStringMapper(Class, Function) * @see #addMapper(ConfigMapperProvider) * @see #disableMapperServices */ Builder addMapper(Class type, Function mapper); /** * Register a mapping function for specified {@link GenericType}. * This is useful for mappers that support specifically typed generics, such as {@code Map} * or {@code Set>}. * To support mappers that can map any type (e.g. all cases of {@code Map}), * use {@link #addMapper(ConfigMapperProvider)} as it gives you full control over which types are supported, through * {@link ConfigMapperProvider#mapper(GenericType)}. * * @param type generic type to register a mapper for * @param mapper mapping function * @param type of the result * @return updated builder instance */ Builder addMapper(GenericType type, Function mapper); /** * Registers simple {@link Function} from {@code String} for specified {@code type}. * The last registration of same {@code type} overwrites previous one. * Programmatically registered mappers have priority over other options. *

* As another option, mappers are loaded automatically as a {@link java.util.ServiceLoader service} * via {@link io.helidon.config.spi.ConfigMapperProvider} SPI, if not {@link #disableMapperServices() disabled}. *

* And the last option, {@link ConfigMappers built-in mappers} are registered. * * @param type class of type the {@code mapper} is registered for * @param mapper mapper instance * @param type the {@code mapper} is registered for * @return an updated builder instance * @see #addMapper(ConfigMapperProvider) * @see ConfigMappers * @see #disableMapperServices */ Builder addStringMapper(Class type, Function mapper); /** * Registers a {@link ConfigMapperProvider} with a map of {@code String} to specified {@code type}. * The last registration of same {@code type} overwrites previous one. * Programmatically registered mappers have priority over other options. *

* As another option, mappers are loaded automatically as a {@link java.util.ServiceLoader service} * via {@link io.helidon.config.spi.ConfigMapperProvider} SPI, if not {@link #disableMapperServices() disabled}. *

* And the last option, {@link ConfigMappers built-in mappers} are registered. * * @param configMapperProvider mapper provider instance * @return modified builder instance * @see #addStringMapper(Class, Function) * @see ConfigMappers * @see #disableMapperServices */ Builder addMapper(ConfigMapperProvider configMapperProvider); /** * Disables automatic registration of mappers via {@link io.helidon.config.spi.ConfigMapperProvider} SPI * loaded as a {@link java.util.ServiceLoader service}. *

* Order of configuration mapper providers loaded as a service * is defined by {@link io.helidon.common.Weight} annotation. *

* Automatic registration of mappers as a service is enabled by default. * * @return an updated builder instance * @see io.helidon.config.spi.ConfigMapperProvider */ Builder disableMapperServices(); /** * Registers a {@link ConfigParser} instance that can be used by config system to parse * {@link io.helidon.config.spi.ConfigParser.Content} of {@link io.helidon.config.spi.ParsableSource}. * Parsers {@link io.helidon.config.spi.ConfigParser#supportedMediaTypes()} is queried * in same order as was registered by this method. * Programmatically registered parsers have priority over other options. *

* As another option, parsers are loaded automatically as a {@link java.util.ServiceLoader service}, if not * {@link #disableParserServices() disabled}. * * @param configParser parser instance * @return an updated builder instance * @see #disableParserServices */ Builder addParser(ConfigParser configParser); /** * Disables automatic registration of parsers loaded as a {@link java.util.ServiceLoader service}. *

* Order of configuration parsers loaded as a service is defined by {@link io.helidon.common.Weight} annotation. *

* Automatic registration of parsers as a service is enabled by default. * * @return an updated builder instance * @see ConfigParser */ Builder disableParserServices(); /** * Registers a {@link ConfigFilter} instance that will be used by {@link Config} to * filter elementary value before it is returned to a user. *

* Filters are applied in same order as was registered by this method, {@link * #addFilter(Function)} or {@link #addFilter(Supplier)} method. *

* {@link ConfigFilter} is actually a {@link java.util.function.BiFunction}<{@link String},{@link String},{@link * String}> where the first input parameter is the config key, the second is the original value and the result is the * new value. So the filter can be added as simply as: *

         *     Config.builder()
         *          .addFilter((key, originalValue) -> originalValue.toUpperCase())
         *          .build();
         * 
* * The config system will automatically load filters defined as a * {@link java.util.ServiceLoader service}, unless * {@link #disableFilterServices() disabled}. * * @param configFilter filter instance * @return an updated builder instance * @see #addFilter(Function) * @see #addFilter(Supplier) */ Builder addFilter(ConfigFilter configFilter); /** * Registers a {@link ConfigFilter} provider as a {@link Function}<{@link Config}, {@link ConfigFilter}>. An * obtained filter will be used by {@link Config} to filter elementary value before it is returned to a user. *

* Filters are applied in same order as was registered by the {@link #addFilter(ConfigFilter)}, this method, * or {@link #addFilter(Supplier)} method. *

* Registered provider's {@link Function#apply(Object)} method is called every time the new Config is created. E.g. when * this builder's {@link #build} method creates the {@link Config} or when the new * {@link Config#onChange(java.util.function.Consumer)} is fired with new Config instance with its own filter instance * is created. * * @param configFilterProvider a config filter provider as a function of {@link Config} to {@link ConfigFilter} * @return an updated builder instance * @see #addFilter(ConfigFilter) * @see #addFilter(Supplier) */ Builder addFilter(Function configFilterProvider); /** * Registers a {@link ConfigFilter} provider as a {@link Supplier}<{@link Function}<{@link Config}, {@link * ConfigFilter}>>. An obtained filter will be used by {@link Config} to filter elementary value before it is * returned to a user. *

* Filters are applied in same order as was registered by the {@link #addFilter(ConfigFilter)}, {@link * #addFilter(Function)}, or this method. *

* Registered provider's {@link Function#apply(Object)} method is called every time the new Config is created. E.g. when * this builder's {@link #build} method creates the {@link Config} or when the new * {@link Config#onChange(java.util.function.Consumer)} change event * is fired with new Config instance with its own filter instance is created. * * @param configFilterSupplier a config filter provider as a supplier of a function of {@link Config} to {@link * ConfigFilter} * @return an updated builder instance * @see #addFilter(ConfigFilter) * @see #addFilter(Function) */ Builder addFilter(Supplier> configFilterSupplier); /** * Disables automatic registration of filters loaded as a {@link java.util.ServiceLoader service}. *

* Order of configuration filters loaded as a service is defined by {@link io.helidon.common.Weight} annotation. *

* Automatic registration of filters as a service is enabled by default. * * @return an updated builder instance * @see ConfigFilter */ Builder disableFilterServices(); /** * Disables caching of elementary configuration values on {@link Config} side. *

* Caching is about {@link ConfigFilter}s. With disabled caching, registered filters are applied always you * access elementary configuration value. With enabled caching, registered filters are applied just once per * unique config node (key). Repeated access of already filtered key directly returns already cached value. *

* Caching is enabled by default. * * @return an updated builder instance * @see #addFilter(ConfigFilter) */ Builder disableCaching(); /** * Specifies "observe-on" {@link Executor} to be used by {@link Config#onChange(java.util.function.Consumer)} to deliver * new Config instance. * Executor is also used to process reloading of config from appropriate {@link ConfigSource source}. *

* By default, dedicated thread pool that creates new threads as needed, but * will reuse previously constructed threads when they are available is used. * * @param changesExecutor the executor to use for async delivery of {@link Config#onChange(java.util.function.Consumer)} * @return an updated builder instance * @see Config#onChange(java.util.function.Consumer) */ Builder changesExecutor(Executor changesExecutor); /** * Builds new instance of {@link Config}. * * @return new instance of {@link Config}. */ Config build(); /** * Check if meta configuration is present and if so, update this builder using * the meta configuration. * * @return updated builder instance * @see #config(Config) */ default Builder metaConfig() { MetaConfig.metaConfig() .ifPresent(this::config); return this; } /** * Configure this config builder from meta configuration. *

* The following configuration options are supported in a meta configuration file: * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Meta configuration
keydefault valuedescriptionreference
caching.enabled{@code true}Enable or disable caching of results of filters.{@link #disableCaching()}
key-resolving.enabled{@code true}Enable or disable resolving of placeholders in keys.{@link #disableKeyResolving()}
value-resolving.enabled{@code true}Enable or disable resolving of placeholders in values.{@link #disableValueResolving()}
parsers.enabled{@code true}Enable or disable parser services.{@link #disableParserServices()}
mappers.enabled{@code true}Enable or disable mapper services.{@link #disableMapperServices()}
override-sourcenoneConfigure an override source. Same as config source configuration (see below){@link #overrides(java.util.function.Supplier)}
sourcesDefault config sources are prefixed {@code application}, and suffix is the first available of * {@code yaml, conf, json, properties}Configure config sources to be used by the application. This node contains the array of objects defining * config sources{@link #addSource(io.helidon.config.spi.ConfigSource)}
* * Config source configuration options: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Config source
keydefault valuedescriptionreference
type Type of a config source - a string supported by a provider.{@link io.helidon.config.spi.ConfigSourceProvider#create(String, Config)}
multi-source{@code false}If set to true, the provider creates more than one config source to be added{@link io.helidon.config.spi.ConfigSourceProvider#createMulti(String, Config)}
properties Configuration options to configure the config source (meta configuration of a source){@link io.helidon.config.spi.ConfigSourceProvider#create(String, Config)}, * {@link MetaConfig#configSource(Config)}
properties.optionalfalseConfig sources can be configured to be optional{@link io.helidon.config.spi.Source#optional()}
properties.polling-strategy Some config sources can have a polling strategy defined{@link io.helidon.config.spi.PollableSource.Builder#pollingStrategy(io.helidon.config.spi.PollingStrategy)}, * {@link MetaConfig#pollingStrategy(Config)}
properties.change-watcher Some config sources can have a change watcher defined{@link io.helidon.config.spi.WatchableSource.Builder#changeWatcher(io.helidon.config.spi.ChangeWatcher)}, * {@link MetaConfig#changeWatcher(Config)}
properties.retry-policy Config sources can have a retry policy defined{@link io.helidon.config.spi.Source#retryPolicy()}, * {@link MetaConfig#retryPolicy(Config)}
* * Full meta configuration example: *
         * sources:
         *   - type: "system-properties"
         *   - type: "environment-variables"
         *   - type: "file"
         *     properties:
         *       optional: true
         *       path: "conf/dev-application.yaml"
         *       polling-strategy:
         *         type: "regular"
         *       retry-policy:
         *         type: "repeat"
         *         properties:
         *           retries: 5
         *    - type: "classpath"
         *      properties:
         *        optional: true
         *        resource: "application.yaml"
         * 
* * @param metaConfig meta configuration to set this builder up * @return updated builder from meta configuration */ Builder config(Config metaConfig); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy