OSGi Component Vs Service in AEM

AEM ships with an OSGi container Apache felix that implements Declarative Services (DS) component model. Most of the developers who are new to AEM often gets confused between OSGi components and services.

OSGi Component
If you want the life of your object to be managed by the OSGi container, you should declare it as a component. Using DS annotations, you could make a POJO a OSGi component by annotating it with@Component With this, you will get the ability to start, stop and configure the component using the felix web console.

OSGi Service
OSGi components can be made as OSGi service by marking it with @Service annotation. All it mandates is that an interface – Services should implement an interface (1 or more). When you mark a component as service, you could refer (call) this service from other osgi components.

OSGi Component Vs Service

  • All objects managed by OSGi container are components. You qualify components as services. This means that all services are components but not vice-versa.
  • Components can refer/call (using container injection – @Reference) other services but not components. In other words, a component cannot be injected into another component / service. Only services can be injected into another component.

OSGi Component Example
We’ve a weather printing component that creates a thread when activated (started) and calls the weather service (an OSGi service) to pull the weather details from yahoo server.

package com.computepatterns.apppatterns.osgi;

import java.io.IOException;
import java.util.Map;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(
    label = "Compute Patterns - Weather details printing osgi component.",
    description = "Sample OSGi component that uses thread and a OSGi service to print weather details in log.",
    immediate = true)
public class WeatherPrintingComponent {
  private static final Logger log = LoggerFactory.getLogger(WeatherPrintingComponent.class);

  /* Yahoo weather api end point */
  private static final String weatherApiEndpoint =
      "http://weather.yahooapis.com/forecastrss?p=80020&u=f";

  @Reference
  WeatherService weatherService;

  @Activate
  protected void activate(Map<String, String> config) {
    log.info("Weather printing component - activiated");
    // Set up a thread which wakes up every 5s and make a make a service call to fetch weather info
    // and print it in the log.
    Runnable task = () -> {
      try {
        while (!Thread.currentThread().isInterrupted()) {
          Thread.sleep(5000);
          try {
            log.info(weatherService.getWeatherFeed(weatherApiEndpoint));
          } catch (IOException e) {
            log.error("Unable to get weather details.", e);
          }
        }
      } catch (InterruptedException e) {
        log.error("Weather printing thread interrupted", e);
      }
    };

    Thread weatherThread = new Thread(task);
    weatherThread.setName("Compute Patterns - Weather printing");
    weatherThread.start();
  }
}

WeatherPrintingComponent class has been marked with @Component annotation (line# 12). A service WeatherService is injected in to the component (line #.24). Remember that a component can refer other services.
Here’s the source code for WeatherService interface and its implementation.

package com.computepatterns.apppatterns.osgi;

import java.io.IOException;

/**
 * Service to provide weather details.
 */
public interface WeatherService {

  /**
   * Get weather feed using given end point.
   * 
   * @param apiEndPoint Url end point to hit and get the weather feed. Example - Yahoo weather end
   *        point.
   * @return Weather feed in xml format.
   * @throws IOException Exception in connecting to the url.
   */
  String getWeatherFeed(String apiEndPoint) throws IOException;
}
package com.computepatterns.apppatterns.osgi.impl;

import java.io.IOException;
import java.util.Map;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang.StringUtils;

import com.computepatterns.apppatterns.osgi.WeatherService;

/**
 * Weather service implementation. Connects to the provided end point using apache http client to
 * fetch the feed.
 *
 */
@Component(label = "Compute Patterns - Weather Service.",
    description = "Connects to the weather apis and fetches weather details.")
@Service
public class WeatherServiceImpl implements WeatherService {
  private static final Logger log = LoggerFactory.getLogger(WeatherServiceImpl.class);

  @Activate
  protected void activate(Map<String, String> config) {
    log.info("Weather Service - ACTIVATED");
  }

  @Override
  public String getWeatherFeed(String apiEndPoint) throws IOException {
    // Sanity check the arguments.
    if (StringUtils.isBlank(apiEndPoint)) {
      return StringUtils.EMPTY;
    }
    // Create a http client and hit the server.
    HttpClient httpClient = new HttpClient();
    GetMethod httpMethod = new GetMethod(apiEndPoint);
    // Return the response body if the request is successfully executed.
    if (httpClient.executeMethod(httpMethod) == HttpStatus.SC_OK) {
      log.trace("Successfully fetched data from the endpoint.");
      return httpMethod.getResponseBodyAsString();
    }
    log.trace("Connection not successful.");
    return StringUtils.EMPTY;
  }
}

 OSGi Component – Use cases
Now, you must be wondering, why you shouldn’t be marking every component as service. Yes, you want your objects to be usable outside its body. However, in certain use-cases modeling your object as component (not marking it as service) makes sense. Here are few of them.

  • A server object in your application which listens to a socket.
  • A filter object which intercepts the requests.
  • An object which monitors a resource and report.
  • An objects which pulls data from external system and writes to the repository.

Grab the complete OSGi component vs service source code from github aem project.

Learn more on OSGi design patterns including basics on OSGi trail.

OSGi Component Vs Service in AEM