Day 29 of 40
OSGi Services in Practice
Writing services, the component lifecycle, and references
By the end of today you should be able to
- Write an OSGi service with Declarative Services annotations
- Explain the component lifecycle and when activate and deactivate run
- Choose correctly between mandatory, optional, static and dynamic references
- Use service ranking to override an existing service
Writing a service
Define an interface, implement it, annotate the implementation. That is the whole pattern:
public interface AdventureService {
List<Adventure> findByDifficulty(String difficulty);
}
@Component(service = AdventureService.class, immediate = true)
public class AdventureServiceImpl implements AdventureService {
private static final Logger LOG =
LoggerFactory.getLogger(AdventureServiceImpl.class);
@Reference
private ResourceResolverFactory resolverFactory;
@Activate
protected void activate() {
LOG.info("AdventureService starting");
}
@Deactivate
protected void deactivate() {
LOG.info("AdventureService stopping");
}
@Override
public List<Adventure> findByDifficulty(String difficulty) { ... }
}
service = AdventureService.classpublishes it under that interface. Omittingserviceregisters the component without publishing a service — fine for something that only reacts to events.immediate = trueactivates it at startup rather than lazily on first use. Use it when the component must do something on its own, such as register a listener or start a scheduler.
The lifecycle
A DS component moves through a small set of states, and knowing them makes diagnosis quick:
- Unsatisfied — a mandatory reference cannot be bound. It will not activate.
- Satisfied — all mandatory references are available. For a lazy component, it waits here until first use.
- Active —
@Activatehas run and it is serving.
/system/console/components shows the state, and for unsatisfied components it names the
reference that could not be bound. That single page answers most "why isn't my service running"
questions.
Bundles stop when you redeploy, and on Cloud Service instances are replaced routinely. Anything you start in @Activate — a thread, a listener, a scheduled task — must be stopped in @Deactivate, or you leak it across redeploys.
Reference cardinality and policy
Two independent dimensions on @Reference:
- Cardinality —
MANDATORY(default),OPTIONAL,MULTIPLE,AT_LEAST_ONE. - Policy —
STATIC(default: the component is deactivated and reactivated when the reference changes) orDYNAMIC(the field is swapped without restarting the component).
// Collect every implementation, and cope with them coming and going
@Reference(cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC)
private volatile List<AdventureProvider> providers;
A dynamic multiple reference must be volatile, because another thread can replace it while
your code reads it. This is the standard pattern for a plug-in style extension point.
Service ranking
When several components implement the same interface, the one with the highest
service.ranking wins for a single-reference injection. This is how you override an AEM
service without touching Adobe's code:
@Component(
service = SomeAemService.class,
property = { Constants.SERVICE_RANKING + ":Integer=1000" }
)
public class MyOverridingImpl implements SomeAemService { ... }
Powerful and worth using carefully — you have now taken responsibility for behaviour Adobe may change or rely on elsewhere. Prefer composition where you can.
Getting a ResourceResolver in a service
A service has no request, so it has no user. It needs a service user — covered properly on day 31 — and the resolver must always be closed:
Map<String, Object> params = Collections.singletonMap(
ResourceResolverFactory.SUBSERVICE, "wknd-content-reader");
try (ResourceResolver resolver =
resolverFactory.getServiceResourceResolver(params)) {
Resource r = resolver.getResource("/content/wknd");
// ...
} // closed automatically
It is deprecated and unavailable on Cloud Service, and it bypasses the permission model entirely. Use a service user scoped to exactly the access it needs — and always close the resolver, or you leak repository sessions until the instance degrades.
@Component, @Reference, @Activate, @Deactivate. Clean up what you start, use dynamic multiple references for extension points, and always close service resolvers.
Watch
Adobe's own videos for this topic. They load only when you press play.
Read on Experience League
The primary sources these notes are drawn from.
Your notes
Saved automatically to this browser.
Check yourself
10 questions on today's material. 80% to pass.