# OSGi in AEM: Bundles, Components and the Lifecycle

Every piece of Java you write for AEM runs inside **OSGi**. Sling Models, servlets, workflow steps, schedulers and your own services are all OSGi components living in bundles. If you understand how OSGi starts, wires and stops your code, many "it works locally but not in the cloud" mysteries become easy to diagnose. This post covers the basics and the lifecycle, with a focus on **AEM as a Cloud Service** and notes on where 6.5 differs.

## What OSGi Gives You

OSGi is a module system for Java. Instead of one big application, AEM is a collection of **bundles**, each a JAR file with a manifest that says what it **exports** (offers to others) and what it **imports** (needs from others).

- A **bundle** is the unit of deployment. Your `core` module builds one
- A **component** is a class managed by OSGi, created and destroyed by the framework
- A **service** is a component that other components can use through an interface (the next post covers this)

The archetype's `core` module uses the bnd tooling to generate the manifest for you, so you rarely edit it by hand.

## Bundle States

A bundle moves through these states:

| State | Meaning |
| --- | --- |
| **Installed** | Uploaded, but its dependencies aren't resolved yet |
| **Resolved** | All imports found, ready to start |
| **Starting** | Being activated |
| **Active** | Running, and its components can be created |

The state you'll meet most often in trouble is **Installed**. A bundle stuck there almost always has an **unresolved import**, for example a package your code uses that no active bundle exports at a matching version. The usual fixes are to depend only on APIs from the **AEM SDK API**, check version ranges, and avoid pulling in third-party libraries the runtime doesn't provide.

## Declarative Services Components

You write OSGi components with the annotations from `org.osgi.service.component.annotations`. A minimal service with a configuration looks like this:

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

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service = GreetingService.class)
@Designate(ocd = GreetingServiceImpl.Config.class)
public class GreetingServiceImpl implements GreetingService {

    private static final Logger LOG = LoggerFactory.getLogger(GreetingServiceImpl.class);

    @ObjectClassDefinition(name = "My Project - Greeting Service")
    public @interface Config {
        @AttributeDefinition(name = "Greeting")
        String greeting() default "Hello";
    }

    private volatile String greeting;

    @Activate
    protected void activate(Config config) {
        this.greeting = config.greeting();
        LOG.info("GreetingService activated with greeting '{}'", greeting);
    }

    @Modified
    protected void modified(Config config) {
        this.greeting = config.greeting();
        LOG.info("GreetingService configuration changed");
    }

    @Deactivate
    protected void deactivate() {
        LOG.info("GreetingService deactivated");
    }

    @Override
    public String greet(String name) {
        return greeting + ", " + name;
    }
}
```

The interface is a plain Java interface with one method, `String greet(String name)`.

## The Component Lifecycle

Bundles have states, and so do components. A component is created by the framework, and the three lifecycle methods are your hooks:

| Annotation | When it runs | Typical use |
| --- | --- | --- |
| `@Activate` | Once, when all required references and configuration are available | Read configuration, initialize caches, register with other systems |
| `@Modified` | When the component's configuration changes, without restarting it | Update fields from the new configuration |
| `@Deactivate` | When the component is stopped: bundle stop, config removed or a reference is lost | Release resources, close clients, cancel work |

A component is only **active** when it is **satisfied**: every mandatory reference is available and, if you required one, its configuration exists. Otherwise it stays **unsatisfied** and nothing is created. This is the single most common reason a service "doesn't work": it was never activated.

### Rules of Thumb for the Lifecycle

- **Keep** `activate` **fast.** Heavy work (remote calls, big queries) delays startup and can fail the whole component
- **Treat components as singletons shared by many threads.** Avoid mutable fields, or make them `volatile` or thread-safe
- **Undo in** `deactivate` **what you did in** `activate`**.** Close clients, stop timers, deregister listeners
- **Never do blocking work for a long time** in lifecycle methods
- **Log at activation** at INFO level. It makes it easy to see in the logs which configuration a component started with

## Seeing What's Running

You can inspect bundles, components and services at runtime.

| Environment | Tool |
| --- | --- |
| **Local SDK** | The Felix web console at `/system/console`: Bundles, Components, Configuration, Services |
| **AEM as a Cloud Service** | The **Developer Console** (Status section) for a read-only view of bundles, components, configurations and services, plus logs |

For a component that doesn't work, check in this order:

1. Is the **bundle Active**?
2. Is the **component listed**, and in which state? An "unsatisfied reference" or "missing configuration" message tells you exactly what's missing
3. Does the log show your `activate` message?

## AEM as a Cloud Service vs 6.5

| Topic | AEM 6.5 | AEM as a Cloud Service |
| --- | --- | --- |
| Installing bundles | Can upload and start or stop bundles in the web console | Deployed only through the **Cloud Manager pipeline**. No manual installs on Stage and Production |
| Changing a running component | Possible in the web console | Not possible. Change the code or configuration and redeploy |
| Inspecting at runtime | Full Felix web console | Read-only through the Developer Console |
| API surface | Full product jars | Use the **AEM SDK API**, which exposes only the supported, cloud-compatible APIs |
| Updates | You choose when to apply service packs | Continuous. Your bundle must stay compatible with the platform's updated APIs |

## Common Pitfalls

- **Bundle stays Installed.** Unresolved import. Read the message in the console, then fix the dependency
- **Component never activates.** A mandatory reference is missing, or a configuration required by `configurationPolicy = REQUIRE` hasn't been deployed for that run mode
- **Works locally, fails on Cloud.** The local SDK is a single instance with a forgiving setup. Always test on a Dev environment
- **Work in** `activate` **that fails.** An exception there leaves the component unsatisfied. Catch, log and fail clearly
- **Forgetting** `service = ...` **in** `@Component`**.** The component exists but isn't registered as a service, so nothing can inject it

## What's Next

You now know how components start and stop. The next post shows how components **find and use each other**: services, references and the common wiring errors.

---

*Next in this series: \[OSGi Services and References: Wiring Your Code Together\]*
