Adobe Experience Manager Blog

AEM concepts, snippets and implementation

Category Archives: AEM OSGi

DS Annotations – dependency updates

0

With AEM 6.2, we can use the new Declarative Service annotations. These are improvements over Felix annotations. Adding the recommendation excerpt from Apache Felix website:

While the Apache Felix Maven SCR Plugin is a great tool (see below), for developing OSGi components using Declarative Services you should use the official annotations from the OSGi R6 specification. The development of the Apache Felix SCR Plugin is in maintenance mode.

The examples and dependencies have been verified on AEM 6.3 Instance

Maven dependency changes:

In order to use the new OSGi annotations, we need to add following dependencies to pom.xml.

 <dependency>
     <groupId>org.osgi</groupId>
     <artifactId>osgi.core</artifactId>
     <version>6.0.0</version>
     <scope>provided</scope>
 </dependency>
 <dependency>
     <groupId>org.osgi</groupId>
     <artifactId>osgi.cmpn</artifactId>
     <version>6.0.0</version>
     <scope>provided</scope>
 </dependency>
 <dependency>
     <groupId>org.osgi</groupId>
     <artifactId>osgi.annotation</artifactId>
     <version>6.0.1</version>
     <scope>provided</scope>
 </dependency>
 

Use latest version of mvn-bundle-plugin (>=3.2.0)

For a project upgrading from Felix to DS annotations, you can remove following:

  • All Felix dependencies. Example:
    • org.apache.felix.scr.annotations
    • biz.aQute.bnd
  • Plugins
    • maven-scr-plugin: is required to resolve felix annotations at build time

Code changes:

You can choose to update java files at once, or one-by-one.

  • Incase you wish to modify all files together, then remove felix dependencies and plugins. The IDE would now recognize all the files that need modifcation.
  • However, if you wish to change files one-by-one, then you can keep both DS and Felix dependencies in pom.xml. A bundle with both types of annotations would still be good. Once all the code changes are done, you should remove all felix dependencies.

How to identify the changes:

You can easily identify the files, by looking for package imports of “org.apache.felix.scr.annotations.*”.

We would be using the following packages instead:

  • org.osgi.service.component.annotations.*
  • org.osgi.service.metatype.annotations.*

For more details on code changes involved, please visit the specific links:

Verify annotation resolution

To verify if the DS annotation is generated:

  • decompile the jar created after mvn clean install
  • Check for the Service description available below /OSGI-INF/ folder

service-description.PNG

 

Advertisements

DS Annotations – Component, property and configurations

1

@Component Annotation

An component is a piece of code that is managed by OSGi container. The container would be responsible for its instantiation and management.

A component is activated only after all its service dependencies are satisfied by the container.

Attributes of a component: 

component-attributes.PNG

The above table have been noted from: https://www.knopflerfish.org/releases/5.2.0/docs/javadoc/org/osgi/service/component/annotations/Component.html

  • configurationPolicy: The attribute can hold following values of ConfigurationPolicy
    • IGNORE: Always allow the component configuration to be satisfied and do not use the corresponding Configuration object even if it is present.
    • OPTIONAL: Use the corresponding Configuration object if present but allow the component to be satisfied even if the corresponding Configuration object is not present.
    • REQUIRE: There must be a corresponding Configuration object for the component configuration to become satisfied.
  • factory: used to create a configuration factory. More implementation details are available on link.
  • name: The attribute doesn’t support special characters. If invalid, the component will not be registered.
  • property: A replacement for “@Properties felix annotation used at Class-level”.
  • service: registers component as a Service. More implementation details available on link.

Example:

To create a component, add @Component annotation to a class. Also, configure its attributes as per your need.

In the following example, we have:

  • @Activate annotation is used to mark a function which would be called when the component activates. The function can have any name.
  • @Deactivate annotation is used to mark a function which would be called when the component deactivates. The function can have any name.
  • Declared a custom property using property attribute.
    • The property value is read from componentContext in @Activate/@Deactivate methods.

More on defining property:

One may also need to declare multiple properties of a component. In such scenarios, declare an array of values for property attribute:

To define multiple values of a property, create each value as a separate element of the property Array.

 

 

Creating configurable properties

