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
  • 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
  • Smart precedence - Configurable source priority with sensible defaults
  • Extensible architecture - Easy to add custom configuration sources
  • 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

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.0</version>
</dependency>

2.2. Gradle

dependencies {
    implementation 'org.confng:confng:1.1.0'
}

2.3. Gradle Plugin (Recommended)

For seamless system property forwarding, add the ConfNG Gradle plugin:

plugins {
    id 'org.confng' version '1.1.0'
}

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.0

Version 1.1.0 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!

📊 Typed Configuration Getters

New getLong(), getDouble(), getList(), and getDuration() methods for type-safe configuration access with human-readable duration parsing.

✅ Optional/Required Values

New getOptional(), getRequired(), and getOrDefault() methods for explicit null handling and fail-fast configuration.

🔍 Validation Framework

Annotation-based validation with @Required, @NotEmpty, @Pattern, and @Range for configuration integrity checks.

🔬 Source Diagnostics

New getSourceInfo() method to see exactly where each configuration value comes from for easier debugging.

🏷️ Prefix-based Retrieval

New getByPrefix() and getKeysWithPrefix() methods for retrieving grouped configuration values.

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:

  1. Add the dependency to your project
  2. Create an enum implementing ConfNGKey
  3. Use ConfNG.get() to access configuration values
  4. 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):

  1. Environment Variables - System environment variables
  2. System Properties - Java system properties (-D flags)
  3. TestNG Parameters - Parameters from TestNG XML (Method > Test > Suite levels)
  4. Properties Files - .properties files
  5. JSON Files - .json configuration files
  6. YAML Files - .yaml/.yml files (requires custom source)
  7. TOML Files - .toml files (requires custom source)
  8. Secret Managers - AWS Secrets Manager, Azure Key Vault, HashiCorp Vault
  9. Custom Sources - Implement your own configuration source
  10. 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.0'
}

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.0'
}
// That's it! All -D properties are automatically available in tests

3.7. Typed Configuration Getters

ConfNG 1.1.0 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);
}

4. Examples

The ConfNG Playground repository contains comprehensive examples demonstrating various features and use cases:

Basic Examples

Advanced Examples

TestNG Integration Examples

5. API Documentation

For detailed API documentation and additional resources:

6. GitHub Repository

ConfNG is open source and hosted on GitHub:

Contributions, bug reports, and feature requests are welcome!