New Configuration Approach for AEM 6

Since OSGI R6 we can beautify old style configurations for AEM 6 and create more readable, maintainable and loosely coupled code. In this target Declarative Services 1.3 (DS 1.3)will assists to us. Let’s consider how this can be reached step by step in scope of AEM 6 and Java 8. I wouldn’t provide a long description with explanation all pros and cons of this approach, what Declarative Services is and other things. Anyway you can simply google it if it’s interesting for you.

Prerequisites

  1. First of all we had to check thatDS 1.3 is using in OSGI environment:Declarative Services 1.3
  2. Create Apache Maven Project, as a baseline you can check this post.
  3. Specify dependencies for annotations and bnd tools:
    <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>
  4. Update Maven plugins to support Java 8 and DS 1.3
    <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>
  5. Now we are ready to write code with new  approach.

Write some code

Common configuration approach

Now let’s consider class with common AEM configuration approach from here. Prior to OSGI R6 we had to specifying properties within our component class:

  1. before class body like here
  2. within class body  like here

Also we should to add configuration instantiation:

@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);
    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 is weird to me because code of the java class mixed with code for configuration. As a result we have a fat class with many code lines for configuration. But additionally we should add some behaviour to our component. Are you kidding?

New configuration approach

What can be opposite to “Common configuration approach”?  Of course “New configuration approach”. Let’s refactor previous class.

  1. Create an interface class, mark this interface with @ObjectClassDefinition annotation and move to it all configuration stuff. Do not hesitate to specify default values for fields because they are already has correct type of variable in case if there is a mistake in configurations.
  2. Add @Designate annotation to class which should contain configuration object.
  3. Now it’s time to inject configuration within @Activate and @Modified methods:
    private BaseCacheConfig cacheConfig;
    
    @Activate
    @Modified
    protected void init(ComponentContext componentCtx, BundleContext bundleCtx, BaseCacheConfig cacheConfig) {
        this.cacheConfig = cacheConfig;
    }

    Conclusions

    As you can see with new approach we decreased initial class size so our code becomes more readable.  Code within configuration interface is self-explanatory. We don’t need to cast configuration variables to required type and at the same time we can assign default values in case if configuration property was missed or incorrect. Also we are able to reuse configuration in any classes (DRY principle).

Source code example you can find on GitHub here.