Skip to main content

Command Palette

Search for a command to run...

A Deep Dive into OSGi Components and Services

Updated
7 min read

The Final Post in Our OSGi Series!

In our previous post, we explored the IoC, and Dependency Injection (DI) - the foundation that makes OSGi modular, dynamic, and flexible. Understanding these concepts is essential before diving into Components and Services, as they rely on IoC and DI for runtime wiring and dynamic service injection.

In this post, we’ll cover:

  • What Components are and how they relate to bundles

  • How Services work in OSGi

  • The role of the Service Registry in dynamic service discovery and injection

  • How Declarative Services (DS) simplify component and service management

By the end of this post, you’ll understand how OSGi bundles expose, consume, and dynamically wire services, completing the picture of how OSGi achieves true modularity and runtime flexibility.


What Is a Component?

A component in OSGi is a logical unit of functionality inside a bundle. It is self-contained, declarative, and managed by the container. Components allow bundles to provide or consume services dynamically without tight coupling.

Key Characteristics:

  • Self-contained: Implements a specific function or service

  • Declarative: Dependencies, lifecycle, and configuration are defined via annotations or metadata

  • Container-managed: The OSGi container controls activation, deactivation, and dependency injection

Example Component:

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component
public class OrderProcessor {

    @Reference
    private PaymentService paymentService;

    public void process(Order order) {
        paymentService.processPayment(order);
    }
}

Explanation:

  • OrderProcessor is a component inside a bundle

  • It declares a dependency on PaymentService

  • The OSGi container injects the PaymentService automatically at runtime (DI)

  • Component lifecycle is managed by the container - no need for manual initialization


Component Lifecycle in OSGi

Every OSGi component has a lifecycle managed by the container via SCR (Service Component Runtime). This ensures components are only active when all dependencies are satisfied, and they react dynamically to runtime changes.

Lifecycle States:

  1. Inactive / Unsatisfied – Dependencies missing, component not active

  2. Active / Satisfied – Dependencies available, @Activate is called

  3. Modified (optional) – Runtime configuration changes trigger @Modified

  4. Deactivated – Component is stopped or a dependency disappears; @Deactivate is called

Example

import org.osgi.service.component.annotations.*;

@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {

    @Activate
    void activate() {
        System.out.println("PaypalPaymentService Activated");
    }

    @Modified
    void modified() {
        System.out.println("Configuration changed dynamically");
    }

    @Deactivate
    void deactivate() {
        System.out.println("PaypalPaymentService Deactivated");
    }
}

Key Points:

  • The SCR manages the lifecycle automatically

  • Components never manually look up services - container injects them

  • Lifecycle + DS (Declarative Services) + SCR (Service Component Runtime) = dynamic, hot-swappable components


What Is a Service?

A service in OSGi is a Java object registered under one or more interfaces, optionally with metadata (properties).

Key Characteristics:

  • Interface-based – Consumers depend on the interface, not implementation

  • Dynamic – Services can appear, disappear, or be replaced at runtime

  • Registry-managed – Service Registry tracks all services

Example

import org.osgi.service.component.annotations.Component;

@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
    @Override
    public void processPayment(Order order) {
        System.out.println("Processing via PayPal: " + order.getId());
    }
}
  • Declares itself as a service

  • Automatically registered in Service Registry

  • Consumers can bind dynamically without knowing the implementation


Components vs Services

It’s important to clarify the relationship between components and services in OSGi:

  • Every service is a component:

    • A service must exist inside a component to be registered in the Service Registry.

    • The component defines how the service is implemented, activated, and injected.

  • Not every component is a service:

    • A component may perform internal logic or act as a helper without exposing any service interface.

    • Such components are still managed by the container and can consume services, but they do not register themselves in the Service Registry.

Example

@Component
public class InternalHelper {
    // Component with internal logic, not registered as a service
    public void doSomethingInternal() {}
}

@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
    // Component exposing a service
}

Key Points:

  • PaypalPaymentServiceComponent + Service

  • InternalHelperComponent only (no service registration)

  • The container manages both, but only services appear in the Service Registry


