Using Annotations with OSGi Declarative Services

Introduction

The OSGi compendium specification has now standardized the annotation usage for declarative services. The core services bundle (org.eclipse.osgi.services) includes the set of classes and packages that can be used for this purpose. The specification clearly mentions that there should be some tools that are capable of processing these annotation and generate the declarative services component descriptor files. The Apache Felix project has developed these tools (maven dependencies and plugins) which can process the standard annotations and generate the descriptor meta files for you. A point to note that Apache Felix project also has its own annotations, based on org.apache.felix.scr.annotations dependency, which has similar names as those mentioned in the specification. The standard annotations specified by the compendium specification are supported by the org.apache.felix.scr.ds-annotations  felix project. This post will focus on the standard annotations and how to use within your project.

Dependencies

The main dependencies and plugins you will be needing are as follows.

<dependency>
     <groupId>org.eclipse.osgi</groupId>
     <artifactId>org.eclipse.osgi.services</artifactId>
</dependency>

<dependency>
     <groupId>org.apache.felix</groupId>
     <artifactId>org.apache.felix.scr.ds-annotations</artifactId>
</dependency>
<plugin>
     <groupId>org.apache.felix</groupId>
     <artifactId>maven-scr-plugin</artifactId>
     <version>1.16.0</version>
     <executions>
          <execution>
               <id>generate-scr-scrdescriptor</id>
               <goals>
                    <goal>scr</goal>
               </goals>
          </execution>
     </executions>
</plugin>

Alternatively using maven-bundle-plugin, you can use the “_dsannotations” instruction to process the annotated declarative service components  and then component descriptors will be generated. An example of using maven-bundle-plugin for this purpose is below.

<plugin>
     <groupId>org.apache.felix</groupId>
     <artifactId>maven-bundle-plugin</artifactId>
     <version>2.4.0</version>
     <extensions>true</extensions>
     <configuration>
          <instructions>
               <Bundle-Vendor>WSO2 Inc</Bundle-Vendor>
               <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
               <Export-Package>
                    org.wso2.carbon.deployment.*;version="5.0.0"
               </Export-Package>
               <Import-Package>
                    org.slf4j.*;version="${slf4j.logging.import.version.range}",
                    org.wso2.carbon.kernel.*;version="${carbon.kernel.package.import.version.range}",
                    org.osgi.framework.*;version="${osgi.framework.import.version.range}"
               </Import-Package>
               <_dsannotations>*</_dsannotations>
          </instructions>
     </configuration>
</plugin>

The available annotations classes are as below, based on the OSGi compendium specification.

  • Activate – Identify the annotated method as the activate method of a Service Component.
  • Component – Identify the annotated class as a Service Component.
  • ConfigurationPolicy – Configuration Policy for the Component annotation.
  • Deactivate – Identify the annotated method as the deactivate method of a Service Component.
  • Modified – Identify the annotated method as the modified method of a Service Component.
  • Reference – Identify the annotated method as a bind method of a Service Component.
  • ReferenceCardinality – Cardinality for the Reference annotation.
  • ReferencePolicy – Policy for the Reference annotation.
  • ReferencePolicyOption – Policy option for the Reference annotation.

Example Usage

Below is an example service component class which uses the standard annotations.

import org.osgi.service.component.annotations.Activate;
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.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.servlet.SampleServlet;

import javax.servlet.ServletException;

/**
 * This service  component is responsible for retrieving the HttpService
 * OSGi service and register servlets
 */

@Component(
        name = "org.wso2.carbon.transport.HttpServiceComponent",
        immediate = true
)
public class HttpServiceComponent {

    private static final Logger logger = LoggerFactory.getLogger(HttpServiceComponent.class);

    private HttpService httpService;

    @Activate
    protected void start() {
        SampleServlet servlet = new SampleServlet();
        String context = "/sample";
        try {
            logger.info("Registering a sample servlet : {}", context);
            httpService.registerServlet(context, servlet, null,
                                        httpService.createDefaultHttpContext());
        } catch (ServletException | NamespaceException e) {
            logger.error("Error while registering servlet", e);
        }
    }


    @Reference(
            name = "http.service",
            service = HttpService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.STATIC,
            unbind = "unsetHttpService"
    )
    protected void setHttpService(HttpService httpService) {
        this.httpService = httpService;
    }

    protected void unsetHttpService(HttpService httpService) {
        this.httpService = null;
    }
}

With the usage of maven dependencies and plugin given in the “Usage” section above, the component descriptor will be generated as below. Also if a component class is an implementation class of a service and needed to be registered as a service, then the @Component annotation will automatically populate the component descriptor with that information.

<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
     <scr:component immediate="true" name="org.wso2.carbon.transport.HttpServiceComponent" activate="start">
          <implementation class="org.wso2.carbon.transport.internal.HttpServiceComponent"/>
          <reference name="http.service" interface="org.osgi.service.http.HttpService" cardinality="1..1" policy="static" bind="setHttpService" unbind="unsetHttpService"/>
     </scr:component>
</components>

@Component

This annotation identifies the annotated class as a service component.

@Component(
        name = "ClusteringAgentServiceComponent",
        immediate = true,
        property = "Agent=hazelcast"
)

The component annotation can take multiple parameters like in the above example. Those will be available for that component at run-time. For example, if your component needs to register component or service level properties, then it can be done by using the “property” parameter and with one or many “key=value” pairs.

@Reference

    @Reference(
            name = "http.service",
            service = HttpService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.STATIC,
            unbind = "unsetHttpService"
    )

The @Reference annotation should be applied to a method which will be used as a “bind” method of a service component. You can refer the example class above. The “unbind” parameter specifies the unbind method in the service component, along with other needed parameters for the reference to satisfy.

@Acitvate, @Deactivate, @Modified

The above three annotation are used with the respective methods that will be called when the service component state changes from one to another. For example the @Activate annotated method gets invoked when the service component becomes satisfied with all the service references and their requirements.

Advertisements

About kishanthan

I’m currently working as a Software Engineer at WSO2, an open source software company. I hold an Engineering degree, majoring in Computer Science & Engineering field, from University of Moratuwa, Sri Lanka.
This entry was posted in How to, Java, OSGi and tagged , , , , , , . Bookmark the permalink.

2 Responses to Using Annotations with OSGi Declarative Services

  1. pkriensPeter says:

    These annotations are also supported by bnd … Since bnd is the underlying engine used in the maven bundle plugin (which is used by virtually all maven users making OSGi bundles anyway) you do not have to use yet another plugin. The only thing you need to add to the maven instructions section is:

    *</_dsannotations

    • kishanthan says:

      Hi Peter,

      Yes, you are correct. We can also use instruction with maven bundle plugin to process and generate DS descriptor files. Thanks for pointing it out.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s