ConfNG

Lightweight Configuration Management for Java

Simplify loading and resolving configuration values from multiple sources with precedence-based resolution, type safety, and TestNG integration.

// Define configuration keys
public enum TestConfig implements ConfNGKey {
    BROWSER("browser"),
    BASE_URL("base.url"),
    TIMEOUT("timeout");
    
    // Implementation...
}

// Load configuration sources
ConfNG.loadProperties("test.properties");
ConfNG.loadJson("config.json");

// Use in tests
@Test
public void testWithConfiguration() {
    String browser = ConfNG.get(TestConfig.BROWSER);
    Integer timeout = ConfNG.getInt(TestConfig.TIMEOUT);
    // Your test logic...
}

Why Choose ConfNG?

🔧

Multiple Sources

Environment variables, system properties, properties files, JSON files, and custom sources with configurable precedence.

🛡️

Type Safety

Enum-based configuration keys with compile-time checking and automatic type conversion for integers and booleans.

🚀

High Performance

Eager resolution of configuration values during initialization saves resources and improves runtime performance.

🔍

Auto-discovery

Automatic scanning for configuration keys using reflection with support for custom package filtering.

🔐

Secret Management

Built-in support for secret managers like AWS Secrets Manager and HashiCorp Vault with caching and error handling.

🧩

Extensible

Easy to add custom configuration sources and integrate with existing systems through clean interfaces.

Quick Start

1

Add Dependency

dependencies {
    implementation 'org.confng:confng:1.0.0'
}
<dependency>
    <groupId>org.confng</groupId>
    <artifactId>confng</artifactId>
    <version>1.0.0</version>
</dependency>
2

Define Configuration Keys

import org.confng.api.ConfNGKey;

public enum TestConfig implements ConfNGKey {
    BROWSER("browser", "chrome"),
    BASE_URL("base.url", "http://localhost:8080"),
    TIMEOUT("timeout", "30"),
    HEADLESS("headless", "false");
    
    private final String key;
    private final String defaultValue;
    
    TestConfig(String key, String defaultValue) {
        this.key = key;
        this.defaultValue = defaultValue;
    }
    
    @Override
    public String getKey() {
        return key;
    }
    
    @Override
    public String getDefaultValue() {
        return defaultValue;
    }
}
3

Load Configuration Sources

import org.confng.ConfNG;

public class TestBase {
    static {
        // Load configuration files (optional - skipped if not found)
        ConfNG.loadProperties("test.properties");
        ConfNG.loadJson("config.json");
        
        // Add custom sources if needed
        ConfNG.registerSource(new CustomConfigSource());
    }
}
4

Use in Your Tests

@Test
public void testWebApplication() {
    // Get configuration values with type safety
    String browser = ConfNG.get(TestConfig.BROWSER);
    String baseUrl = ConfNG.get(TestConfig.BASE_URL);
    Integer timeout = ConfNG.getInt(TestConfig.TIMEOUT);
    Boolean headless = ConfNG.getBoolean(TestConfig.HEADLESS);
    
    // Use configuration in your test
    WebDriver driver = createDriver(browser, headless);
    driver.manage().timeouts().implicitlyWait(timeout, TimeUnit.SECONDS);
    driver.get(baseUrl);
    
    // Your test logic here...
}

Documentation

📚 User Guide

Complete guide covering all features, configuration sources, and advanced usage patterns.

Read Guide

🔧 API Reference

Detailed API documentation with all classes, methods, and configuration options.

View API

💡 Examples

Working examples and code samples for common use cases and integrations.

See Examples

Examples

Basic Configuration Usage

// Configuration files
// test.properties
browser=chrome
base.url=https://staging.example.com
timeout=30

// config.json
{
  "browser": "firefox",
  "base.url": "https://prod.example.com",
  "timeout": 45
}

// Java code
public enum AppConfig implements ConfNGKey {
    BROWSER("browser", "chrome"),
    BASE_URL("base.url", "http://localhost"),
    TIMEOUT("timeout", "30");
    
    // Constructor and methods...
}

@BeforeClass
public void setup() {
    ConfNG.loadProperties("test.properties");
    ConfNG.loadJson("config.json");
}

