Avaje Config

avaje-config provides external configuration for JVM apps. We can provide configuration via yaml or properties files and specify which files to load using command line argument and resources.

License Source API Docs Issues Releases
Apache2 Github Javadoc Github Latest 1.0

 

Dependency

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-config</artifactId>
  <version>1.0</version>
</dependency>

 

Expression evaluation

Expressions start with ${ end with }. They can optionally define a default value using a : as we see in the example below for the username and password which have default values of mydb and notSecure respectively.

Expressions are evaluated using environment variables, system properties as well as other properties.

example
app.name: myapp
images.home: ${user.home}/myapp/images

datasource:
  db:
    username: ${DB_USER:mydb}
    password: ${DB_PASS:notSecure}
    url: ${DB_URL}

Loading configuration

avaje-config provides ways to load external configuration for running an application and running tests. It also provides a simple mechanism to make it easy to run the application locally (typically in order to run integration tests locally).

Running app

There are four main ways to provide configuration used for the purpose of running the application.

1. Default values via main resources

We can provide "default" configuration values by putting into src/main/resources either application.yaml or application.properties.

These values act as default values and can be overwritten by configuration provided by other sources such as files specified by command line arguments.

2. Command line arguments

We can specify external configuration files to use via command line arguments. Arguments proceeded by -P are considered possible configuration files and avaje-config will try to load them. If they are not valid files that exist then they are ignored.

example
java -jar myapp.jar -P/etc/config/myapp.properties -P/etc/other.yaml

In the example above 2 external files are configuration files that are loaded providing configuration values for the application.

3. load.properties

Optionally we specify a load.properties property to define configuration files to load.

example
## in application.yaml
load.properties: /etc/config/myapp.properties /etc/other.yaml

After default configuration files are loaded the load.properties property is read and if specified these configuration files are loaded.

4. Plugins

We can also use plugins that implement ConfigurationSource to load configuration from other sources. Refer to the Plugins section for more details.

Running Tests

To provide configuration for running tests add into src/test/resources either application-test.yaml or application-test.properties.

The configuration from these files is loaded and used when running tests. This configuration will override any configuration supplied by files in src/main/resources.

Running locally

When we run our application locally we can provide configuration for this via properties or yaml files in a ~/.localdev directory.

For example, when running locally we want to provide configuration that configures our application to use a local database. We want to do this when we run integration tests locally running application(s).

app.name

We must specify a app.name property to do this.

example
app.name=myapp

Then in ~/.localdev put a yaml or properties configuration file using the app.name. For example, given app.name=myapp have either ~/.localdev/myapp.yaml or ~/.localdev/myapp.properties define the configuration we want to use when running locally.

Startup

avaje-config will initialise and load configuration when it is first used.

Initialisation

 

config.load.systemProperties

If we set config.load.systemProperties to true then all the properties that have been loaded are then set into system properties.

File watching

If config.watch.enabled is set to true then avaje-config will watch the config files and reload configuration when the files change.

By default it will check the config files every 60 seconds. We can change this by setting config.watch.period

example

config:
  watch:
    enabled: true
    period: 10   ## check every 10 seconds

We can use this as a simple form of feature toggling. For example if we are using kubernetes and have configuration file based on a config map, we can edit the config map to enable/disable features.

config.watch.enabled: true

## some config values to toggle features on/off
feature.cleanup.enabled: true
feature.processEmails.enabled: false
if (Config.enabled("feature.cleanup.enabled") {
  ...
}

Config

We use Config to access the application configuration. Config can be used anywhere in application code - static initialisers, constructors etc. There is no limitation on where we can use Config.

Config has convenient static methods to access the underlying configuration and this is how the majority of applications will use it. It also has asConfiguration() to return the underlying configuration and asProperties() to return the loaded configuration as standard properties.

Get property

Config provides method to get property values as String, int, long, boolean, BigDecimal, Enum and URL.

examples
// get a String property
String value = Config.get("myapp.foo");

// with a default value
String value = Config.get("myapp.foo", "withDefaultValue");
int port = Config.getInt("app.port");
long val = Config.getInt("foo", 42);
long port = Config.getLong("app.port");
long val = Config.getLong("foo", 42);
boolean val = Config.getBool("feature.cleanup");
boolean val = Config.getBool("feature.cleanup", true);

// Config.enabled() is an alias for Config.getBool()
boolean val = Config.enabled("feature.cleanup");
boolean val = Config.enabled("feature.cleanup", true);
BigDecimal val = Config.getDecimal("myapp.tax.rate");
BigDecimal val = Config.getDecimal("myapp.tax.rate", "12.5");
MyEnum val = Config.getEnum(MyEnum.class, "myapp.code");
MyEnum val = Config.getEnum(MyEnum.class, "myapp.code", MyEnum.DEFAULT);
URL val = Config.getURL("other.url");
URL val = Config.getURL("other.url", "https://other:8090");
List and Set

We can also return configuration values as List or Set of String, int and long.

List<Integer> codes = Config.getList().ofInt("my.codes", 42, 54);

Set<String> operations = Config.getSet().of("my.operations", "put", "delete");

Set property

Use setProperty() to set configuration values.

When we set values this will fire any configuration callback listeners that are registered for the key.

Config.setProperty("other.url", "https://bazz");

Config.setProperty("feature.cleanup", "false");

On change

We can register callbacks that execute when a configuration key has it's value changed.

example
Config.onChange("other.url", newUrl -> {
  // do something interesting with the changed
  // config value
  ...
});

asProperties()

Obtain all the configuration values as standard properties using Config.asProperties()

Properties properties = Config.asProperties();

asConfiguration()

Obtain the underlying configuration via Config.asConfiguration(). Config is providing static singleton scope for the underlying Configuration. We generally always use Config because it is convenient and there should not be much need to access the underlying configuration.

Configuration configuration = Config.asConfiguration();

Plugins

Plugins implement the ConfigurationSource interface and found and registered via ServiceLoader. This means they have a file at src/main/resources/META-INF/services/io.avaje.config.ConfigurationSource which contains the class name of the implementation.

Plugins implement the method

void load(Configuration configuration);

They are able to use configuration to read properties if needed, for example read the host name of a redis server to read configuration from.

Plugins typically read their configuration source and then use setProperty(key, value) to set the properties into configuration. The values can contain expressions and will have their expressions evaluated as part of setProperty.

Refer to the (silly) example plugin - MyExternalLoader.java