CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-typesafe--config

Configuration library for JVM languages using HOCON files

Pending
Overview
Eval results
Files

options.mddocs/

Options and Customization

Options and customization provide comprehensive control over parsing, resolution, and rendering behavior. The library offers extensive configuration through option classes and extension interfaces for custom includers, resolvers, and loading strategies.

Parse Options

ConfigParseOptions Class

Control configuration parsing behavior with comprehensive options.

public final class ConfigParseOptions {
    public static ConfigParseOptions defaults();
    public ConfigParseOptions setSyntax(ConfigSyntax syntax);
    public ConfigParseOptions setOriginDescription(String originDescription);
    public ConfigParseOptions setAllowMissing(boolean allowMissing);
    public ConfigParseOptions setClassLoader(ClassLoader loader);
    public ConfigParseOptions setIncluder(ConfigIncluder includer);
    public ConfigSyntax getSyntax();
    public String getOriginDescription();
    public boolean getAllowMissing();
    public ClassLoader getClassLoader();
    public ConfigIncluder getIncluder();
}

Usage Examples:

// Parse with specific syntax
ConfigParseOptions jsonOptions = ConfigParseOptions.defaults()
    .setSyntax(ConfigSyntax.JSON);
Config config = ConfigFactory.parseString(jsonString, jsonOptions);

// Allow missing files
ConfigParseOptions lenientOptions = ConfigParseOptions.defaults()
    .setAllowMissing(true);
Config config = ConfigFactory.parseFile(optionalFile, lenientOptions);

// Custom origin description for debugging
ConfigParseOptions describedOptions = ConfigParseOptions.defaults()
    .setOriginDescription("generated config from service discovery");
Config config = ConfigFactory.parseString(dynamicConfig, describedOptions);

// Custom classloader for resource loading
ConfigParseOptions loaderOptions = ConfigParseOptions.defaults()
    .setClassLoader(pluginClassLoader);
Config config = ConfigFactory.parseResources("plugin.conf", loaderOptions);

Syntax Options

Control file format interpretation with ConfigSyntax enum.

public enum ConfigSyntax {
    JSON,        // Strict JSON format
    CONF,        // HOCON format (Human-Optimized Config Object Notation)
    PROPERTIES   // Java Properties format
}

Usage Examples:

// Force JSON parsing (strict)
Config jsonConfig = ConfigFactory.parseString(jsonString,
    ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON));

// Force HOCON parsing (allows comments, includes, etc.)
Config hoconConfig = ConfigFactory.parseString(hoconString,
    ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF));

// Force Properties parsing
Config propsConfig = ConfigFactory.parseString(propsString,
    ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES));

// Auto-detect syntax (default behavior)
Config autoConfig = ConfigFactory.parseString(unknownFormat);

Resolve Options

ConfigResolveOptions Class

Control substitution resolution behavior.

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();
    public boolean getUseSystemEnvironment();
    public boolean getAllowUnresolved();
}

Usage Examples:

// Disable system environment variable resolution
ConfigResolveOptions noEnv = ConfigResolveOptions.defaults()
    .setUseSystemEnvironment(false);
Config resolved = config.resolve(noEnv);

// Allow partial resolution (don't fail on unresolved substitutions)
ConfigResolveOptions partial = ConfigResolveOptions.defaults()
    .setAllowUnresolved(true);
Config partiallyResolved = config.resolve(partial);

// Completely isolated resolution (no system integration)
ConfigResolveOptions isolated = ConfigResolveOptions.noSystem();
Config resolved = config.resolve(isolated);

// Add custom resolvers
ConfigResolver customResolver = new DatabaseConfigResolver();
ConfigResolveOptions withCustom = ConfigResolveOptions.defaults()
    .appendResolver(customResolver);
Config resolved = config.resolve(withCustom);

Render Options

ConfigRenderOptions Class

Control configuration rendering to strings.

public final class ConfigRenderOptions {
    public static ConfigRenderOptions defaults();
    public static ConfigRenderOptions concise();
    public ConfigRenderOptions setComments(boolean value);
    public ConfigRenderOptions setOriginComments(boolean value);
    public ConfigRenderOptions setFormatted(boolean value);
    public ConfigRenderOptions setJson(boolean value);
    public ConfigRenderOptions setShowEnvVariableValues(boolean value);
    public boolean getComments();
    public boolean getOriginComments();
    public boolean getFormatted();
    public boolean getJson();
    public boolean getShowEnvVariableValues();
}

Usage Examples:

Config config = ConfigFactory.load();

// Default rendering (HOCON with comments and formatting)
String defaultRender = config.root().render();

// Compact JSON (no comments, minimal whitespace)
String json = config.root().render(ConfigRenderOptions.concise().setJson(true));

