A small library for enabling configuration parameters to be loaded in Java
This library is intended for use in small applications for loading parameters that may be set as system properties or environment variables.
Create a YML file to describe the properties to load, and a Java object to represent them.
Lightweight Config uses SnakeYML as its YML engine and loads the YML after first pre-processing it to import placeholders relating to environment variables or system properties.
NOTE: SnakeYml 2.0 is now the standard If you need SnakeYml 1.33, then use LightweightConfig version 1.2.1
It also allows some custom logic to be added to handle custom tags in the YML.
A thin wrapper around Properties.load this pre-processes properties files found in
resources or file system, in order to interpolate environment variables or
system properties.
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>lightweight-config</artifactId>
<version>1.3.0</version>
</dependency>Assuming a config file in resources of config.yml and a class
that models that file called Configuration:
Configuration configuration =
ConfigLoader.loadYmlConfigFromResource("config.yml", Configuration.class);This reads config.yml from the available resources into an object of the Configuration
class.
or
Configuration configuration =
ConfigLoader.loadYmlConfig(filePath, Configuration.class);Allows the .yml file to be on the file system somewhere.
For Properties loading:
Properties properties =
ConfigLoader.loadPropertiesFromResource("examples/interpolation.properties");Properties files can also be loaded from file system via Path:
Properties properties = ConfigLoader.loadProperties(Paths.get("src", "test", "resources",
"subdir", "import.properties"));Other examples are available in ExamplesTest.
Using the standard YML loader, a file like this:
name: 'Audrey'
age: 32
isLoggedIn: truecan be loaded into a java class:
// POJO class
// It should have a default constructor and getters/setters
// compatible with loading by SnakeYml
class Config {
private String name;
private int age;
// getters/setters
}The Yaml can express hardcoded values, but can also use placeholders to indicate fields which should be calculated at runtime before the yaml parser is used.
Example:
name: ${USER_NAME:-anonymous}
age: ${USER_AGE:-0}
isLoggedIn: ${USER_LOGGED_IN}Placeholders can be provided with or without defaults. Their values will be loaded from the first available value in environment variables or system properties.
Examples:
${some.value}- replaced by the contents ofsome.valueor blank if there isn't one${SOME_VALUE}- replaced by the contents ofSOME_VALUEor blank if there isn't one${some.value:-123}- allows a default of123to apply if thesome.valuecannot be found from the context${}- equates to$- allowing you to use things like look like placeholders as strings in the data- So to express the actual value
${this-is-not-a-placeholder}- you can put${}{this-is-not-a-placeholder}
- So to express the actual value
The lookup evaluates in the order of:
- Environment variable
- System property
- Default
- Blank
Any non-null in the first three will result in the placeholder being filled. This allows a blank value in an environment variable to supersede a non-blank in a system property.
Although the convention is
lowercase.system.propertyandUPPER_SNAKE_CASE_ENVIRONMENT_VARIABLEthis is not enforced in any way by the library
While a key benefit of this library is its ability to deserialize the configuration
into a POJO, for very simple cases, it may be desirable to load into a Map. This
may also cover cases where there's a variable number of properties that cannot be
predicted at development time. E.g. where the configuration is fundamentally
a dictionary.
// Configuration loaded as a map
Map<String, Object> configuration =
ConfigLoader.loadYmlConfigFromResource("config.yml");For reuse of segments of configuration, use the placeholder #import followed by a space
and then the filename:
#import commonConfig.ymlThis imports the file commonConfig.yml verbatim into wherever in the source file
the #import statement was made.
The #import statement also allows placeholders to be used, so subsets
of configuration can be loaded, driven by an environment variable. This might
for example, allow different runtime profiles to be in separate files.
Example:
#import commonConfig.yml
#import ${runtime.profile}-Config.yml
someVariable: true
someVariableByPlaceholder: ${PLACEHOLDER_VALUE}Here, the contents of the property runtime.profile might load
a file such as dev-Config.yml or prod-Config.yml.
Note: the parent file is loaded into the Java object, so it may also express some values to load into that object.
When #import is used within resources, the import path is the full
resource path to the imported file.
With file imports, the paths for #import are
relative to the current file. E.g. #import ../somefile.properties or
#import neighbour.properties.
An object of ConfigLoader allows customization to be added. Rather than using the static
methods on ConfigLoader to load the configuration, create a new ConfigLoader,
apply any customizations to it, and then use loadAs or load to load the
configuration into a custom object or a Map.
Config myConfig = new ConfigLoader()
.withResourceLoader(StringProvider::fromString)
.loadAs("concurrency: ${CONCURRENCY}", Config.class);The custom StringProvider allows the input resource to be treated as a YML literal.
Note: when building a custom provider, it's necessary to include placeholder parsing and import logic as part of the provider. This can be implemented using either
PlaceholderParserorImportAwarePlaceholderResolver.
Note: this is a good way to integrate with a password manager
Config myConfig = new ConfigLoader()
.withResourceLoader(StringProvider::fromString)
.withTag("password", myPasswordManager::load)
.loadAs("password: !password ${PASSWORD_ID}", Config.class);When parsing !password, the String value to the right of it - here defined by a
placeholder - will be passed to the load function of myPasswordManager.
For other examples see ExamplesTest.
If you have any issues or improvements, please at least submit an issue.
Please also feel free to fork the project and submit a PR for consideration.
- Please write a test for your change
- Ensure that the build succeeds and there's no drop in code coverage - see the GitHub PR for feedback
The basic coding style is described in the
EditorConfig file .editorconfig.
# to build
./mvnw clean installInternal use
export GPG_TTY=$(tty)
./mvnw clean -Dgpg.executable=gpg -Prelease-sign-artifacts -Dgpg.passphrase=secret release:prepare release:perform -f pom.xml