Configuration
Module is responsible for mapping the values of configuration files to classes in Kora and then using them for application settings.
HOCON¶
Support for HOCON is implemented with Typesafe Config. HOCON is a JSON-based config file format. The format is less strict than JSON and has a slightly different syntax.
services {
    foo {
      bar = "SomeValue" //(1)!
      baz = 10 //(2)!
      propRequired = ${REQUIRED_ENV_VALUE} //(3)!
      propOptional = ${?OPTIONAL_ENV_VALUE} //(4)!
      propDefault = 10
      propDefault = ${?NON_DEFAULT_ENV_VALUE} //(5)!
      propReference = ${services.foo.bar}Other${services.foo.baz} //(6)!
      propArray = ["v1", "v2"] //(7)!
      propArrayAsString = "v1, v2" //(8)!
      propMap = { //(9)!
          "k1" = "v1"
          "k2" = "v2"
      }
      propObject = { //(10)!
          p1 = "v1"
          p2 = "v2"
      }
      propObjects = [ //(11)!
        {
          p1 = "v1"
          p2 = "v2"
        },
        {
          p1 = "v3"
          p2 = "v4"
        }
      ]
    }
}
- String configuration value
- Numeric configuration value
- Mandatory configuration value that is substituted from the REQUIRED_ENV_VALUEenvironment variable.
- Optional configuration value which is substituted from the OPTIONAL_ENV_VALUEenvironment variable, if no such variable is found, the configuration value will be omitted. 5.
- Configuration value with default value, the default value is specified in propDefault = 10and ifNON_DEFAULT_ENV_VALUEenvironment variable is found, its value will replace the default value.
- Configuration value assembled from substitutions of other parts of the configuration and the Othervalue between the
- String list configuration value, the value is set as an array of strings or can also be set as a string with values separated by commas
- String list configuration value, the value is set as a string with values separated by commas or can also be set as an array of strings
- Configuration value as a dictionary key and value
- Configuration value as a mapped class
- Configuration value as a list of mapped classes
Configuration representation in code:
@ConfigSource("services.foo")
public interface FooConfig {
    String bar();
    Integer baz();
    String propRequired();
    @Nullable
    String propOptional();
    Integer propDefault();
    String propReference();
    List<String> propArray();
    List<String> propArrayAsString();
    Map<String, String> propMap();
    @ConfigValueExtractor
    public interface ObjectConfig {
        String p1();
        String p2();
    }
    ObjectConfig propObject();
    List<ObjectConfig> propObjects();
}
@ConfigSource("services.foo")
interface FooConfig {
    fun bar(): String
    fun baz(): Int
    fun propRequired(): String
    fun propOptional(): String?
    fun propDefault(): Int
    fun propReference(): String
    fun propArray(): List<String>
    fun propArrayAsString(): List<String>
    fun propMap(): Map<String, String>
    @ConfigValueExtractor
    interface ObjectConfig {
        fun p1(): String
        fun p2(): String
    }
    fun propObject(): ObjectConfig
    fun propObjects(): List<ObjectConfig>
}
Dependency¶
Dependency build.gradle:
Module:
Dependency build.gradle.kts:
Module:
File¶
By default, the configuration files reference.conf and application.conf are expected.
First, all reference.conf files are merged, second, the application.conf file is overlaid on the unresolved
reference.conf file, the result is calculated and checked that all variable values are available.
It is assumed that the application configuration is in application.conf and the library configurations are in reference.conf.
Prioritize reading the application.conf configuration file:
- Use the file from config.resourceif specified (file fromresourcesdirectory)
- Use the file from config.fileif specified (file from the file system)
- Use the application.conffile if available (file fromresourcesdirectory)
- Use an empty configuration file if none of the above is present
YAML¶
Support for YAML is implemented using SnakeYAML.
services:
    foo:
        bar: "SomeValue" #(1)!
        baz: 10 #(2)!
        propRequired: ${REQUIRED_ENV_VALUE} #(3)!
        propOptional: ${?OPTIONAL_ENV_VALUE} #(4)!
        propDefault: ${?NON_DEFAULT_ENV_VALUE:10} #(5)!
        propReference: ${services.foo.bar}Other${services.foo.baz} #(6)!
        propArray: ["v1", "v2"] #(7)!
        propArrayAsString: "v1, v2" #(8)!
        propMap: #(9)!
            k1: "v1"
            k2: "v2"
        propObject: #(10)!
            p1: "v1"
            p2: "v2"
        propObjects: #(11)!
            - p1: "v1"
              p2: "v2"
            - p1: "v1"
              p2: "v2"
