Configuration library for JVM languages using HOCON files
—
Resolution and substitution provide dynamic configuration capabilities through variable substitution using ${path} syntax. The system supports environment variables, system properties, and custom resolvers with comprehensive fallback and override mechanisms.
Resolve all substitutions in a configuration to create a fully resolved Config.
public Config resolve();
public Config resolve(ConfigResolveOptions options);
public boolean isResolved();Usage Examples:
// Basic resolution (resolves all ${...} substitutions)
Config unresolved = ConfigFactory.parseString("""
app {
name = "MyApp"
version = "1.0.0"
database {
url = "jdbc:postgresql://"${database.host}":"${database.port}"/"${database.name}
host = ${?DB_HOST}
port = ${?DB_PORT}
name = ${?DB_NAME}
}
}
""");
Config resolved = unresolved.resolve();
// Check if configuration is fully resolved
if (resolved.isResolved()) {
String dbUrl = resolved.getString("app.database.url");
}Resolve substitutions using values from another configuration.
public Config resolveWith(Config source);
public Config resolveWith(Config source, ConfigResolveOptions options);Usage Examples:
// Configuration with substitutions
Config template = ConfigFactory.parseString("""
server {
host = ${server.host}
port = ${server.port}
ssl = ${server.ssl.enabled}
}
""");
// Source configuration with actual values
Config values = ConfigFactory.parseString("""
server {
host = "production.example.com"
port = 8443
ssl.enabled = true
}
""");
// Resolve template using values
Config resolved = template.resolveWith(values);Standard path-based substitutions reference other configuration values.
Syntax Examples:
# Reference another path
app.name = "MyApp"
app.display-name = ${app.name}
# Nested path references
database {
host = "localhost"
port = 5432
url = "jdbc:postgresql://"${database.host}":"${database.port}"/mydb"
}
# Self-references and concatenation
base-path = "/api/v1"
endpoints {
users = ${base-path}"/users"
orders = ${base-path}"/orders"
}Optional substitutions use ${?path} syntax and don't fail if the path is missing.
Syntax Examples:
# Optional environment variable substitution
database {
host = "localhost"
host = ${?DATABASE_HOST} # Override with env var if present
port = 5432
port = ${?DATABASE_PORT}
# Optional with fallback chain
url = ${?DATABASE_URL}
url = "jdbc:postgresql://"${database.host}":"${database.port}"/mydb"
}
# Optional system property
jvm {
max-heap = "512M"
max-heap = ${?JAVA_MAX_HEAP}
}Self-substitution allows a path to reference its own previous value.
Syntax Examples:
# Append to existing value
path = "/base"
path = ${path}"/extended" # Results in "/base/extended"
# Prepend environment-specific prefix
config-file = "app.conf"
config-file = ${?ENV}"."${config-file} # Could become "prod.app.conf"Control resolution behavior with comprehensive options.
public final class ConfigResolveOptions {
public static ConfigResolveOptions defaults();
public static ConfigResolveOptions noSystem();
public ConfigResolveOptions setUseSystemEnvironment(boolean value);
public ConfigResolveOptions setAllowUnresolved(boolean value);
public ConfigResolveOptions appendResolver(ConfigResolver resolver);
public List<ConfigResolver> getResolvers();
}Usage Examples:
// Default resolution (includes system environment and properties)
Config resolved = config.resolve();
// Resolution without system environment
Config resolved = config.resolve(ConfigResolveOptions.noSystem());
// Custom resolution options
ConfigResolveOptions options = ConfigResolveOptions.defaults()
.setUseSystemEnvironment(false) // Disable environment variables
.setAllowUnresolved(true); // Allow unresolved substitutions
Config partiallyResolved = config.resolve(options);
// Add custom resolver
ConfigResolver customResolver = new MyCustomResolver();
ConfigResolveOptions withCustom = ConfigResolveOptions.defaults()
.appendResolver(customResolver);
Config resolved = config.resolve(withCustom);Automatic integration with system properties and environment variables.
Environment Variables:
# Environment variables are available automatically
database.host = ${?DATABASE_HOST}
database.port = ${?DATABASE_PORT}
app.debug = ${?DEBUG_MODE}System Properties:
# System properties are available automatically
jvm.max-heap = ${?java.max.heap}
user.home = ${user.home}
temp.dir = ${java.io.tmpdir}Usage Examples:
// Set environment variables (in shell or process builder)
// export DATABASE_HOST=prod.db.example.com
// export DATABASE_PORT=5432
// Set system properties
System.setProperty("app.environment", "production");
System.setProperty("log.level", "INFO");
// Configuration automatically picks up system values
Config config = ConfigFactory.load();Create custom substitution resolvers for specialized value sources.
public interface ConfigResolver {
ConfigValue lookup(String path);
ConfigResolver withFallback(ConfigResolver fallback);
}Implementation Examples:
// Custom resolver for external service configuration
public class ServiceConfigResolver implements ConfigResolver {
private final ConfigService configService;
public ServiceConfigResolver(ConfigService service) {
this.configService = service;
}
@Override
public ConfigValue lookup(String path) {
try {
String value = configService.getValue(path);
if (value != null) {
return ConfigValueFactory.fromAnyRef(value, "service:" + path);
}
} catch (Exception e) {
// Log error but don't fail resolution
}
return null; // Path not found in this resolver
}
@Override
public ConfigResolver withFallback(ConfigResolver fallback) {
return new FallbackConfigResolver(this, fallback);
}
}
// Usage
ConfigResolver serviceResolver = new ServiceConfigResolver(myConfigService);
ConfigResolveOptions options = ConfigResolveOptions.defaults()
.appendResolver(serviceResolver);
Config resolved = config.resolve(options);Chain multiple resolvers with fallback behavior.
// Chain resolvers in priority order
ConfigResolver primary = new DatabaseConfigResolver();
ConfigResolver secondary = new FileConfigResolver();
ConfigResolver tertiary = new DefaultConfigResolver();
ConfigResolver chain = primary
.withFallback(secondary)
.withFallback(tertiary);
ConfigResolveOptions options = ConfigResolveOptions.defaults()
.appendResolver(chain);Handle various resolution error conditions.
public static class ConfigException.UnresolvedSubstitution extends ConfigException {
public String path();
public String detail();
}
public static class ConfigException.NotResolved extends ConfigException {
public String getMessage();
}Error Handling Examples:
try {
Config resolved = config.resolve();
} catch (ConfigException.UnresolvedSubstitution e) {
// Handle unresolved substitution
String problematicPath = e.path();
String details = e.detail();
System.err.println("Cannot resolve ${" + problematicPath + "}: " + details);
}
// Check resolution status before accessing values
if (!config.isResolved()) {
try {
config.resolve();
} catch (ConfigException.UnresolvedSubstitution e) {
// Handle or allow partial resolution
Config partial = config.resolve(
ConfigResolveOptions.defaults().setAllowUnresolved(true)
);
}
}Use substitutions for conditional configuration.
# Environment-specific configuration
environment = "development"
environment = ${?APP_ENVIRONMENT}
# Conditional database settings
database = {
development {
host = "localhost"
port = 5432
}
production {
host = "prod.db.example.com"
port = 5432
}
}
# Select configuration based on environment
current-db = ${database.${environment}}Create configuration templates with substitution placeholders.
# Template configuration
service-template = {
host = ${service.host}
port = ${service.port}
health-check = "http://"${service.host}":"${service.port}"/health"
metrics = "http://"${service.host}":"${service.port}"/metrics"
}
# Specific service configurations
services {
user-service = ${service-template} {
service.host = "user.service.internal"
service.port = 8080
}
order-service = ${service-template} {
service.host = "order.service.internal"
service.port = 8081
}
}Resolve substitutions across multiple configuration sources.
// Base configuration with substitutions
Config baseConfig = ConfigFactory.parseString("""
app {
name = ${app.metadata.name}
version = ${app.metadata.version}
database.url = ${database.connection.url}
}
""");
// Metadata configuration
Config metadata = ConfigFactory.parseString("""
app.metadata {
name = "MyApplication"
version = "2.1.0"
}
""");
// Database configuration
Config dbConfig = ConfigFactory.parseString("""
database.connection {
url = "jdbc:postgresql://localhost:5432/myapp"
}
""");
// Combine and resolve
Config combined = baseConfig
.withFallback(metadata)
.withFallback(dbConfig)
.resolve();${?path} for environment variables and optional settingsisResolved() before using configuration in productionConfigException.UnresolvedSubstitution appropriatelyInstall with Tessl CLI
npx tessl i tessl/maven-com-typesafe--config