The annotations described in this section will help you to create Components whose configurations can be edited via OSGi console. To achieve the same:

  1. Create a separate or an inner interface which would hold configurations. In example, we have created Config interface.
  2. Add @ObjectClassDefinition annotation to the interface. Also, add desired attributes
    • name: The name would help you search the configuration in OSGi’s configuration manager.
  3. Add @Designate to the Component that would consume the configurations.  The ocd attribute should refer to the Configuration interface created in Step-2.
  4. Declare properties that you would like to configure via @AttributeDefinition
    • Following image maps annotation attributes with the OSGi UI.osgi.PNG
    • Please note that there are 2 ways to define default values:
      • defaultValue attribute of @AttributeDefinition: The value is displayed to the user, when he/she tries to configure the interface via Configuration manager. OSGi will NOT pick this default value, if no Configuration exists. Thus, when you install a bundle, the output would appear as:default-values.PNG
      • Specifying default value in variable declarartion: The value is displayed to the user, when he/she tries to configure the interface via Configuration manager. OSGi will pick this default value, even if no Configuration exists.

Also, note that we no longer need PropertiesUtil to resolve OSGi configurations. 🙂

Notes:

Via Declarative Services, the number of annotations have been reduced. For example: @Component annotation is used for:

  • Component
  • Service
  • Servlet
  • Filter etc..

All of the above can be created by utilizing attributes of @Component details. More details are available on specific links

 

 

DS Annotation – Multiple implementation of Service

0

Multiple Service Implementation can be used to define multiple implementations of a Service, and then execute all/specific implementation depending on the use-case.

A close example would be Replication Agents. Here a specific replication implementation (Static Agent, Default Agent, Custom Transport Handler/Builders etc) is called, depending on the configuration.

Lets construct a multiple service implementation via an Example.

Scenario:

Consider an online course website. A lecturer can publish his content by submitting a course bundle comprising of html pages, videos and assessment questions.

While the bundle is processed, each content type is to be dealt with specific import-service implementation:

  1. Page import
  2. Video import
  3. Assessment import

Lets get started with the implementation !!!

Create multiple service implementation

The implementation can be divided into 3 simple steps:

  1. Create an interface which each of Service implementation would implement.
  2. Implement concrete service implementations
  3. A handler which would hold list of service implementation & execute relevent implementation.

Step 1: Interface which each service implementation would implement

The interface would comprise of abstract methods which:

  • would execute business logic for each service.
  • allow identification of best service to process current use-case.

Sample interface for the course import scenario:

package blog.techrevel.service;
public interface CourseImport {
    //Import pages, videos and assessment
    public abstract void importContent();

    //Identify the service which would be able to process current file
    public abstract boolean canProcess(String fileName);
}

Step 2: Implement concrete service implementations

Here we would be create multiple content import implementation to deal specifically with each content type. Example:

PageImport.java

package blog.techrevel.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import blog.techrevel.service.CourseImport;
@Component(name="pages", service = CourseImport.class, property={"type=page"})
public class PageImport implements CourseImport {
    private static final Logger LOGGER = LoggerFactory.getLogger(PageImport.class);
    @Override
    public void importContent() {
        LOGGER.info("Business Logic to import HTML Pages");
    }

    @Override
    public boolean canProcess(String fileName) {
        return StringUtils.endsWith(fileName, ".html");
    }

    @Activate
    @Modified
    protected void activate(ComponentContext componentContext){
         LOGGER.info("Registering: " + componentContext.getProperties().get("type").toString());
    }
}

 AssessmentImport.java

package blog.techrevel.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import blog.techrevel.service.CourseImport;
@Component(name = "assessment", service = CourseImport.class, property = { "type=assessment" })
public class AssessmentImport implements CourseImport {
    private static final Logger LOGGER = LoggerFactory.getLogger(AssessmentImport.class);
    @Activate
    @Modified
    protected void activate(ComponentContext componentContext)
    {
        LOGGER.info("Registering: " + componentContext.getProperties().get("type").toString());
    }
@Override
    public void importContent() {
         LOGGER.info("Business Logic to import assessment questions");
    }
@Override
    public boolean canProcess(String fileName) {
       return StringUtils.equals(fileName, "assessment.xls");
    }
}

VideoImport.java