- String configuration value
- Numeric configuration value
- Mandatory configuration value that is substituted from the REQUIRED_ENV_VALUEenvironment variable.
- Optional configuration value which is substituted from the OPTIONAL_ENV_VALUEenvironment variable, if no such variable is found, the configuration value will be omitted. 5.
- Configuration value with default value, the default value is specified as 10and ifNON_DEFAULT_ENV_VALUEenvironment variable is found, its value will replace the default value.
- Configuration value assembled from substitutions of other parts of the configuration and the Othervalue between the
- String list configuration value, the value is set as an array of strings or can also be set as a string with values separated by commas
- String list configuration value, the value is set as a string with values separated by commas or can also be set as an array of strings
- Configuration value as a dictionary key and value
- Configuration value as a mapped class
- Configuration value as a list of mapped classes
Configuration representation in code:
@ConfigSource("services.foo")
public interface FooConfig {
    String bar();
    Integer baz();
    String propRequired();
    @Nullable
    String propOptional();
    Integer propDefault();
    String propReference();
    List<String> propArray();
    List<String> propArrayAsString();
    Map<String, String> propMap();
    @ConfigValueExtractor
    public interface ObjectConfig {
        String p1();
        String p2();
    }
    ObjectConfig propObject();
    List<ObjectConfig> propObjects();
}
@ConfigSource("services.foo")
interface FooConfig {
    fun bar(): String
    fun baz(): Int
    fun propRequired(): String
    fun propOptional(): String?
    fun propDefault(): Int
    fun propReference(): String
    fun propArray(): List<String>
    fun propArrayAsString(): List<String>
    fun propMap(): Map<String, String>
    @ConfigValueExtractor
    interface ObjectConfig {
        fun p1(): String
        fun p2(): String
    }
    fun propObject(): ObjectConfig
    fun propObjects(): List<ObjectConfig>
}
Dependency¶
Dependency build.gradle:
Module:
Dependency build.gradle.kts:
Module:
File¶
By default, the reference.yaml and application.yaml configuration files are expected.
First, all reference.yaml files are merged, second, the application.yaml file is overlaid on an unresolved
reference.yaml file, the result is calculated and it is checked that all variable values are available.
It is assumed that the application configuration is in the application.yaml file and the library configurations are in reference.yaml.
Prioritize reading the application.yaml configuration file:
- Use the file from config.resourceif specified (file fromresourcesdirectory)
- Use the file from config.fileif specified (file from the file system)
- Use the application.yamlfile if available (file fromresourcesdirectory)
- Use an empty configuration file if none of the above is present
Custom configuration¶
A custom configuration provides a mapping of the configuration file to a user interface. Such a user interface can later be injected as a dependency along with other components.
Application config¶
In order to simplify the creation of custom configurations, the @ConfigSource annotation should be used:
This code sample will add an instance of the FooServiceConfig class to the dependency container, which when created will expect the following kind of configuration:
After that, the FooServiceConfig class can already be used as a dependency in other classes:
Library config¶
In order to create custom configurations within custom libraries, use the @ConfigValueExtractor annotation
which will create rules for processing a configuration file into an instance of a configuration class.
Let's consider an example when there is such a configuration class:
In order for the library to provide configuration, you need to implement the factory in a module:
The factory will expect a configuration of the following kind:
Then by connecting the FooLibraryModule module in the application, the FooServiceConfig config can be used as a dependency in other classes.
Required values¶
By default, all values declared in the config are considered required (NotNull) and must be present in the configuration file.
Optional values¶
If you need to specify a value from the configuration file as optional, you can use this format:
It is suggested to use the @Nullable annotation over the method signature:
@ConfigSource("services.foo")
public interface FooServiceConfig {
    @Nullable//(1)!
    String bar();
    int baz();
}
- Any @Nullableannotation will do, such asjavax.annotation.Nullable/jakarta.annotation.Nullable/org.jetbrains.annotations.Nullable/ etc.
It is expected to use the Kotlin Nullability syntax and mark such a parameter as Nullable:
Default values¶
If there is a need to use default values in a class, you can use this format:
Injecting configuration¶
You can inject the base class ru.tinkoff.kora.config.common.Config which provides a common abstraction over the
configuration file mapping. The resulting configuration mapping consists of several layers that represent:
- Environment variables
- System variables
- Configuration file
Environment variables¶
In case you want to embed the configuration only environment variables,
you can use the @Environment annotation as a tag for the configuration class:
System variables¶
In case you want to inject a configuration of only system variables,
then you can use the @SystemProperties annotation as a tag for the configuration class:
Configuration file¶
In case you want to inject a complete application configuration that consists only of a configuration file,
you can use the @ApplicationConfig annotation as a tag for the configuration class:
Resulting configuration¶
If you want to inject a complete application configuration that consists of a configuration file, environment variables and system variables, you simply inject the configuration class without the tag:
Recommendations¶
Recommendation
We do not recommend using ru.tinkoff.kora.config.common.Config directly as a dependency in components,
because when you update the configuration it will cause all graph components that use it to be updated,
it is recommended to always create custom user configuration interfaces.
Config Watcher¶
By default, Kora has a configuration file watcher that updates the contents of the configuration file, which causes the dependency graph for the affected components to be updated if the configuration file is changed.
You can disable the watcher by using:
- Environment variable KORA_CONFIG_WATCHER_ENABLED.
- System property kora.config.watcher.enabled.
Supported types¶
Configuration Extractors provide an extensive list of supported types that covers most of what
you might need to specify in custom configurations, or you can extend the behavior with your custom ConfigValueExtractor<T> component.
List of supported types
- boolean / Boolean
- short / Short
- int / Integer
- long / Long
- double / Double
- float / Float
- double[]
- String
- BigInteger
- BigDecimal
- Period
- Duration
- Size
- Properties
- Pattern
- UUID
- Properties
- LocalDate
- LocalTime
- LocalDateTime
- OffsetTime
- OffsetDateTime
- Enum (any custom ENUM type) (Change mapping change toString()contract)
- List<T>(where- Tis any of the above listed types)
- Set<T>(where- Tis any of the above types)
- Map<K, V>(where- Kor- Vis any of the above types)
- Either<A, B>(where- Aand- Bare any of the above types)
Size¶
Size is a special type that allows you to specify the size of bytes in a human-friendly system of calculations according to both the IEEE 1541-2002 (binary) standard and the SI (decimal) standard.
Example values:
- 1Mb- 1 megabytes (- 1.000.000bytes)
- 1Mib- 1 megabit (- 1.048.576bytes)
- 1024b- 1024 bytes
- 1024- 1024 bytes
If just a number without a suffix is specified, it is considered that bytes are specified.