AppConfig
A configuration library for Java apps
AppConfig is a configuration library which provides properties for
applications deployed to different environments. For example, your local
file storage is located at /home/joe/project1/files in a
development environment (laptop), but in production it is located on the
NFS: /opt/project1/files.
AppConfig makes it easy to configure multiple property
files, one per environment and load just one depending what environment
the application is running on.
As such, you may have a property file
development.properties with content:
file_storage=/home/joe/project1/files
while the production.properties file will include:
file_storage=/opt/project1/files
Reading properties
First, add a static import for the p() method:
then, simply call it as: p(..) in places where you need
to inject a property:
Property substitution
AppConfig allows a property substitution to make it possible to refactor large property files by specifying a repeating value once.
If your property file has these properties:
first.name=John
phrase= And the name is ${first.name}
than this code will print And the name is John:
System.out.println(p("phrase"));Note: The order of properties does not matter.
Setting up for multiple environments
AppConfig allows configuration of applications that is
specific for different deployment environments. Applications have
environment-specific files, whose names follow this pattern:
environment.properties, where environment is a name of a
deployment environment, such as development,
staging, production, etc.
You can also provide a global file, properties from which will be loaded in all environments: global.properties.
In all cases the files need to be on the classpath under directory/package
/app_config.
Environment-specific file will have an environment
part of the
file name match to an environment variable called
ACTIVE_ENV. Such configuration is easy to achieve in
Unix shell:
A typical file structure
/app_config
|
+--global.properties
|
+--development.properties
|
+--staging.properties
|
+--production.properties
If you are using Maven, then the location will be:
src/main/resources/app_config.
Global property file will always be loaded, while others will be
loaded depending on the value of ACTIVE_ENV environment
variable.
If environment variable
ACTIVE_ENVis missing,AppConfigdefaults to environmentdevelopment.
System property override
You can point to an external properties file using the
app_config.properties system property.
Here is an example (add this to the startup script for your app):
-Dapp_config.properties=/opt/project1/production.properties
The app_config.properties system property points to a
file specific to that computer (local box, server, etc.). Properties
loaded from this file will override properties loaded from the
classpath.
You can also override the ACTIVE_ENV environment
variable with a system property:
-Dactive_env=staging
The
active_envsystem property takes precedence over theACTIVE_ENVenvironment variable.
Environment Variables
Environment variables have the highest precedence and will override properties from all other configuration sources (classpath files, external files, and custom providers).
Because environment variable names cannot contain dots,
AppConfig translates between the two naming
conventions:
- Dots (
.) and dashes (-) are replaced with underscores (_) - Letters are uppercased
For example, the property database.password maps to the
environment variable DATABASE_PASSWORD. Both uppercase
(DATABASE_PASSWORD) and lowercase
(database_password) variants are accepted.
export DATABASE_PASSWORD=secret
String pwd = p("database.password"); // returns "secret"If a property is not defined in any property file, p()
and getKeys() will still find it via the corresponding
environment variable. For example, if JAVA_HOME is set in
the environment, p("java.home") returns its value even
without a java.home entry in any properties file.
Similarly, getKeys("java") will include
java.home in its results.
Note: env-var-only properties (not declared in any property file) are accessible via
p()andgetKeys(), but are not visible through the fullMapinterface methods such ascontainsKey(),keySet(), orsize().
Limitation ? camelCase property names: The
translation from env var to property name lowercases all letters, so
camelCase information is lost. For example,
MYAPP_MAXTHREADS translates to
myapp.maxthreads, not myapp.maxThreads. In
practice this rarely matters, because the properties most commonly
supplied via environment variables are secrets (password,
url, username) which are all lowercase.
Properties with camelCase names are typically non-sensitive tuning
parameters that are fine to declare in a property file.
For cases where env-var-only configuration of camelCase properties is required, implement a custom AppConfigProvider and return the properties with the correct names from any source.
Watch out for log lines like:
Duplicate property defined..in case of a duplicate value override. It will tell you exactly where the new value is coming from and which property source is overridden with it.
Custom Configuration Provider
Note: This feature is available in version 3.4-SNAPSHOT and later in 3.4.
AppConfig supports a pluggable provider architecture
that allows you to load properties from any custom source such as AWS
Secrets Manager, Azure Key Vault, HashiCorp Vault, databases, or any
other external configuration system.
Implementing a Provider
To create a custom provider, implement the
AppConfigProvider interface:
package com.myapp.config;
import org.javalite.app_config.AppConfigProvider;
import java.util.HashMap;
import java.util.Map;
public class AwsSecretsProvider implements AppConfigProvider {
@Override
public Map<String, String> getProperties() {
Map<String, String> properties = new HashMap<>();
// Load properties from AWS Secrets Manager
// ... your implementation here ...
properties.put("database.password", "secret_from_aws");
properties.put("api.key", "key_from_aws");
return properties;
}
}Using a Provider
Specify your provider class using the
app_config.provider system property when starting your
application:
java -cp $CLASSPATH -Dapp_config.provider=com.myapp.config.AwsSecretsProvider com.myproject.Main
Provider Loading Priority
All four sources always run in order ? using a provider does not skip classpath files, external files, or environment variables. Each step can override values from the previous ones:
- Classpath files are loaded first (
global.properties, then the environment-specific file) ? lowest priority - The external file (if configured) overrides classpath values
- The provider overrides anything loaded so far from files
- Environment variables have the final say and override everything, including provider values ? highest priority
This ensures that: - Provider properties can override file-based configuration - Environment variables still maintain the highest precedence for sensitive data - You can use providers for centralized configuration while allowing local overrides
Example Use Cases
AWS Secrets Manager Integration:
public class AwsSecretsProvider implements AppConfigProvider {
public Map<String, String> getProperties() {
// Use AWS SDK to fetch secrets
return awsSecretsClient.getSecrets("my-app/config");
}
}Database Configuration:
public class DatabaseConfigProvider implements AppConfigProvider {
public Map<String, String> getProperties() {
// Load from a configuration database table
return configRepository.getAllSettings();
}
}HashiCorp Vault:
public class VaultConfigProvider implements AppConfigProvider {
public Map<String, String> getProperties() {
// Fetch from Vault
return vaultClient.read("secret/myapp").getData();
}
}Configuration Sources Summary
AppConfig supports multiple configuration sources that
are loaded in a specific order. Later sources override earlier ones:
| Priority | Source | How to Configure | Use Case |
|---|---|---|---|
| 1 (Lowest) | Classpath Files | /app_config/global.properties and
/app_config/{env}.properties |
Default configuration committed to source control |
| 2 | External File | -Dapp_config.properties=/path/to/file.properties |
Server-specific configuration, deployment overrides |
| 3 | Custom Provider | -Dapp_config.provider=com.example.MyProvider |
Cloud secrets (AWS, Azure, Vault), centralized config |
| 4 (Highest) | Environment Variables | Standard OS environment variables | Sensitive data, container orchestration, CI/CD |
Key Points
- Properties from higher priority sources override those from lower priority sources
- The
ACTIVE_ENVenvironment variable (oractive_envsystem property) determines which classpath file is loaded - Environment variable names are translated to property names:
DATABASE_PASSWORD?database.password(underscores ? dots, lowercase). Both uppercase and lowercase env var names are accepted. p()andgetKeys()fall back to env vars for properties not declared in any property file; the fullMapinterface (containsKey(),keySet(), etc.) only reflects properties loaded from files, external files, and providers${placeholder}substitution works for properties loaded from files, external files, and providers; pure env-var-only properties do not participate as substitution sources
How to comment
The comment section below is to discuss documentation on this page.
If you have an issue, or discover bug, please follow instructions on the Support page