// Pretty-printed HOCON without comments
String clean = config.root().render(
    ConfigRenderOptions.defaults()
        .setComments(false)
        .setFormatted(true)
);

// Include file/line origin information in comments
String withOrigins = config.root().render(
    ConfigRenderOptions.defaults()
        .setOriginComments(true)
);

// Hide environment variable values for security
String secure = config.root().render(
    ConfigRenderOptions.defaults()
        .setShowEnvVariableValues(false)
);

Custom Includers

ConfigIncluder Interface

Customize how include statements are processed in configuration files.

public interface ConfigIncluder {
    ConfigIncluder withFallback(ConfigIncluder fallback);
    ConfigObject include(ConfigIncludeContext context, String what);
}

Specialized Includer Interfaces:

public interface ConfigIncluderFile extends ConfigIncluder {
    ConfigObject includeFile(ConfigIncludeContext context, File what);
}

public interface ConfigIncluderURL extends ConfigIncluder {
    ConfigObject includeURL(ConfigIncludeContext context, URL what);
}

public interface ConfigIncluderClasspath extends ConfigIncluder {
    ConfigObject includeResources(ConfigIncludeContext context, String what);
}

Implementation Examples:

// Custom includer that loads from a database
public class DatabaseIncluder implements ConfigIncluder {
    private final ConfigService configService;
    
    public DatabaseIncluder(ConfigService service) {
        this.configService = service;
    }
    
    @Override
    public ConfigObject include(ConfigIncludeContext context, String what) {
        try {
            String configData = configService.getConfiguration(what);
            if (configData != null) {
                return ConfigFactory.parseString(configData,
                    context.parseOptions().setOriginDescription("database:" + what))
                    .root();
            }
        } catch (Exception e) {
            throw new ConfigException.IO(context.parseOptions().getOriginDescription(),
                "Failed to load from database: " + what, e);
        }
        return null; // Not found
    }
    
    @Override
    public ConfigIncluder withFallback(ConfigIncluder fallback) {
        return new FallbackConfigIncluder(this, fallback);
    }
}

// Usage
ConfigIncluder dbIncluder = new DatabaseIncluder(myConfigService);
ConfigParseOptions options = ConfigParseOptions.defaults()
    .setIncluder(dbIncluder);

Config config = ConfigFactory.parseString(configWithIncludes, options);

ConfigIncludeContext Interface

Context information available during include processing.

public interface ConfigIncludeContext {
    ConfigIncludeContext relativeTo(String filename);
    ConfigParseOptions parseOptions();
}

Usage in Custom Includers:

@Override
public ConfigObject include(ConfigIncludeContext context, String what) {
    // Get parse options from context
    ConfigParseOptions options = context.parseOptions();
    ClassLoader loader = options.getClassLoader();
    
    // Create relative context for nested includes
    ConfigIncludeContext relativeContext = context.relativeTo(what);
    
    // Use context information for resolution
    String resolvedPath = resolveRelativePath(what, context);
    
    return loadFromPath(resolvedPath, relativeContext);
}

Custom Resolvers

ConfigResolver Interface

Provide custom substitution value sources.

public interface ConfigResolver {
    ConfigValue lookup(String path);
    ConfigResolver withFallback(ConfigResolver fallback);
}

Implementation Examples:

// Resolver that fetches values from external service
public class ServiceConfigResolver implements ConfigResolver {
    private final ConfigurationService service;
    private final Map<String, ConfigValue> cache = new ConcurrentHashMap<>();
    
    public ServiceConfigResolver(ConfigurationService service) {
        this.service = service;
    }
    
    @Override
    public ConfigValue lookup(String path) {
        return cache.computeIfAbsent(path, this::fetchFromService);
    }
    
    private ConfigValue fetchFromService(String path) {
        try {
            String value = service.getValue(path);
            if (value != null) {
                return ConfigValueFactory.fromAnyRef(value, "service:" + path);
            }
        } catch (Exception e) {
            // Log but don't fail - allow fallback resolvers
            logger.warn("Failed to resolve {} from service", path, e);
        }
        return null;
    }
    
    @Override
    public ConfigResolver withFallback(ConfigResolver fallback) {
        return new ChainedConfigResolver(this, fallback);
    }
}

// Resolver for encrypted values
public class EncryptedConfigResolver implements ConfigResolver {
    private final Cipher cipher;
    
    @Override
    public ConfigValue lookup(String path) {
        // Only handle paths that start with "encrypted."
        if (!path.startsWith("encrypted.")) {
            return null;
        }
        
        try {
            String encryptedValue = getEncryptedValue(path);
            String decryptedValue = decrypt(encryptedValue);
            return ConfigValueFactory.fromAnyRef(decryptedValue, "decrypted:" + path);
        } catch (Exception e) {
            throw new ConfigException.BadValue(path, "Failed to decrypt value", e);
        }
    }
}