How Components and Services Work Together

OSGi enables dynamic wiring of components and services:

  1. Components declare dependencies (IoC)

  2. Services are registered in the Service Registry

  3. Container injects services into components (DI)

  4. Components consume services without knowing implementation

  5. Hot updates: Services can be replaced or updated at runtime


Declarative Services (DS): The Backbone of Modern OSGi Applications

Declarative Services (DS) is the recommended programming model for building OSGi components and services. It allows developers to declare components, their dependencies, and their lifecycle using metadata and annotations, rather than writing complex framework code.

In simple terms, DS lets you focus on business logic while the OSGi container handles wiring, lifecycle, and dependency management for you.

Why Declarative Services Exist

Before DS, developers had to:

  • Manually register services in the Service Registry

  • Track service availability

  • Handle dynamic binding and unbinding

  • Manage lifecycle code in Bundle Activators

This approach was:

  • Verbose

  • Error-prone

  • Hard to maintain

Declarative Services solve this by moving all that complexity into the framework.


Component Lifecycle in Declarative Services

DS components have a well-defined lifecycle, fully managed by the container:

@Component
public class InventoryService {

    @Activate
    void activate() {
        System.out.println("Component activated");
    }

    @Deactivate
    void deactivate() {
        System.out.println("Component deactivated");
    }
}

Lifecycle states are controlled by:

  • Service availability

  • Configuration changes

  • Bundle lifecycle events

You do not control lifecycle manually - DS does.


What Is SCR (Service Component Runtime)?

Declarative Services do not work by magic.

Behind the scenes, DS is implemented by the Service Component Runtime (SCR). It is the Engine Behind Declarative Services.

SCR is the runtime engine for Declarative Services:

  • Reads component metadata (annotations/XML)

  • Registers services in the Service Registry

  • Creating component instances

  • Injects dependencies dynamically (DI)

  • Manages lifecycle methods: activate(), deactivate(), modified()

  • Handles hot updates and dynamic rebinding

Example

@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
    @Activate
    void activate() {}
    @Deactivate
    void deactivate() {}
    @Modified
    void modified() {}
}
  • SCR performs all runtime wiring automatically

  • Ensures dynamic, modular behavior without manual service lookups

DS is the programming model
SCR is the runtime engine that makes it work

Without SCR, there is no DS.


Why DS Is the Preferred Model in OSGi

Declarative Services are considered best practice because they:

  • Remove boilerplate code

  • Enforce loose coupling

  • Support true runtime dynamism

  • Integrate seamlessly with IoC and DI

  • Work naturally with the Service Registry

  • Scale cleanly in large modular systems

This is why platforms like Adobe AEM, Eclipse, and Apache Karaf rely heavily on DS.


Key Takeaways

  • Components: Building blocks inside bundles

  • Services: Interface-based objects registered in Service Registry

  • IoC + DI: Components declare dependencies; container injects services

  • Declarative Services: Simplifies wiring and lifecycle management

  • SCR: Runtime engine that orchestrates DS, lifecycle, and service injection

  • Component Lifecycle: Inactive → Active → Modified → Deactivated, fully managed by container


This post wraps up our OSGi series. You now have a complete understanding of:

  • OSGi Container – runtime engine

  • Bundles & Classloading – modular packaging and isolation

  • IoC and DI – dynamic wiring

  • Components and Services – building and consuming dynamic modular functionality

  • SCR and DS – runtime orchestration

With this knowledge, you’re ready to design and implement truly modular, dynamic, and flexible Java applications using OSGi.

OSGi Deep Dive Series

Part 5 of 10

OSGi is often described as complex or hard to understand, mainly because its concepts are deeply interconnected - containers, bundles, classloading, services, IoC, DI, and declarative components all work together at runtime.

Up next

Multiple Implementations of the Same Service in OSGi

One of the most common questions when working with OSGi services is: What if more than one bundle provides an implementation of the same service interface? In traditional Java or even Spring-based a