Introduction
Since OSGi R6, developers can move away from traditional AEM configuration patterns and create cleaner, more maintainable code. Declarative Services 1.3 (DS 1.3) facilitates this modernization within AEM 6 and Java 8 environments.
Prerequisites
- Verify DS 1.3 is deployed in your OSGi environment.
- Create an Apache Maven project baseline.
- Specify the required dependencies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<version>1.12.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>biz.aQute</groupId>
<artifactId>bndlib</artifactId>
<version>1.43.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<version>1.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
- Update Maven plugins for Java 8 and DS 1.3 support:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<version>1.23.0</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
The Old Configuration Approach
Traditional AEM configurations embedded properties within component classes. Configuration instantiation required manual casting and type conversion:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Activate
@Modified
protected void init(ComponentContext componentCtx, BundleContext bundleCtx,
Map<String, ?> properties) {
enabled = PropertiesUtil.toBoolean(
componentCtx.getProperties().get(ENABLE_CACHE), true);
heap = PropertiesUtil.toLong(
componentCtx.getProperties().get(CACHE_HEAP), DEFAULT_CACHE_HEAP);
offHeap = PropertiesUtil.toLong(
componentCtx.getProperties().get(CACHE_OFFHEAP),
DEFAULT_CACHE_OFFHEAP);
initialCitiesProp = PropertiesUtil.toStringArray(
componentCtx.getProperties().get(INITIAL_CITIES));
areas = PropertiesUtil.toString(
componentCtx.getProperties().get(ENABLED_CACHE_AREAS), "1");
}
This approach mixes configuration code with business logic, creating bloated classes that are harder to read and maintain.
The New Configuration Approach
The modernized strategy separates concerns effectively:
- Create an interface marked with
@ObjectClassDefinitioncontaining all configuration properties with proper type safety and default values. - Annotate the implementation class with
@Designateto reference the configuration interface. - Inject configuration via
@Activateand@Modifiedmethods:
1
2
3
4
5
6
7
8
private BaseCacheConfig cacheConfig;
@Activate
@Modified
protected void init(ComponentContext componentCtx, BundleContext bundleCtx,
BaseCacheConfig cacheConfig) {
this.cacheConfig = cacheConfig;
}
The configuration interface defines properties with annotations like @AttributeDefinition, providing self-documenting code with type-safe defaults.
Benefits
- Reduced complexity: Configuration is separated from business logic
- Self-documenting: Configuration interfaces describe their own properties
- Type safety: Eliminates casting errors at runtime
- Default values: Prevents configuration misses and null pointer exceptions
- Reusability: Configuration interfaces can be shared across multiple components
- DRY principle: No repeated property parsing code across classes
Source code examples are available on GitHub.