Custom Loading Strategy

ConfigLoadingStrategy Interface

Override the default application configuration loading process.

public interface ConfigLoadingStrategy {
    Config parseApplicationConfig(ConfigParseOptions parseOptions);
}

Implementation and Usage:

// Custom loading strategy that loads from multiple sources
public class MultiSourceLoadingStrategy implements ConfigLoadingStrategy {
    @Override
    public Config parseApplicationConfig(ConfigParseOptions parseOptions) {
        Config base = ConfigFactory.parseResources("base.conf", parseOptions);
        Config environment = loadEnvironmentConfig(parseOptions);
        Config secrets = loadSecrets(parseOptions);
        
        return secrets
            .withFallback(environment)
            .withFallback(base);
    }
    
    private Config loadEnvironmentConfig(ConfigParseOptions options) {
        String env = System.getProperty("app.environment", "development");
        return ConfigFactory.parseResources("environments/" + env + ".conf", options);
    }
    
    private Config loadSecrets(ConfigParseOptions options) {
        // Load from secure storage
        return secretsService.loadConfig(options);
    }
}

// Register strategy via system property
System.setProperty("config.strategy", 
    "com.mycompany.config.MultiSourceLoadingStrategy");

// Strategy will be used automatically by ConfigFactory.load()
Config config = ConfigFactory.load();

Extension Patterns

Chaining Pattern

Many extension interfaces support chaining with fallback behavior.

// Chain includers
ConfigIncluder primary = new DatabaseIncluder(dbService);
ConfigIncluder secondary = new S3Includer(s3Service);
ConfigIncluder fallback = ConfigIncluderFactory.newDefault();

ConfigIncluder chain = primary
    .withFallback(secondary)
    .withFallback(fallback);

// Chain resolvers
ConfigResolver encrypted = new EncryptedConfigResolver();
ConfigResolver service = new ServiceConfigResolver(configService);
ConfigResolver system = new SystemConfigResolver();

ConfigResolver resolverChain = encrypted
    .withFallback(service)
    .withFallback(system);

Composite Pattern

Combine multiple extension instances into a single component.

// Composite includer that delegates based on scheme
public class SchemeBasedIncluder implements ConfigIncluder {
    private final Map<String, ConfigIncluder> includers = new HashMap<>();
    
    public SchemeBasedIncluder() {
        includers.put("http", new HttpIncluder());
        includers.put("https", new HttpsIncluder());
        includers.put("file", new FileIncluder());
        includers.put("classpath", new ClasspathIncluder());
        includers.put("database", new DatabaseIncluder());
    }
    
    @Override
    public ConfigObject include(ConfigIncludeContext context, String what) {
        URI uri = URI.create(what);
        String scheme = uri.getScheme();
        
        ConfigIncluder includer = includers.get(scheme);
        if (includer != null) {
            return includer.include(context, what);
        }
        
        throw new ConfigException.BadPath(what, "Unsupported include scheme: " + scheme);
    }
}

Option Validation

Validation Patterns

Validate configuration after loading and resolution.

public void checkValid(Config reference, String... restrictToPaths);

Usage Examples:

// Load reference configuration (schema)
Config reference = ConfigFactory.parseResources("reference.conf");

// Load actual configuration
Config config = ConfigFactory.load().resolve();

// Validate against reference
try {
    config.checkValid(reference);
    // Configuration is valid
} catch (ConfigException.ValidationFailed e) {
    // Handle validation errors
    List<ConfigException.ValidationProblem> problems = e.problems();
    for (ConfigException.ValidationProblem problem : problems) {
        String path = problem.path();
        String description = problem.problem();
        System.err.println("Validation error at " + path + ": " + description);
    }
}

// Validate only specific paths
config.checkValid(reference, "database", "server", "logging");

Best Practices

  1. Use defaults as starting point: Always start with *.defaults() and modify from there
  2. Chain extensions properly: Use withFallback() to create robust fallback chains
  3. Handle errors gracefully: Extension code should handle errors without failing the entire configuration loading process
  4. Cache expensive operations: Cache results in custom resolvers and includers
  5. Provide meaningful origins: Always include descriptive origin information for debugging
  6. Validate configurations: Use checkValid() to catch configuration errors early
  7. Test extension code: Thoroughly test custom includers and resolvers with various input scenarios
  8. Document custom behavior: Clearly document any custom loading strategies or extensions for your team

Install with Tessl CLI

npx tessl i tessl/maven-com-typesafe--config

docs

access.md

exceptions.md

index.md

loading.md

options.md

resolution.md

values.md

tile.json