This guide documents cconfig, the Chenile modular configuration system, with emphasis on the current OWIZ-based runtime flow, the built-in configuration commands, and the file/resource lookup conventions.
What cconfig does
cconfig lets a Chenile application define configuration as module JSON on the classpath and then progressively enrich or override that configuration at runtime.
The main ideas are:
- each configuration belongs to a module such as
payments,payments.rules, orctest - base values usually come from a JSON file on the classpath
- later sources can replace a full key or mutate a nested path inside a JSON value
- overrides can be selected by a custom attribute, which defaults to the Chenile tenant id header
- the orchestration of those sources is explicit in OWIZ XML instead of being hardcoded into retriever ordering logic
Repository structure
The repository contains two modules:
chenile-configcconfig-db
chenile-config holds the SDK client implementation, the OWIZ command types used for config loading, and utility classes. cconfig-db provides the database-backed config command and the production OWIZ orchestration.
Current package layout
The current design is split into four main packages:
org.chenile.cconfig.spiorg.chenile.cconfig.sdk.implorg.chenile.cconfig.utilorg.chenile.cconfig.service.impl
The SPI package contains:
ConfigContextKeyManipulatingConfigRetriever
The SDK implementation package contains:
CconfigClientImplJsonBasedCconfigRetrieverPropertiesBasedCconfigRetrieverEnvBasedCconfigRetrieverMessageBundleConfigRetriever
The utility package contains:
ExpressionSupportResourceSupport
The service implementation package contains:
DbBasedCconfigRetriever
Runtime flow
CconfigClientImpl resolves a configuration value through ConfigContext and an OWIZ orchestration.
The flow is:
- Build a
ConfigContext(module, customAttribute). - Look in
MemoryCachefor the resolved module map. - Execute an
OrchExecutor<ConfigContext>. - Let each OWIZ command mutate or enrich
configContext.allKeys. - Cache the resulting
configContext.allKeys. - Return either the full module map or the selected key.
The default customization attribute comes from chenile-tenant-id, which is read from the Chenile request context.
OWIZ orchestration model
cconfig no longer uses a retriever factory or an order() contract. The composition is now defined in OWIZ XML.
The production orchestration in cconfig-db is:
<flows>
<flow>
<chain>
<json-based-cconfig-retriever/>
<db-based-cconfig-retriever/>
<properties-based-cconfig-retriever/>
<message-bundle-config-retriever/>
<env-based-cconfig-retriever/>
</chain>
</flow>
</flows>
This keeps orchestration concerns outside the individual config commands. If the sequence changes, the XML changes, not the command API.
OWIZ tag convention
OWIZ supports a useful Spring bean naming convention:
- a Spring bean named
jsonBasedCconfigRetrievercan be used in OWIZ XML as<json-based-cconfig-retriever/> - a Spring bean named
messageBundleConfigRetrievercan be used as<message-bundle-config-retriever/>
This means normal Spring bean names can be referenced directly in OWIZ XML without add-command-tag, as long as the XML tag is the hyphenated form of the camel-case bean name.
Built-in config commands
JsonBasedCconfigRetriever
- lives in
chenile-config - is the baseline config loader
- loads module JSON from a configured classpath folder
- uses
ResourceSupport.resourceLoader(...)so it first checks a customization-specific JSON path and then falls back to the base JSON path - maps module names to JSON resources
ctest->.../ctest.jsonctest1.ctest1->.../ctest1/ctest1.json
- replaces
configContext.allKeyswith the parsed module map
This command establishes the starting key set for the module.
PropertiesBasedCconfigRetriever
This command lives in chenile-config and extends KeyManipulatingConfigRetriever.
It:
- treats the configured
propertiesPathas a folder, not a file - derives the actual properties file name from the module
- module
m1->m1.properties
- module
- uses
ResourceSupport - first checks
<propertiesPath>/<customAttribute>/<module>.properties - then falls back to
<propertiesPath>/<module>.properties - reads entries of the form
module.key=value - also supports nested-path overrides in the form
module.key.path.to.node=value - converts matching entries for the current module into
Cconfigrecords - relies on
KeyManipulatingConfigRetrieverandExpressionSupportto apply those records
This is useful for file-based overrides that should behave like database Cconfig rows without requiring a database.
DbBasedCconfigRetriever
This is the service-side command and extends KeyManipulatingConfigRetriever.
It:
- queries runtime overrides from the database
- includes both
__GLOBAL__and the request-specific customization attribute - returns
List<Cconfig>fromretrieveCconfigs(...) - relies on
KeyManipulatingConfigRetrieverandExpressionSupportto apply those overrides toconfigContext.allKeys
This is the right pattern when a source naturally returns Cconfig rows rather than directly mutating the resolved map.
MessageBundleConfigRetriever
This command lives in chenile-config and augments keys from Spring message bundles.
It:
- scans configured message bundle property files
- looks for keys in the forms
module.key,__GLOBAL__.module.key, andcustomAttribute.module.key - builds locale-aware maps from those bundle entries
- treats
module.keyas the default message key - lets
__GLOBAL__.module.keyoverride that default - lets
customAttribute.module.keyoverride both - adds those values into
configContext.allKeyswithout replacing existing non-bundle values
This is intended for i18n-style configuration values where the resolved value is a locale map rather than a single scalar.
EnvBasedCconfigRetriever
This command lives in chenile-config and reads overrides from environment variables.
It:
- uses the key combinations already known in
ConfigContext - looks for environment entries named
{customAttribute}_{module}_{key} - also checks
__GLOBAL___{module}_{key} - emits pathless
Cconfigvalues so the normal merge logic applies - overrides known keys rather than inventing arbitrary new ones
This makes environment overrides a late-stage source in the default orchestration.
ResourceSupport
ResourceSupport is the shared classpath lookup utility used by commands that read resources from the classpath.
Its static method:
resourceLoader(basePath, resourceName, customAttribute)
behaves like this:
- try
<basePath>/<customAttribute>/<resourceName> - if not found, try
<basePath>/<resourceName> - if neither exists, return
null
This gives JSON and properties loaders a consistent customization-aware classpath lookup path.
Override semantics
cconfig still supports two kinds of mutation:
Full-value overrides
If a command applies or returns a record with no path, the full key value is replaced.
Path-based overrides
If a command returns a Cconfig with a path, the existing expression-based logic updates only that nested location.
Example:
- base JSON defines
key2.fields.field1.range - runtime override can add or replace
fields.field2 - runtime override can replace a scalar like
abc
In the current default orchestration:
JsonBasedCconfigRetrieverestablishes the starting key setDbBasedCconfigRetrievercan replace existing values, add new values, or mutate nested pathsPropertiesBasedCconfigRetrievercan add or override keys from classpath properties files and can apply nested path updatesMessageBundleConfigRetrievercan add locale-aware values for message-driven keysEnvBasedCconfigRetrieveroverrides known keys from the environment
How to add a new config source
To add another override source:
- Implement a normal OWIZ
Command<ConfigContext>. - If the source naturally produces
List<Cconfig>, extendKeyManipulatingConfigRetrieverand implementretrieveCconfigs(...). - If the source directly mutates the resolved map, update
configContext.allKeysinexecute(...). - Register the command as a Spring bean.
- Place it in the desired sequence in the OWIZ XML.
This keeps the orchestration explicit and decoupled from the individual config commands.
Testing approach
The current flow is covered by tests in both modules:
- OWIZ-based orchestration tests verify the multi-command merge flow
ResourceSupporttests verify customization-specific resource lookup with fallback- properties tests verify that module-derived
*.propertiesfiles map correctly intoCconfigrecords - message-bundle tests verify locale-aware config extraction
- env tests verify environment overrides over already-discovered keys
The OWIZ tests also verify that the XML bean-tag DSL works with hyphenated Spring bean names such as <json-based-cconfig-retriever/>.