1. Welcome to ConfNG
ConfNG is a configuration management library designed specifically for Java applications with first-class TestNG integration. It provides a flexible, type-safe way to manage configuration across multiple sources with automatic precedence handling.
Key features include:
- Zero-configuration TestNG integration - TestNG parameters automatically injected via service loader
- JUnit 5 Extension - Full JUnit 5 support with
@ConfigValueparameter injection - Type-safe configuration - Enum-based keys with compile-time checking
- Multiple configuration sources - Environment variables, system properties, files (Properties, JSON, YAML, TOML), secret managers, and custom sources
- Remote sources - HTTP endpoints, HashiCorp Consul, and Spring Cloud Config Server
- Encryption/Decryption - AES-256-GCM encryption for sensitive values with
@Encryptedannotation - Hot-reload - Configuration reloading without restart with file watching and change listeners
- Metrics & Logging - Track lookups, cache hits, and performance with structured logging
- Code generators - Generate config enums from properties/YAML/JSON files
- Framework integrations - Spring and Micronaut PropertySource support
- Smart precedence - Configurable source priority with sensible defaults
- Secret management - Built-in support for AWS, Azure, and HashiCorp Vault
- Thread-safe - Safe for concurrent access
- Lightweight - Minimal dependencies
Here is a very simple example:
package com.example;
import org.confng.ConfNG;
import org.confng.ConfNGKey;
import org.testng.annotations.*;
public enum AppConfig implements ConfNGKey {
BASE_URL("base.url", "http://localhost:8080"),
TIMEOUT("timeout", "30"),
BROWSER("browser", "chrome");
private final String key;
private final String defaultValue;
AppConfig(String key, String defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
@Override
public String getKey() { return key; }
@Override
public String getDefaultValue() { return defaultValue; }
}
public class SimpleTest {
@BeforeClass
public void setUp() {
// TestNG parameters automatically available!
// No configuration needed
}
@Test
public void testApplication() {
String url = ConfNG.get(AppConfig.BASE_URL);
Integer timeout = ConfNG.getInt(AppConfig.TIMEOUT);
String browser = ConfNG.get(AppConfig.BROWSER);
System.out.println("Testing " + url + " with " + browser);
}
}
The enum AppConfig defines your configuration keys with default values. TestNG parameters are automatically injected when tests run - no setup required!
1.1. Requirements
ConfNG requires Java 11 or higher.
1.2. Mailing-lists
For questions, support, or discussions, please contact us at team@confng.org.
1.3. Locations of the projects
- Main Repository: https://github.com/confng/confng
- Playground Examples: https://github.com/confng/confng-playground
- Documentation: https://confng.github.io
1.4. Bug reports
Bug reports and feature requests can be submitted on the GitHub Issues page.
1.5. License
ConfNG is released under the Apache License 2.0.
2. Download
ConfNG is available through Maven Central. Add it to your project using Maven or Gradle:
2.1. Maven
<dependency>
<groupId>org.confng</groupId>
<artifactId>confng</artifactId>
<version>1.1.1</version>
</dependency>
2.2. Gradle
dependencies {
implementation 'org.confng:confng:1.1.1'
}
2.3. Gradle Plugin (Recommended)
For seamless system property forwarding, add the ConfNG Gradle plugin:
plugins {
id 'org.confng' version '1.1.1'
}
This automatically forwards all -D flags to the test JVM:
./gradlew test -Dbrowser=firefox -Ddatabase.url=jdbc:mysql://localhost/test
No additional configuration needed - properties are automatically available via ConfNG.get().
Plugin Configuration (Optional)
confng {
// Disable automatic forwarding (default: true)
forwardSystemProperties = false
// Only forward properties matching these patterns
includePatterns = ['app.', 'database.', 'browser']
// Exclude properties matching these patterns
excludePatterns = ['secret.', 'password']
}
2.4. Build from source
To build ConfNG from source:
git clone https://github.com/confng/confng.git
cd confng
./gradlew build
2.6. What's New in 1.1.1
Version 1.1.1 introduces the ConfNG Gradle Plugin and powerful features for configuration management:
๐ Gradle Plugin
New org.confng Gradle plugin automatically forwards system properties to test JVM. No more manual systemProperty configuration needed!
๐ Encryption/Decryption
Built-in AES-256-GCM encryption for sensitive configuration values with @Encrypted annotation and pluggable encryption providers.
๐ Configuration Reloading
Hot-reload configuration without restart using ConfigReloadManager with file watching, change listeners, and debouncing.
๐ Metrics & Logging
Track configuration lookups, cache hits, and performance with ConfigMetrics. Structured logging with ConfigLogger and sensitive value masking.
๐ Remote Sources
Fetch configuration from HTTP endpoints, HashiCorp Consul, and Spring Cloud Config Server with retry logic and caching.
โ๏ธ Code Generators
Generate configuration enums from properties/YAML/JSON files and create environment-specific templates with ConfigKeyGenerator and TemplateGenerator.
๐งช JUnit 5 Extension
New @ExtendWith(ConfNGExtension.class) for JUnit 5 integration with @ConfigValue parameter injection.
๐ Spring Integration
Use ConfNG as a Spring PropertySource with ConfNGPropertySource for seamless Spring Boot integration.
๐ Micronaut Integration
Native Micronaut support with ConfNGPropertySourceLoader for automatic property source loading.
๐ Typed Configuration Getters
New getLong(), getDouble(), getList(), and getDuration() methods for type-safe configuration access.
โ Optional/Required Values
New getOptional(), getRequired(), and getOrDefault() methods for explicit null handling.
๐ Validation Framework
Annotation-based validation with @Required, @NotEmpty, @Pattern, and @Range.
3. ConfNG Documentation
3.1. Introduction
ConfNG simplifies configuration management in Java applications, especially for TestNG-based test automation. It eliminates boilerplate code and provides a unified API for accessing configuration from multiple sources.
3.2. Quick Start
Getting started with ConfNG is simple:
- Add the dependency to your project
- Create an enum implementing
ConfNGKey - Use
ConfNG.get()to access configuration values - Run your tests - TestNG parameters are automatically available!
3.3. Configuration Keys
Configuration keys in ConfNG are defined using enums that implement the ConfNGKey interface. This provides type-safety and compile-time checking.
public enum MyConfig implements ConfNGKey {
API_URL("api.url", "https://api.example.com"),
TIMEOUT("timeout", "30"),
RETRY_COUNT("retry.count", "3");
private final String key;
private final String defaultValue;
MyConfig(String key, String defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
@Override
public String getKey() { return key; }
@Override
public String getDefaultValue() { return defaultValue; }
}
Access configuration values using the ConfNG class:
// Get as String
String url = ConfNG.get(MyConfig.API_URL);
// Get as Integer
Integer timeout = ConfNG.getInt(MyConfig.TIMEOUT);
// Get as Boolean
Boolean enabled = ConfNG.getBoolean(MyConfig.FEATURE_ENABLED);
// Get as Long
Long count = ConfNG.getLong(MyConfig.MAX_RECORDS);
3.4. Configuration Sources
ConfNG supports multiple configuration sources with automatic precedence handling. Sources are checked in the following order (highest to lowest priority):
- Environment Variables - System environment variables
- System Properties - Java system properties (-D flags)
- TestNG Parameters - Parameters from TestNG XML (Method > Test > Suite levels)
- Properties Files - .properties files
- JSON Files - .json configuration files
- YAML Files - .yaml/.yml files (requires custom source)
- TOML Files - .toml files (requires custom source)
- Secret Managers - AWS Secrets Manager, Azure Key Vault, HashiCorp Vault
- Custom Sources - Implement your own configuration source
- Default Values - Values defined in the enum
Example of loading from a properties file:
# config.properties
api.url=https://production.example.com
timeout=60
retry.count=5
Example of loading from a JSON file:
{
"api": {
"url": "https://production.example.com",
"timeout": 60
},
"retry": {
"count": 5
}
}
3.5. TestNG Integration
ConfNG provides zero-configuration TestNG integration through a service-loaded listener. TestNG parameters are automatically captured and made available through the ConfNG API.
Example TestNG XML:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Test Suite" verbose="1">
<parameter name="base.url" value="https://test.example.com"/>
<parameter name="browser" value="firefox"/>
<test name="Smoke Tests">
<parameter name="timeout" value="45"/>
<classes>
<class name="com.example.SmokeTest"/>
</classes>
</test>
</suite>
Access these parameters in your test:
public class SmokeTest {
@Test
public void testApplication() {
// No @Parameters annotation needed!
String url = ConfNG.get(AppConfig.BASE_URL);
String browser = ConfNG.get(AppConfig.BROWSER);
Integer timeout = ConfNG.getInt(AppConfig.TIMEOUT);
// Use the configuration...
}
}
The TestNG listener is automatically registered via Java's ServiceLoader mechanism. No manual configuration is required!
3.6. Gradle Plugin
The ConfNG Gradle Plugin (org.confng) automatically forwards system properties from the Gradle command line to the test JVM. This eliminates the need for manual systemProperty configuration in your build files.
Installation
plugins {
id 'java'
id 'org.confng' version '1.1.1'
}
Usage
Once applied, any -D flags passed to Gradle are automatically available in your tests:
# Run tests with custom configuration
./gradlew test -Dbrowser=firefox -Dbase.url=https://staging.example.com
# All properties are available via ConfNG.get()
String browser = ConfNG.get(AppConfig.BROWSER); // "firefox"
String url = ConfNG.get(AppConfig.BASE_URL); // "https://staging.example.com"
Configuration Options
The plugin provides a confng extension for customization:
confng {
// Enable/disable automatic property forwarding (default: true)
forwardSystemProperties = true
// Only forward properties matching these prefixes
includePatterns = ['app.', 'database.', 'browser', 'env']
// Exclude properties matching these patterns (takes precedence over includes)
excludePatterns = ['secret.', 'password', 'credential']
}
Pattern Matching
The plugin uses prefix matching for filtering:
- Include patterns: If specified, only properties starting with these prefixes are forwarded
- Exclude patterns: Properties starting with these prefixes are never forwarded
- Precedence: Exclude patterns take precedence over include patterns
confng {
// Forward only app.* and db.* properties, but never secrets
includePatterns = ['app.', 'db.']
excludePatterns = ['app.secret.', 'db.password']
}
// With this config:
// -Dapp.name=MyApp โ forwarded
// -Dapp.secret.key=xxx โ NOT forwarded (excluded)
// -Ddb.host=localhost โ forwarded
// -Ddb.password=secret โ NOT forwarded (excluded)
// -Dother.prop=value โ NOT forwarded (not in includes)
Why Use the Plugin?
Without the plugin, you need to manually forward each property:
// Without plugin - manual and error-prone
test {
systemProperty 'browser', System.getProperty('browser')
systemProperty 'base.url', System.getProperty('base.url')
systemProperty 'timeout', System.getProperty('timeout')
// ... repeat for every property
}
With the plugin, all properties are forwarded automatically:
// With plugin - zero configuration needed!
plugins {
id 'org.confng' version '1.1.1'
}
// That's it! All -D properties are automatically available in tests
3.7. Typed Configuration Getters
ConfNG 1.1.1 introduces additional typed getter methods for common data types:
// Long values
Long maxFileSize = ConfNG.getLong(AppConfig.MAX_FILE_SIZE);
// Double values
Double taxRate = ConfNG.getDouble(AppConfig.TAX_RATE);
// List values (comma-separated)
List<String> allowedOrigins = ConfNG.getList(AppConfig.ALLOWED_ORIGINS);
// Config: "http://localhost,http://example.com" -> ["http://localhost", "http://example.com"]
// List with custom delimiter
List<String> features = ConfNG.getList(AppConfig.FEATURES, ";");
// Duration values (human-readable formats)
Duration timeout = ConfNG.getDuration(AppConfig.SESSION_TIMEOUT);
// Supports: "30s", "5m", "2h", "1d", "500ms", "PT30S" (ISO-8601)
3.8. Optional/Required Configuration
Explicit methods for handling optional and required configuration values:
// Optional - returns Optional<String>, never null
Optional<String> apiKey = ConfNG.getOptional(AppConfig.API_KEY);
apiKey.ifPresent(key -> client.setApiKey(key));
// Required - throws ConfigurationException if missing
String databaseUrl = ConfNG.getRequired(DatabaseConfig.URL);
// With explicit fallback default
String browser = ConfNG.getOrDefault(AppConfig.BROWSER, "firefox");
Integer timeout = ConfNG.getOrDefault(AppConfig.TIMEOUT, 60);
3.9. Configuration Validation Framework
Annotation-based validation for configuration values:
public enum AppConfig implements ConfNGKey {
@Required
DATABASE_URL("database.url"),
@NotEmpty
API_KEY("api.key", "default-key"),
@Range(min = 1, max = 65535)
SERVER_PORT("server.port", "8080"),
@Pattern(regex = "^(debug|info|warn|error)$")
LOG_LEVEL("log.level", "info");
// ... implementation
}
// Validate configuration
ValidationResult result = ConfNG.validate(AppConfig.values());
if (!result.isValid()) {
for (ValidationError error : result.getErrors()) {
System.err.println(error.getKey() + ": " + error.getMessage());
}
}
3.10. Configuration Source Diagnostics
See exactly where each configuration value comes from:
// Get source info for a single key
ConfigSourceInfo info = ConfNG.getSourceInfo(AppConfig.DATABASE_URL);
System.out.println("Key: " + info.getKey()); // "database.url"
System.out.println("Value: " + info.getValue()); // "jdbc:mysql://..." (masked if sensitive)
System.out.println("Source: " + info.getSourceName()); // "Environment", "SystemProperties", etc.
System.out.println("Found: " + info.isFound()); // true/false
System.out.println("Default: " + info.isFromDefault()); // true if using default value
// Get source info for multiple keys
Map<String, ConfigSourceInfo> infos = ConfNG.getAllSourceInfo(AppConfig.values());
3.11. Prefix-based Configuration Retrieval
Retrieve all configuration values with a common prefix:
// Get all values with prefix
Map<String, String> dbConfig = ConfNG.getByPrefix("db.");
// Returns: {"db.host": "localhost", "db.port": "5432", "db.name": "mydb"}
// Get just the key names
Set<String> apiKeys = ConfNG.getKeysWithPrefix("api.");
// Returns: {"api.url", "api.key", "api.version"}
// Use cases: feature flags, tenant config, environment overrides
Map<String, String> features = ConfNG.getByPrefix("feature.");
for (Map.Entry<String, String> entry : features.entrySet()) {
String featureName = entry.getKey().substring("feature.".length());
boolean enabled = Boolean.parseBoolean(entry.getValue());
featureManager.setEnabled(featureName, enabled);
}
3.12. Encryption/Decryption
ConfNG provides built-in support for encrypting sensitive configuration values using AES-256-GCM encryption:
// Set up encryption with a 32-byte key
byte[] key = "your-32-byte-encryption-key!!!!".getBytes();
AesEncryptionProvider provider = new AesEncryptionProvider(key);
EncryptionManager.getInstance().setDefaultProvider(provider);
// Encrypt a value
String encrypted = EncryptionManager.getInstance().encrypt("my-secret-password");
// Result: "ENC(base64-encoded-ciphertext)"
// Decrypt a value
String decrypted = provider.decrypt(encrypted);
// Result: "my-secret-password"
Use the @Encrypted annotation to mark sensitive configuration keys:
public enum SecureConfig implements ConfNGKey {
@Encrypted
DATABASE_PASSWORD("db.password", ""),
@Encrypted(provider = "custom-provider", failOnError = true)
API_SECRET("api.secret", "");
// ... implementation
}
3.13. Configuration Reloading
Hot-reload configuration without restarting your application:
// Get the reload manager
ConfigReloadManager manager = ConfigReloadManager.getInstance();
// Add a change listener
manager.addChangeListener(event -> {
System.out.println("Config changed: " + event.getChangedKeys());
if (event.hasChanged("database.pool.size")) {
reconfigureConnectionPool(event.getNewValue("database.pool.size"));
}
});
// Enable auto-reload with file watching
manager.enableAutoReload();
// Or trigger manual reload
manager.triggerReload("manual-refresh");
3.14. Metrics & Logging
Track configuration usage and performance with built-in metrics:
// Enable metrics
ConfigMetrics metrics = ConfigMetrics.getInstance();
metrics.setEnabled(true);
// Record lookups (automatically tracked when integrated)
metrics.recordLookup("app.name", true, false); // key, cacheHit, usedDefault
// Get metrics
long totalLookups = metrics.getTotalLookups();
long cacheHits = metrics.getCacheHits();
double hitRate = metrics.getCacheHitRate();
// Export as JSON
String json = metrics.toJson();
Use ConfigLogger for structured logging with sensitive value masking:
ConfigLogger logger = ConfigLogger.builder()
.logLevel(ConfigLogger.LogLevel.INFO)
.maskSensitiveValues(true)
.addSensitivePattern("password")
.addSensitivePattern("secret")
.build();
logger.logConfigAccess("db.password", "****"); // Masked automatically
3.15. Remote Configuration Sources
Fetch configuration from remote services:
HTTP Configuration Source
HttpConfigSource httpSource = HttpConfigSource.builder()
.url("https://config-server.example.com/api/config")
.headers(Map.of("Authorization", "Bearer token"))
.cacheTimeSeconds(300)
.maxRetries(3)
.build();
ConfNG.addSource(httpSource);
HashiCorp Consul
ConsulConfigSource consulSource = ConsulConfigSource.builder()
.host("consul.example.com")
.port(8500)
.prefix("myapp/config/")
.token("consul-acl-token")
.build();
ConfNG.addSource(consulSource);
Spring Cloud Config Server
SpringCloudConfigSource springSource = SpringCloudConfigSource.builder()
.baseUrl("http://config-server:8888")
.application("myapp")
.profile("production")
.label("main")
.build();
ConfNG.addSource(springSource);
3.16. Code Generators
Generate configuration enums from existing configuration files:
// Generate from properties file
String enumCode = ConfigKeyGenerator.generateFromProperties(
"src/main/resources/application.properties",
"com.example.config",
"AppConfig"
);
// Generate from YAML file
String yamlEnumCode = ConfigKeyGenerator.generateFromYaml(
"src/main/resources/application.yml",
"com.example.config",
"AppConfig"
);
// Generate from JSON schema
String jsonEnumCode = ConfigKeyGenerator.generateFromJsonSchema(
"src/main/resources/config-schema.json",
"com.example.config",
"AppConfig"
);
Generate environment-specific configuration templates:
// Generate properties template
String propsTemplate = TemplateGenerator.generatePropertiesTemplate(
AppConfig.values(),
"Production configuration template"
);
// Generate YAML template
String yamlTemplate = TemplateGenerator.generateYamlTemplate(
AppConfig.values(),
"Production configuration template"
);
// Generate JSON template
String jsonTemplate = TemplateGenerator.generateJsonTemplate(
AppConfig.values(),
"Production configuration template"
);
3.17. JUnit 5 Extension
Use ConfNG with JUnit 5 tests:
<!-- Add the JUnit 5 extension dependency -->
<dependency>
<groupId>org.confng</groupId>
<artifactId>confng-junit5-extension</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
import org.confng.junit5.ConfNGExtension;
import org.confng.junit5.ConfigValue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(ConfNGExtension.class)
public class MyJUnit5Test {
@ConfigValue(key = "app.name")
private String appName;
@ConfigValue(key = "app.timeout", defaultValue = "30")
private int timeout;
@Test
void testWithInjectedConfig() {
assertNotNull(appName);
assertTrue(timeout > 0);
}
}
3.18. Spring Integration
Use ConfNG as a Spring PropertySource:
import org.confng.integration.spring.ConfNGPropertySource;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
@Configuration
public class ConfNGSpringConfig {
public ConfNGSpringConfig(ConfigurableEnvironment environment) {
// Add ConfNG as a property source with high priority
environment.getPropertySources().addFirst(
new ConfNGPropertySource("confng")
);
}
}
// Now use @Value annotations as usual
@Component
public class MyService {
@Value("${app.name}")
private String appName;
@Value("${database.url}")
private String databaseUrl;
}
3.19. Micronaut Integration
ConfNG integrates seamlessly with Micronaut:
import org.confng.integration.micronaut.ConfNGPropertySourceLoader;
import io.micronaut.context.env.PropertySourceLoader;
// Register via ServiceLoader (META-INF/services/io.micronaut.context.env.PropertySourceLoader)
// or programmatically:
public class Application {
public static void main(String[] args) {
Micronaut.build(args)
.propertySources(new ConfNGPropertySourceLoader())
.start();
}
}
// Use @Value or @ConfigurationProperties as usual
@Singleton
public class MyService {
@Value("${app.name}")
private String appName;
}
4. Examples
The ConfNG Playground repository contains comprehensive examples demonstrating various features and use cases:
Basic Examples
- Environment Auto-Loading - Automatic environment detection and configuration loading
- Properties Configuration - Using .properties files for configuration
- JSON Configuration - Using JSON files for configuration
Advanced Examples
- Multi-Source Configuration - Combining multiple configuration sources with precedence
- YAML Configuration - Custom YAML source implementation
- Secret Managers - Integration with AWS, Azure, and HashiCorp Vault
- Database Configuration - Using a database as a configuration source
TestNG Integration Examples
- TestNG Integration - Comprehensive TestNG patterns and best practices
- Environment Variables - Using environment variables in tests
- System Properties - Using Java system properties
5. API Documentation
For detailed API documentation and additional resources:
- JavaDoc: Complete API reference and documentation at docs.confng.org
- Source Code: Browse the complete source code and JavaDoc comments in the GitHub repository
- README: View the comprehensive README documentation on GitHub
- Issues & Support: Report bugs or request features on GitHub Issues
6. GitHub Repository
ConfNG is open source and hosted on GitHub:
- Main Repository: https://github.com/confng/confng
- Playground Examples: https://github.com/confng/confng-playground
Contributions, bug reports, and feature requests are welcome!