@Test
public void testApplication() {
    String browser = ConfNG.get(AppConfig.BROWSER);
    String url = ConfNG.get(AppConfig.BASE_URL);
    Integer timeout = ConfNG.getInt(AppConfig.TIMEOUT);
    
    // Precedence: Env > System Props > Properties > JSON
    // If BROWSER env var is set, it overrides file values
}

Secret Manager Integration

// Custom AWS Secrets Manager integration
public class AWSSecretsSource extends SecretManagerSource {
    private final SecretsManagerClient client;
    
    public AWSSecretsSource() {
        super(300000); // 5 minute cache timeout
        this.client = SecretsManagerClient.builder()
            .region(Region.US_EAST_1)
            .build();
    }
    
    @Override
    public String getName() {
        return "AWSSecretsManager";
    }
    
    @Override
    protected String fetchSecret(String secretId) throws Exception {
        GetSecretValueRequest request = GetSecretValueRequest.builder()
            .secretId(secretId)
            .build();
        
        GetSecretValueResponse response = client.getSecretValue(request);
        return response.secretString();
    }
}

// Usage
AWSSecretsSource secretSource = new AWSSecretsSource();
secretSource.addKeyMapping("db.password", "prod/database/password");
secretSource.addKeyMapping("api.key", "prod/external-api/key");
ConfNG.loadSecretSource(secretSource);

// In tests
String dbPassword = ConfNG.get(DatabaseConfig.PASSWORD);
String apiKey = ConfNG.get(ApiConfig.KEY);

Custom Configuration Sources

// Custom database configuration source
public class DatabaseConfigSource implements ConfigSource {
    private final Map<String, String> configCache = new HashMap<>();
    private final DataSource dataSource;
    
    public DatabaseConfigSource(DataSource dataSource) {
        this.dataSource = dataSource;
        loadConfigurations();
    }
    
    @Override
    public String getName() {
        return "DatabaseConfig";
    }
    
    @Override
    public Optional<String> get(String key) {
        return Optional.ofNullable(configCache.get(key));
    }
    
    private void loadConfigurations() {
        try (Connection conn = dataSource.getConnection()) {
            PreparedStatement stmt = conn.prepareStatement(
                "SELECT config_key, config_value FROM app_config WHERE active = true"
            );
            ResultSet rs = stmt.executeQuery();
            
            while (rs.next()) {
                configCache.put(rs.getString("config_key"), rs.getString("config_value"));
            }
        } catch (SQLException e) {
            throw new RuntimeException("Failed to load database configuration", e);
        }
    }
}

// Register the custom source
ConfNG.registerSource(new DatabaseConfigSource(dataSource));

TestNG Integration

// Base test class with ConfNG setup
public abstract class BaseTest {
    
    @BeforeSuite(alwaysRun = true)
    public void setupConfiguration() {
        // Load configuration sources in order of precedence
        ConfNG.loadProperties("test.properties");
        ConfNG.loadJson("config.json");
        
        // Add environment-specific configurations
        String environment = System.getProperty("test.env", "local");
        ConfNG.loadProperties("config/" + environment + ".properties");
        
        // Force resolution of all configuration values
        ConfNG.resolveAllValues();
        
        // Log configuration for debugging
        System.out.println("Loaded " + ConfNG.getResolvedValueCount() + " configuration values");
    }
    
    @DataProvider(name = "browsers")
    public Object[][] browserProvider() {
        String browsers = ConfNG.get(TestConfig.BROWSERS);
        return Arrays.stream(browsers.split(","))
            .map(browser -> new Object[]{browser.trim()})
            .toArray(Object[][]::new);
    }
}

// Test class using configuration
public class WebTest extends BaseTest {
    
    @Test(dataProvider = "browsers")
    public void testOnMultipleBrowsers(String browser) {
        String baseUrl = ConfNG.get(TestConfig.BASE_URL);
        Integer timeout = ConfNG.getInt(TestConfig.TIMEOUT);
        
        WebDriver driver = WebDriverFactory.create(browser);
        driver.manage().timeouts().implicitlyWait(timeout, TimeUnit.SECONDS);
        
        // Test implementation...
    }
    
    @Test
    public void testWithSensitiveData() {
        String apiKey = ConfNG.get(ApiConfig.SECRET_KEY);
        
        // For logging, use masked version
        String maskedKey = ConfNG.getForDisplay(ApiConfig.SECRET_KEY);
        System.out.println("Using API key: " + maskedKey); // Prints: ***MASKED***
    }
}