DS annotations – Configuration Factory


OSGi provides many modular features which enhances a developer’s implementation experience. Current blog focuses on one such feature: Configuration Factories via new OSGi Annotations

The concept is similar to logging configuration in AEM, where we define multiple configurations for different loggers, but the service implementation stays the same. Thus, multiple configuration, single implementation.

Create and access a configuration factory

It takes just two steps to create and use a Configuration factory.

Step 1: Create Configuration factory

This is achieved by adding ‘factory=true’ attribute to the Component annotation.

package blog.techrevel.service.impl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;

import blog.techrevel.service.ConfigurationFactory;

@Component(service = ConfigurationFactory.class)
@Designate(ocd = ConfigurationFactoryImpl.Config.class, factory=true)
public class ConfigurationFactoryImpl implements ConfigurationFactory
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFactoryImpl.class);

    @ObjectClassDefinition(name = "Techrevel configuration factory")
    public @interface Config {
        @AttributeDefinition(name = "Content Consumer URL", defaultValue = "http://localhost:8081")
        String getContentConsumerUrl();
    }

    private String contentConsumerUrl;

    @Activate
    @Modified
    protected void activate(final Config config) {
        contentConsumerUrl = config.getContentConsumerUrl();
        LOGGER.info("Read the content Consumer Url : " + contentConsumerUrl);
    }

    @Override public String getContentConsumerUrl() {
     return contentConsumerUrl;
    }
}

Step 2: Consume factory’s configurations

This is the service which would act based on values configured via factory.

Step 2.1: To aggregate configurations values in a collection use @Reference annotation with following attributes:

  • name=“The name of this reference.”
  • cardinality=”The cardinality of the service reference. This must be one of value from the enumeration ReferenceCardinality
    • AT_LEAST_ONE: The reference is mandatory and multiple.
    • MANDATORY: The reference is mandatory and unary.
    • MULTIPLE: The reference is optional and multiple.
    • OPTIONAL: The reference is optional and unary.
  • policy=”ReferencePolicy.DYNAMIC Or ReferencePolicy.STATIC”
    • If dynamic the service will be made available to the component as it comes and goes.
    • If static the component will be deactivated and re-activated if the service comes and/or goes away
  • unbind=”name of the method to be called when the service is to be unbound from the component”
    • To declare no unbind method, the value "-" must be used.
    • If not specified, the name of the unbind method is derived from the name of the annotated bind method. If the annotated method name begins with bindset or add, that is replaced withunbindunset or remove, respectively, to derive the unbind method name. Otherwise, un is prefixed to the annotated method name to derive the unbind method name. The unbind method is only set if the component type contains a method with the derived name.
package blog.techrevel.service.impl;
import java.util.ArrayList;
import java.util.List;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;

import blog.techrevel.service.ConfigurationFactory;
import blog.techrevel.service.ConfigurationFactoryConsumer;
@Component(immediate = true, service = ConfigurationFactoryConsumer.class)
public class ConfigurationFactoryConsumerImpl implements ConfigurationFactoryConsumer {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFactoryConsumerImpl.class);
    private List configurationList;
/**
    * Executed on Configuration Add event
    * @param config New configuration for factory
    */
    @Reference(name = "configurationFactory", cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
    protected synchronized void bindConfigurationFactory(final ConfigurationFactory config) {
        LOGGER.info("bindConfigurationFactory: " + config.getContentConsumerUrl());
        if (configurationList == null) {
            configurationList = new ArrayList(); }
            configurationList.add(config);
        }
    /**
    * Executed on Configuration Remove event
    * @param config New configuration for factory
    */
    protected synchronized void unbindConfigurationFactory(final ConfigurationFactory config) {
        LOGGER.info("unbindConfigurationFactory: " + config.getContentConsumerUrl());
        configurationList.remove(config);
    }
}

Step 2.2: Consume the configuration collection

The configurationList collection created in Step 2.1 can be accessed through various functions of ConfigurationFactoryConsumer class.

Access the collection and execute the business logic as needed. Happy Coding !

3 thoughts on “DS annotations – Configuration Factory

  1. Please either post the github link to refer to the files as most of the references listed above “ConfigurationFactoryConsumer” are not present and it is unclear how exactly the binding is happening

    Like

Leave a comment