Context-aware configurations are related to a content resource or a resource tree (e.g. a web site or tenant site). An application may need different configuration for different sites, regions, and tenants = different contexts.
Inheritance for nested contexts and from global fallback values is supported.
- OSGi configuration is targeted to system-wide configuration (singleton services, factory = global list)
- It's difficult to store context-aware configuration in OSGi config
Recommendation: Use Sling Context-Aware Configuration for config related to content paths. Use OSGi configuration for everything else.
Two levels:
- ConfigurationResourceResolver (low-level) — access configuration resources (e.g. workflow definitions). Methods:
getResource/getResourceCollectionfor named config resources by context,getContextPath/getAllContextPaths. - ConfigurationBuilder (high-level) — access configuration data (key/value pairs). Get via adapting context resource or via
ConfigurationResolverOSGi service.
In most cases you only use the high-level API.
// Singleton named configuration as ValueMap
ValueMap props = resource.adaptTo(ConfigurationBuilder.class)
.name("simple-config").asValueMap();
String param1 = props.get("stringParam", String.class);
int param2 = props.get("intParam", 5);
// Collection of configurations as ValueMaps
Collection<ValueMap> propsList = resource
.adaptTo(ConfigurationBuilder.class)
.name("simple-config").asValueMapCollection();
@Configuration
public @interface MatrixConfig {
String stringParam();
int intParam() default 5;
boolean boolParam();
}
// Access typed singleton configuration
MatrixConfig config = resource.adaptTo(ConfigurationBuilder.class)
.as(MatrixConfig.class);
String param1 = config.stringParam();
int param2 = config.intParam();
// Configuration name is derived from annotation class name
@Configuration(collection = true)
public @interface SentinelListConfig {
String stringParam();
int intParam() default 5;
boolean boolParam();
}
// Access typed configuration collection
Collection<SentinelListConfig> configs = resource
.adaptTo(ConfigurationBuilder.class)
.asCollection(SentinelListConfig.class);
for (SentinelListConfig item : configs) {
// ...
}
@Configuration
public @interface ArchitectConfig {
String sampleParam();
MatrixConfig subConfig();
SentinelListConfig[] subListConfig();
}
// Access nested configuration
ArchitectConfig config = resource.adaptTo(ConfigurationBuilder.class)
.as(ArchitectConfig.class);
String param1 = config.subConfig().stringParam();
int param2 = config.subConfig().intParam();
for (SentinelListConfig item : config.subListConfig()) {
// ...
}
<!-- Accessing singleton configuration -->
<dl>
<dt>stringParam:</dt>
<dd>${caconfig['org.matrix.MatrixConfig'].stringParam}</dd>
</dl>
<!-- Accessing configuration collection -->
<ul data-sly-list.item="${caconfig['org.matrix.SentinelListConfig']}">
<li>stringParam: ${item.stringParam}</li>
</ul>
<!-- Nested config: use slash as separator -->
${caconfig['org.matrix.ArchitectConfig']['nestedConfig/stringParam']}
Configuration classes are Java annotation classes (never used as annotations — the syntax is reused for configuration metadata):
- Configuration name (defaults to class name)
- Parameters with names and types
- Default values
- Nested configuration structures
All configuration classes must be registered via bundle header Sling-ContextAware-Configuration-Classes. A bnd plugin sets this automatically:
<_plugin>org.apache.sling.caconfig.bndplugin.ConfigurationClassScannerPlugin</_plugin>
Default implementation stores all configuration below /conf. Fallback paths: /conf/global, /apps/conf, /libs/conf.
Contexts are defined by setting sling:configRef property on root content resources, pointing to the associated /conf path.
/content/mysite @sling:configRef = "/conf/mysite" /conf/mysite/sling:configs/org.matrix.MatrixConfig @stringParam = "value1" @intParam = 123
Singleton: /conf/{site}/sling:configs/{ConfigName}/@properties
Collection: /conf/{site}/sling:configs/{ConfigName}/item1/@properties, .../item2/@properties
Nested: /conf/{site}/sling:configs/{ConfigName}/@sampleParam + /subConfig/@properties + /subListConfig/item1/@properties
Resource inheritance (collections): Not enabled by default. Enable by setting sling:configCollectionInherit=true — produces a merged list with unique collection item names from the lookup chain.
Property inheritance: Not enabled by default. Enable by setting sling:configPropertyInherit=true — properties not defined on current resource are inherited from the next matching resource in lookup order.
By setting these properties on multiple resources in the lookup order, deeper inheritance chains are formed.
A set of SPIs allows overlaying, enhancing, or replacing the default implementation:
- ContextPathStrategy — how context paths and config references are detected
- ConfigurationResourceResolvingStrategy — where config data is looked up, how inheritance works
- ConfigurationInheritanceStrategy — if/how resources are inherited across the chain
- ConfigurationPersistenceStrategy2 — persistence structure of config within resources
- ConfigurationMetadataProvider — provide metadata about configurations
- ConfigurationOverrideProvider — provide custom overrides
Multiple strategies supported simultaneously — iterated in service ranking order. First implementation that "feels responsible" wins.
Override context-aware config values globally or for specific content paths:
Syntax:
{configName}/{propertyName}={propertyJsonValue}
[{contextPath}]{configName}/{propertyName}={propertyJsonValue}
{configName}={propertyJsonObject}
[{contextPath}]{configName}={propertyJsonObject}Built-in providers:
- System properties (prefixed
sling.caconfig.override.) - OSGi factory configuration ("Apache Sling Context-Aware OSGi Configuration Override Provider")
import static org.apache.sling.testing.mock.caconfig.ContextPlugins.CACONFIG;
public class MatrixConfigTest {
@Rule
public SlingContext context = new SlingContextBuilder()
.plugin(CACONFIG)
.build();
@Before
public void setUp() {
// register configuration annotation class
MockContextAwareConfig.registerAnnotationClasses(
context, MatrixConfig.class);
// write config values required for tests
MockContextAwareConfig.writeConfiguration(context,
"/content/region1", MatrixConfig.class,
"stringParam", "value1",
"intParam", 123);
}
}