package blog.techrevel.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import blog.techrevel.service.CourseImport;
@Component(name = "videos", service = CourseImport.class, property = { "type=video" })
public class VideoImport implements CourseImport {
    private static final Logger LOGGER = LoggerFactory.getLogger(VideoImport.class);
    @Override
    public void importContent() {
         LOGGER.info("Business Logic to import course videos");
    }
@Override
    public boolean canProcess(String fileName) {
         return StringUtils.endsWith(fileName, ".mp4");
    }
@Activate
    @Modified
    protected void activate(ComponentContext componentContext) {
         LOGGER.info("Registering: " + componentContext.getProperties().get("type").toString());
    }
}

Step 3: Handler to reference service and trigger execution of appropriate implementation.

Step 3.1: To aggregate references for all implementations 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.
private List courseImportImplList;
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
protected void bind(CourseImport courseImport) {
    if (courseImportImplList == null) {
        courseImportImplList = new ArrayList();
    }
    courseImportImplList.add(courseImport);
}
protected void unbind(CourseImport courseImport) {
    courseImportImplList.remove(courseImport);
}

Step 3.2 Trigger execution of appropriate implementation

Use the ‘collection created in Step-3.1’ to shortlist the best implementation for current use-case. The shortlisted implementation reference can then be used to trigger corresponding business logic.

In the below sample code, canProcess() identifies the right implementation to deal with current content type. Corresponding importContent() is then executed to import html as per business requirement.

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.CourseImport;
import blog.techrevel.service.CourseImportHandler;
@Component(name = "courseImportHandler", immediate = true, service=CourseImportHandler.class)
public class CourseImportHandlerImpl implements CourseImportHandler {
     private static final Logger LOGGER = LoggerFactory.getLogger(CourseImportHandlerImpl.class);
     private List courseImportImplList;
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
     protected void bind(CourseImport courseImport) {
          if (courseImportImplList == null) {
              courseImportImplList = new ArrayList();
          }
          courseImportImplList.add(courseImport);
     }
protected void unbind(CourseImport courseImport) {
         courseImportImplList.remove(courseImport);
     }
@Override
     public void importContent() {
        for (CourseImport courseImportImpl : courseImportImplList) {
            if (courseImportImpl.canProcess("introduction.mp4")) {
                courseImportImpl.importContent();
                break;
            }
        }
    }
}

DS annotations – Configuration Factory

2

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 !

AEM – Fetch product info via API

0

Here is a code snippet that can be used to fetch AEM’s product information via APIs.

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.granite.license.ProductInfo;
import com.adobe.granite.license.ProductInfoProvider;

@Component(immediate = true)
@Service(ProductInfoImpl.class)
public class ProductInfoImpl {
   private static final Logger LOG = LoggerFactory.getLogger(ProductInfoImpl.class);

   @Reference
   private ProductInfoProvider piProvider;

   @Activate
   @Modified
   protected void activate(ComponentContext cc) {
       if (piProvider != null) {
           ProductInfo pi = piProvider.getProductInfo();

           //Logging product information
           LOG.info("Product name: " + pi.getName());
           LOG.info("Product short name: " + pi.getShortName());
           LOG.info("Product version: " + pi.getVersion());
           LOG.info("Product short version: " + pi.getShortVersion());
       }
   }
}

 

Information printed in logs:

Product name: Adobe Experience Manager
Product short name: AEM
Product version: 6.3.0
Product short version: 6.3

 

 

 

 

OSGi – Accessing Bundle/Service information from BundleContext

0

Here are few code snippets compiled in a java program, which would help you fetch bundle and service information from BundleContext. The snippets cover:

  • Fetching bundle information
  • Fetching ServiceReference from bundle
  • Fetching Service from bundleContext
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import blog.techrevel.service.BundleInfo;

@Component(immediate = true)
@Service(BundleInfo.class)
public class BundleInfoImpl implements BundleInfo {
   private static final Logger LOG = LoggerFactory.getLogger(BundleInfoImpl.class);
   private static BundleContext bundleContext;

   @Override
   public void getBundleInfo(BundleContext bundleContext) {
       //Fetching bundles
       Bundle[] bundles = bundleContext.getBundles();

       for (Bundle bundle : bundles) {
           //Bundle details
           LOG.info("State: " + bundle.getState());
           LOG.info("Symbolic name: " + bundle.getSymbolicName());
           LOG.info("Version:" + bundle.getHeaders().get(Constants.BUNDLE_VERSION).toString());
           LOG.info("Imported Packages:" + bundle.getHeaders().get(Constants.IMPORT_PACKAGE));

           extractServicesInfo(bundle);
       }
   }

