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.
Nicely explained :-)…
Thumbs up …
Great article!
Thank you.
This article giving Crystal clear difference between components and services . awesome
Thank you.
Great Article!!! But I have one question in the WeatherServiceImpl, why do we need to have the @Component(), what happens if I dont have that annotation and have only the @Service()?
If you don’t have @Component annotation your class, it will not be registered at all. So @Component is mandatory to make your class as OSGI component.
+1
Great Article, but i have a confusion,
Don’t we provide the service interface name in @Service annotation?
Not mandatory. If you don’t provide one, it picks all the implemented interfaces for the class.
Hi Sivaram,
Nice Article! I got that ‘all services are components but not vice-versa’. But still confused to answer below question 🙂 .
is Answer B or C?
————————————————————-
Which is a valid way of creating an OSGI service?
A. class MyServiceImplextends ServiceRegistry{}
B. @Service
classMyServiceImplextends MyService…
}
C. @Component
@Service class MyServiceImplextends MyService
D. /**
*
*
*/
classMyServiceImpI extends MyService
{
}
————————————————————-
I think Option-2 is perfect answer.