# OSGi Services and References: Wiring Your Code Together

In the previous post we saw how a component starts and stops. Real features need components that **work together**: a servlet that calls a service, a service that uses another service, a workflow step that needs a helper. OSGi handles this wiring through **services** and **references**. This post shows how to design a service, consume it safely, and diagnose it when the wiring fails.

## Service = Interface + Implementation

The standard pattern is a small public interface and one implementation:

```java
package com.myproject.core.services;

public interface PriceService {
    String formatPrice(double amount, String currencyCode);
}
```

```java
package com.myproject.core.services.impl;

import com.myproject.core.services.PriceService;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;
import org.osgi.service.component.annotations.Component;

@Component(service = PriceService.class)
public class PriceServiceImpl implements PriceService {

    @Override
    public String formatPrice(double amount, String currencyCode) {
        NumberFormat format = NumberFormat.getCurrencyInstance(Locale.US);
        format.setCurrency(Currency.getInstance(currencyCode));
        return format.format(amount);
    }
}
```

`service = PriceService.class` is what **registers** the component as a service under that interface. Without it, the component exists but nobody can look it up.

Keep the interface in an API package and the implementation in a separate `impl` package. Other code depends only on the interface, so you can replace or mock the implementation without touching callers.

## Consuming a Service with @Reference

Inside another component, ask for the service with `@Reference`:

```java
@Component(service = Servlet.class)
public class PriceServlet extends SlingSafeMethodsServlet {

    @Reference
    private PriceService priceService;

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws IOException {
        response.setContentType("text/plain");
        response.getWriter().write(priceService.formatPrice(19.99, "USD"));
    }
}
```

OSGi finds an active `PriceService`, injects it, and **only then activates** the servlet. If no matching service exists, the servlet stays unsatisfied.

In a **Sling Model**, don't use `@Reference`. Use `@OSGiService`:

```java
@Model(adaptables = SlingHttpServletRequest.class,
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class PriceModel {
    @OSGiService
    private PriceService priceService;
}
```

## Reference Options That Matter

`@Reference` has defaults that suit most cases: **mandatory**, **static**. These attributes change the behavior:

| Attribute | Values | Meaning |
| --- | --- | --- |
| `cardinality` | `MANDATORY` (default), `OPTIONAL`, `MULTIPLE`, `AT_LEAST_ONE` | How many matching services you need and whether you can run without one |
| `policy` | `STATIC` (default), `DYNAMIC` | Whether a change to the service restarts your component (`STATIC`) or is handled live (`DYNAMIC`) |
| `target` | an LDAP-style filter | Pick a specific implementation by its properties |

Example of an **optional, dynamic** reference. The field must be `volatile`:

```java
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
private volatile AuditLogger auditLogger;

private void audit(String message) {
    AuditLogger logger = auditLogger;   // read once into a local variable
    if (logger != null) {
        logger.log(message);
    }
}
```

Copying the field into a local variable before the null check avoids a race, because a dynamic reference can disappear between two reads.

## Choosing Between Implementations

If two components implement the same interface, OSGi needs a way to pick one. You have two options:

**Service ranking.** The highest `service.ranking` wins:

```java
@Component(service = PriceService.class, property = {"service.ranking:Integer=100"})
```

**A target filter.** Select by a service property:

```java
@Component(service = PriceService.class, property = {"channel=web"})
public class WebPriceService implements PriceService { ... }

@Reference(target = "(channel=web)")
private PriceService priceService;
```

The Advanced series covers multiple implementations and factory configurations in more depth.

## Designing Good Services

- **Make services stateless** where possible. They are shared by many requests at once
- **Keep each service focused.** One responsibility is easier to test and replace
- **Don't use** `new` **on a service.** A hand-created object isn't wired, so its `@Reference` fields are `null`
- **Don't expose AEM internals** in your interface. Return plain types and your own classes
- **Don't implement interfaces that AEM provides for you to *call*.** Many product interfaces are meant to be consumed, not implemented. The Cloud build checks API usage against the SDK, so a misuse shows up as a build failure rather than a runtime surprise

## Diagnosing Wiring Problems

When something isn't injected, the cause is almost always one of these:

| Symptom | Likely cause | Where to look |
| --- | --- | --- |
| Component never appears as active | A mandatory reference is unsatisfied | Components list: it names the missing reference |
| Field is `null` inside a normal class | The object was created with `new`, not by OSGi | Search for `new MyServiceImpl()` |
| `@OSGiService` field is `null` in a Sling Model | The service isn't active, or the interface is wrong | Check the service in the Services list |
| Wrong implementation used | Two services match and ranking decides | Check `service.ranking` and any `target` filter |
| Works after a restart only | A static reference changed and the component was restarted | Consider a dynamic optional reference |

On the local SDK, use the Felix console's **Components** and **Services** pages. On Cloud Service, the Developer Console shows the same information read-only.

## AEM as a Cloud Service vs 6.5

The annotations and the lifecycle are the same on both. A few practical differences:

- **Deploy time:** on Cloud Service a wiring mistake only shows after the pipeline deploys, so test on a Dev environment, not just locally
- **Annotations:** older 6.5 code often uses the Felix SCR annotations (`org.apache.felix.scr.annotations`). Newer code, and everything for Cloud Service, should use the OSGi DS annotations shown here. If you're migrating, the SCR annotations are among the first things to replace
- **Constructor injection:** newer DS versions support injecting references through the constructor, but field injection works everywhere. Check what your runtime supports before using the newer style

## What's Next

Services often need settings: an endpoint, a toggle, a timeout. The next post covers **OSGi configuration**: where the files live, how run modes work, and how to supply secrets safely on Cloud Service.

---

*Next in this series: \[OSGi Configuration: Files, Run Modes and Environment Variables\]*