   // Fetching ServiceReferernces exposed by the bundle
   public static void extractServicesInfo(Bundle bundle) {
       ServiceReference[] registeredServices = bundle.getRegisteredServices();
       if (registeredServices != null) {
           for (ServiceReference registeredService : bundle.getRegisteredServices()) {
               // Fetching any property of the Service
               LOG.info("service.pid: " + registeredService.getProperty("service.pid"));

               // Fetch Service from ServiceReference
               LOG.info("Service: " + bundleContext.getService(registeredService));
           }
       }
   }

   @Activate
   @Modified
   protected void activate(ComponentContext cc) {
       bundleContext = cc.getBundleContext();
       getBundleInfo(bundleContext);
   }
}

More API details can be retrived from:

 

 

Apache Felix – Filtering service references

1

Via an earlier blog Apache Felix – Multiple implementation of Service, we had dicussed:

  1. How to create multiple implementation of a Service
  2. Get references of the registered service implementations as a List.

Here we would be discussing, on how to filter the references and get ONLY pre-defined specific implementation

Scenario:

Consider an online course website. A lecturer can publish his content by submitting a course bundle comprising of html pages, videos and assessment questions.

The lecturer had his/her course live for an year & is appreciated by many curious students. The lecturer now wishes to update the assessment questionnaire, to challenge students to dig deeper.

He/She now submit the latest questionnaire to the onlint course portal for updates.

At this point, the course website needs to assure, that only the “Assessment Import” service implementation is called amogst all available Import implementation (Page, Videos and Assessment)

Implementation details:

Specific service references can be fetched by utilizing the “target” attribute of @Reference annotation. It would comprise of 2 simple steps

Step 1: Add property to identify each implementation

You can add any custom property to uniquely identify the service. For example, in the below sample code, I have added “type” property to uniquely identify my each implementation of CourseImport Service

Page Import Implementation
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import blog.techrevel.service.CourseImport;

@Component(name = "Import HTML Pages")
@Service
@Properties({ @Property(name = "type", value = "page")})
public class PageImport implements CourseImport {

    private static final Logger LOGGER = LoggerFactory.getLogger(PageImport.class);

    @Override
    public void importContent() {
       LOGGER.info("Business Logic to import HTML Pages");
    }

    @Override
    public boolean canProcess(String fileName) {
       return StringUtils.endsWith(fileName, ".html");
    }
}
Video Import Implementation
@Component(name="Import course videos")
@Service
@Properties({ @Property(name = "type", value = "video")})
public class VideoImport implements CourseImport{

 private static final Logger LOGGER = LoggerFactory.getLogger(VideoImport.class);

    @Override
    public void importContent() {
        LOGGER.info("Business Logic to import course videos");
    }

    @Override
    public boolean canProcess(String fileName) {
        return StringUtils.endsWith(fileName, ".mp4");
    }
}
Assessment Import Implementation
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import blog.techrevel.service.CourseImport;

@Component(name="Import assessment questions")
@Service
@Properties({ @Property(name = "type", value = "assessment")})
public class AssessmentImport implements CourseImport{

    private static final Logger LOGGER = LoggerFactory.getLogger(AssessmentImport.class);

    @Override
    public void importContent() {
        LOGGER.info("Business Logic to import assessment questions");
    }

    @Override
    public boolean canProcess(String fileName) {
       return StringUtils.equals(fileName, "assessment.xls");
    }
}

 

Step 2: Use target attribute of @Refernce annotation to filter implementation

The target attribute filters services based on a property available in their ComponentContext

Following sample code, filters only PageImport service by matching ‘page’ value against type property of each implementation.

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import blog.techrevel.service.CourseImport;
import blog.techrevel.service.CourseImportHandler;

@Component(immediate = true)
@Service
public class AssessmentUpdateHandler implements CourseImportHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CourseImportHandler.class);

    @Reference(target="(type=page)")
    private CourseImport courseImport;

    @Override
    public void importContent() {
        courseImport.importContent();
    }
}

 

Using patterns for reference filtering

The target attribute of @Reference annotation also accepts regex (ldap) patterns. For example:

@Reference(target="(type=*age)")
@Reference(target="(|(type=p*)(type=generic))")