April 6, 2020
Estimated Post Reading Time ~

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 have 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 the 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 into the component (line #.24). Remember that a component can refer to other services.
Here’s the source code for the 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 endpoint.
*
* @param apiEndPoint Url endpoint 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 used outside its body. However, in certain use-cases modeling your object as component (not marking it as service) makes sense. Here are a 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 the external system and writes to the repository.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.