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
Add Dependency
dependencies {
implementation 'org.confng:confng:1.0.0'
}
<dependency>
<groupId>org.confng</groupId>
<artifactId>confng</artifactId>
<version>1.0.0</version>
</dependency>
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;
}
}
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());
}
}
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 APIExamples
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***
}
}