Study guide
4.2 OSGi Declarative Services, Component Lifecycle & Service Rankings
Adobe Certified Professional - AEM Sites Developer AD0-E128
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Dispatcher Farm & Multi-Site Configuration
1.1 AEM Dispatcher Configuration & Caching Fundamentals 1.2 Multi-Site Management (MSM) & Blueprint/Live Copy Architecture 1.3 OSGi Configuration Management & Run Modes (.cfg.json)
2 Chapter 2: Oak Indexing & Custom Logging
2.1 Custom Oak Index Definitions & Lucene Query Optimization 2.2 AEM Custom Logging, Logback Configurations & Request Tracing
3 Chapter 3: Component Architecture, HTL & Client Libraries
3.1 Core Components & Proxy Component Pattern 3.2 HTL (HTML Template Language) Block Statements & Context-Aware Escaping 3.3 Client-Side Libraries (Clientlibs) Architecture & Management
4 Chapter 4: Sling Models & OSGi Declarative Services
4.1 Sling Models Framework, Injectors & Model Exporter 4.2 OSGi Declarative Services, Component Lifecycle & Service Rankings
5 Chapter 5: Templates, Fragments & Business Workflows
5.1 Editable Templates, Structure, Initial Content & Content Policies 5.2 Content Fragments, Experience Fragments & Headless Delivery 5.3 AEM Workflows, Custom Process Steps & Launchers
6 Chapter 6: Build, Packaging & Cloud Manager CI/CD
6.1 Maven Project Structure & AEM Project Archetype 6.2 Content Package Types, Packaging Rules & FileVault Validation 6.3 Cloud Manager CI/CD Pipelines & Quality Gates 6.4 Rapid Development Environments (RDE) & Sling Repoinit Scripts
7 Chapter 7: Environment Maintenance & Content Distribution
7.1 AEM Maintenance Releases & Cloud Service Version Updates 7.2 AEM Deployment Topologies & Cloud Architecture 7.3 Replication Agents & Sling Content Distribution 7.4 Repository Maintenance: Revision Cleanup, Compaction & Purge Tasks
8 Chapter 8: Analyzing, Debugging & Diagnostic Tools
8.1 System Log Analysis & Diagnostic Tooling 8.2 Debugging Client-Side Libraries & Granite Dumplibs 8.3 Dispatcher Caching Diagnostics, X-Cache-Info & Local Validation 8.4 Environment Troubleshooting, Web Console & Developer Console
4.2 OSGi Declarative Services, Component Lifecycle & Service Rankings
Key Takeaways Explain This Topic AI
Modern AEM uses standard OSGi R7 Declarative Services annotations (org.osgi.service.component.annotations.*), generating XML descriptors at build time and deprecating legacy Apache Felix SCR annotations.
The @Component annotation controls service registration and activation. A component is delayed when it provides a service unless immediate activation is explicitly required for activation-time work.
The @Reference annotation configures dependency injection with precise cardinality (MANDATORY, OPTIONAL, MULTIPLE), policy (STATIC vs DYNAMIC), and LDAP filter targets.
The @Modified lifecycle callback allows components to process runtime configuration updates in place without undergoing disruptive deactivation and reactivation.
When multiple service implementations exist, OSGi resolves references by highest service.ranking integer value, breaking ties with lowest service.id (oldest registration).
Last updated: September 2026
4.2 OSGi Declarative Services, Component Lifecycle & Service Rankings
Exam Focus: OSGi Declarative Services (DS) constitutes the foundational service architecture of Adobe Experience Manager. The AD0-E128 exam tests your practical knowledge of the OSGi R7 specification ( @Component , @Reference , @Activate , @Modified , @Deactivate ), component configuration policies and immediate execution ( immediate = true/false ), reference cardinality and dynamic policies , service ranking resolution ( service.ranking ), type-safe OSGi configuration interfaces ( @ObjectClassDefinition ), and critical architectural best practices regarding thread safety and circular dependency prevention .
Evolution to OSGi R7 Declarative Services
In older versions of AEM (AEM 6.0–6.3), OSGi components were authored using Apache Felix SCR annotations ( @Service , @Component , @Property , @Reference under package org.apache.felix.scr.annotations ).
Modern AEM (AEM 6.4, 6.5, and AEM as a Cloud Service) has completely deprecated Felix SCR annotations in favor of the official OSGi R7 Declarative Services specification ( org.osgi.service.component.annotations.* ). During the Maven build, the bnd-maven-plugin (or maven-bundle-plugin ) processes standard OSGi R7 annotations at compile time, generating declarative XML component descriptors inside the bundle's OSGI-INF/ directory.
- Aspect Legacy Apache Felix SCR (Deprecated) Modern OSGi R7 Declarative Services
- Package org.apache.felix.scr.annotations.* org.osgi.service.component.annotations.*
- Service Declaration @Service(MyService.class) @Component(service = MyService.class)
- Configuration Dictionary<String, Object> or @Property Type-safe @ObjectClassDefinition interfaces
- Build Processing maven-scr-plugin (runtime reflection) Bnd / bnd-maven-plugin (compile-time bytecode)
- Cloud Manager Gate Flags warnings or deployment failures Mandatory standard for Cloud Manager pipelines
- The @Component Annotation in Depth
The @Component annotation marks a Java class as an OSGi Declarative Services component managed by the Service Component Runtime (SCR). package com.myproject.core.services.impl;
- import com.myproject.core.services.PaymentGatewayService;
- import org.osgi.service.component.annotations.Component;
- import org.osgi.service.component.annotations.ConfigurationPolicy;
- import org.osgi.service.component.propertytypes.ServiceDescription;
- import org.osgi.service.component.propertytypes.ServiceRanking;
- @Component(
- service = PaymentGatewayService.class,
- immediate = false,
- configurationPolicy = ConfigurationPolicy.OPTIONAL,
- property = {
- "service.vendor=MyProject",
- "process.label=Custom Payment Processor"
- }
- )
- @ServiceDescription("Enterprise Payment Gateway Service Implementation")
- @ServiceRanking(100)
- public class PaymentGatewayServiceImpl implements PaymentGatewayService {
// implementation...
- }
- Key @Component Properties Explained
service = MyService.class : Explicitly registers the component under the specified interface. If the service element is omitted, Declarative Services can infer provided service interfaces from the implementation; use an explicit service element when the public contract should be unambiguous.
immediate = true vs immediate = false (Delayed / Lazy Components) :
When immediate = false (the default for components declaring a service interface), the component is lazy-loaded . The OSGi runtime creates and activates the component instance only when another component requests it (via @Reference or bundleContext.getService() ). This minimizes memory footprint and accelerates AEM bundle startup.
When immediate = true , the component activates as soon as its mandatory references and configuration are satisfied, even if no consumer has requested its service. Use this when activation itself must perform work and no service consumer would otherwise trigger a delayed component. Do not assume every event handler, job consumer, or scheduled service requires immediate activation; whiteboard registrations and provided services can activate through normal DS demand.
configurationPolicy :
ConfigurationPolicy.OPTIONAL (default): Activates with or without an OSGi configuration.
ConfigurationPolicy.REQUIRE : Activation is blocked until an explicit configuration for this component's PID is created in ConfigurationAdmin.
ConfigurationPolicy.IGNORE : Completely ignores any OSGi configuration files.
Dependency Injection with @Reference
The @Reference annotation injects required or optional OSGi services into a component. @Component(service = OrderProcessingService.class)
public class OrderProcessingServiceImpl implements OrderProcessingService {
- // Mandatory, static reference (1..1)
- @Reference
- private PaymentGatewayService paymentService;
- // Optional dynamic reference with LDAP filter target
- @Reference(
- cardinality = ReferenceCardinality.OPTIONAL,
- policy = ReferencePolicy.DYNAMIC,
- policyOption = ReferencePolicyOption.GREEDY,
- target = "(integration.environment=production)"
- )
- private volatile AnalyticsService analyticsService;
- // Multiple references (0..n)
- @Reference(
- cardinality = ReferenceCardinality.MULTIPLE,
- policy = ReferencePolicy.DYNAMIC
- )
- private final List<OrderValidator> validators = new CopyOnWriteArrayList<>();
- }
- Reference Configuration Options
- 1. cardinality
Defines the multiplicity and necessity of the dependency:
ReferenceCardinality.MANDATORY ( 1..1 , default): Exactly one service instance must be bound. The consuming component cannot activate until this dependency is satisfied.
ReferenceCardinality.OPTIONAL ( 0..1 ): The dependency can be null. The consuming component activates regardless.
ReferenceCardinality.MULTIPLE ( 0..n ): Injects zero or more instances of the service interface.
ReferenceCardinality.AT_LEAST_ONE ( 1..n ): Requires at least one service implementation to activate.
2. policy : STATIC vs DYNAMIC
ReferencePolicy.STATIC (default): If the referenced service is modified, restarted, or replaced, the consuming component is deactivated and reactivated . This guarantees state consistency but causes cascade restarts across connected services.
ReferencePolicy.DYNAMIC : The referenced service can be bound, unbound, or replaced dynamically without deactivating the consuming component. Fields using dynamic policy should be marked volatile or managed via thread-safe collections ( CopyOnWriteArrayList ).
3. policyOption : RELUCTANT vs GREEDY
ReferencePolicyOption.RELUCTANT (default): If a new, higher-ranked matching service becomes available, the component keeps the currently bound service.
ReferencePolicyOption.GREEDY : If a higher-ranked service appears, OSGi immediately rebinds the reference to the new, superior service.
4. target : LDAP Filter Expressions
Restricts injected services based on OSGi properties:
- target = "(service.ranking>=500)"
- target = "(&(type=cloud)(region=us-east))"
- target = "(component.name=com.myproject.SpecificService)"
- Declarative Services Lifecycle: @Activate , @Modified , @Deactivate
OSGi components progress through an explicit lifecycle managed by the Service Component Runtime. @Component(service = ConfigurationDemoService.class)
- @Designate(ocd = ConfigurationDemoService.Config.class)
- public class ConfigurationDemoServiceImpl implements ConfigurationDemoService {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationDemoServiceImpl.class);
- @ObjectClassDefinition(name = "Configuration Demo Service Config")
- public @interface Config {
- @AttributeDefinition(name = "API Endpoint")
- String apiEndpoint() default "https://api.example.com/v1";
- @AttributeDefinition(name = "Timeout in Seconds")
- int timeoutSeconds() default 30;
- }
private volatile Config config;
- @Activate
- protected void activate(Config config, ComponentContext componentContext, BundleContext bundleContext) {
- this.config = config;
- LOG.info("Service activated with endpoint: {}", config.apiEndpoint());
- }
- @Modified
- protected void modified(Config config) {
- this.config = config;
- LOG.info("Service configuration dynamically refreshed: timeout = {}", config.timeoutSeconds());
- }
- @Deactivate
- protected void deactivate(ComponentContext componentContext) {
- LOG.info("Service deactivating; releasing resources");
- // Close background executors, network sockets, or caches
- }
- }
- Lifecycle Callback Mechanics
@Activate : Executed once all mandatory references are bound and configuration constraints are satisfied. Used to initialize connections, start thread pools, or validate credentials.
@Modified : Crucial for zero-downtime updates. When an administrator updates an OSGi configuration via .cfg.json or Web Console, OSGi inspects the component. If @Modified is present, OSGi invokes it directly with the new configuration. The component updates its internal state in place without undergoing @Deactivate and @Activate cycles , avoiding broken user sessions. If @Modified is omitted, OSGi is forced to tear down and reconstruct the entire component.
@Deactivate : Executed when the component is stopped, the host bundle is stopped, or a mandatory reference is unbound. Must perform complete resource cleanup (e.g., closing background threads, clearing caches) to prevent memory leaks in the OSGi JVM.
Service Ranking & Multi-Implementation Resolution
In enterprise AEM systems, multiple OSGi services often implement the same public interface (e.g., a standard fallback service and a specialized client override).
When a consuming component declares a 1..1 reference to the shared interface without an LDAP target filter, OSGi resolves the tie using a deterministic algorithm:
service.ranking (Primary Criterion): The service with the highest integer ranking is selected. Rankings can be positive, zero (default), or negative (e.g., service.ranking:Integer=1000 overrides service.ranking:Integer=100 ).
service.id (Tie-Breaker): If multiple services have identical service.ranking values, OSGi selects the service with the lowest service.id (meaning the service that was registered first in the OSGi runtime).
Setting Service Ranking
In OSGi R7, ranking can be set via @Component(property = {"service.ranking:Integer=1000"}) or using the type-safe @ServiceRanking(1000) annotation: @Component(service = CustomWorkflowRule.class)
- @ServiceRanking(5000) // Ensures this custom rule executes before default AEM rules (ranking 0)
- public class CustomWorkflowRuleImpl implements CustomWorkflowRule { ... }
- Type-Safe OSGi Configurations ( @ObjectClassDefinition )
Modern AEM uses standard Java annotations to define typed OSGi configuration schemas:
@ObjectClassDefinition (OCD) : Declares the metadata container for the configuration.
@AttributeDefinition : Defines individual configuration properties, including labels, descriptions, data types ( STRING , INTEGER , BOOLEAN , PASSWORD ), and default values.
@Designate(ocd = MyConfig.class) : Binds the configuration definition to the component implementation.
Single vs Factory Configurations
Standard Configuration : A single configuration instance per service.
Factory Configuration ( @Designate(ocd = MyConfig.class, factory = true) ) : Allows creating multiple independent runtime instances of the service, each with its own configuration. In AEM as a Cloud Service, factory configurations are deployed in ui.config using .cfg.json files with sub-service identifiers: com.myproject.core.services.impl.ExportServiceImpl~us_region.cfg.json
Thread Safety, State Management & Anti-Patterns
Because OSGi components are singletons shared across the entire AEM JVM, failure to observe concurrency standards results in severe production outages.
- Critical Rules for Production Services
- 1. Never Store Stateful Objects as Instance Fields
Multiple HTTP request threads execute service methods concurrently. Storing request-specific or session-specific objects as service fields is a fatal bug: @Component(service = BadService.class)
public class BadServiceImpl implements BadService {
// FATAL: ResourceResolver is NOT thread-safe!
- private ResourceResolver requestResolver;
- }
Concurrent requests will overwrite and mutate requestResolver , resulting in unhandled IllegalStateException , race conditions, and repository session corruption.
Correct Pattern: Scope ResourceResolver locally within method calls, always using try-with-resources: public void processContent(ResourceResolverFactory factory) {
try (ResourceResolver resolver = factory.getServiceResourceResolver(authInfo)) {
// execute operations safely...
- } catch (LoginException e) {
- LOG.error("Failed to obtain service resolver", e);
- }
- }
- 2. Managing Dynamic Configuration State
When configurations change via @Modified , assign the new configuration to a volatile reference to ensure thread visibility across CPU caches without heavy synchronization locking: private volatile Config config;
- @Modified
- protected void modified(Config config) {
- this.config = config; // Thread-safe atomic pointer swap
- }
- 3. Preventing Circular Dependencies
A circular dependency occurs when ServiceA requires ServiceB , and ServiceB requires ServiceA .
If both references are ReferenceCardinality.MANDATORY and ReferencePolicy.STATIC , neither service can ever activate. Both remain indefinitely in the UNSATISFIED_REFERENCE state, deadlocking the components.
Resolution Strategies:
Refactor Domain Logic: Extract the shared functionality into a third intermediary service ( ServiceC ).
Break Cardinality: Change one reference to ReferenceCardinality.OPTIONAL and dynamic policy.
Event-Driven Decoupling: Decouple the services asynchronously using Apache Sling Jobs or OSGi EventAdmin .