|
| 1 | +# Lifecycle Overview |
| 2 | + |
| 3 | +This document describes how the lifecycle system works in Packed at both the application level and bean level. |
| 4 | + |
| 5 | +## RunState |
| 6 | + |
| 7 | +The `RunState` enum defines the possible states for managed entities (applications, containers, beans): |
| 8 | + |
| 9 | +``` |
| 10 | +UNINITIALIZED ──► INITIALIZING ──► INITIALIZED ──► STARTING ──► RUNNING ──► STOPPING ──► TERMINATED |
| 11 | +``` |
| 12 | + |
| 13 | +**Steady States** (require external trigger to transition): |
| 14 | +- `UNINITIALIZED` - Initial state before any lifecycle operations |
| 15 | +- `INITIALIZED` - All initialization complete, ready to start |
| 16 | +- `RUNNING` - Application is running and serving requests |
| 17 | +- `TERMINATED` - Final state, no further transitions possible |
| 18 | + |
| 19 | +**Transitional States** (auto-transition when operations complete): |
| 20 | +- `INITIALIZING` - Executing initialization operations |
| 21 | +- `STARTING` - Executing start operations |
| 22 | +- `STOPPING` - Executing stop operations |
| 23 | + |
| 24 | +## Application Lifecycle |
| 25 | + |
| 26 | +### Launch Sequence |
| 27 | + |
| 28 | +1. **UNINITIALIZED → INITIALIZING** |
| 29 | + - Application starts in UNINITIALIZED |
| 30 | + - `launch(RunState desiredState)` triggers transition |
| 31 | + - Creates runtime pool for bean instances |
| 32 | + |
| 33 | +2. **INITIALIZING → INITIALIZED** |
| 34 | + - Executes all bean lifecycle operations in order: |
| 35 | + - FACTORY: Bean instantiation |
| 36 | + - INJECT: Dependency injection (@Inject fields/methods) |
| 37 | + - INITIALIZE: Custom initialization (@Initialize methods) |
| 38 | + - On failure: Transitions directly to TERMINATED (no stop methods run) |
| 39 | + - On success: Transitions to INITIALIZED |
| 40 | + |
| 41 | +3. **INITIALIZED → STARTING** |
| 42 | + - User calls `start()` or launches to RUNNING state |
| 43 | + - Executes all @Start methods |
| 44 | + - Supports forking via StructuredTaskScope |
| 45 | + |
| 46 | +4. **STARTING → RUNNING** |
| 47 | + - All start methods complete successfully |
| 48 | + - Application is ready for use |
| 49 | + |
| 50 | +5. **RUNNING → STOPPING** |
| 51 | + - User calls `stop()` or shutdown hook triggers |
| 52 | + - Executes all @Stop methods |
| 53 | + |
| 54 | +6. **STOPPING → TERMINATED** |
| 55 | + - All stop methods complete |
| 56 | + - Application reaches final state |
| 57 | + |
| 58 | +### State Management |
| 59 | + |
| 60 | +The `RegionalManagedLifetime` class manages state transitions: |
| 61 | + |
| 62 | +- Uses `ReentrantLock` for thread-safe state changes |
| 63 | +- `await(RunState)` blocks until desired state is reached |
| 64 | +- Multiple threads can call `start()` or `stop()` safely |
| 65 | +- Only one thread performs actual state transition |
| 66 | + |
| 67 | +## Bean Lifecycle |
| 68 | + |
| 69 | +Beans go through up to 5 lifecycle phases, controlled by annotations: |
| 70 | + |
| 71 | +### 1. Factory (Construction) |
| 72 | + |
| 73 | +- Bean instance is created |
| 74 | +- Runs during INITIALIZING state |
| 75 | +- Always runs before dependants are constructed |
| 76 | + |
| 77 | +### 2. Inject (@Inject) |
| 78 | + |
| 79 | +- Dependencies injected into fields and methods |
| 80 | +- Runs during INITIALIZING state |
| 81 | +- Should only store dependencies, no business logic |
| 82 | + |
| 83 | +### 3. Initialize (@Initialize) |
| 84 | + |
| 85 | +- Custom initialization logic |
| 86 | +- Runs during INITIALIZING state |
| 87 | +- Controlled by `naturalOrder`: |
| 88 | + - `true` (default): Runs BEFORE dependant beans initialize |
| 89 | + - `false`: Runs AFTER all dependant beans initialize (coordinator pattern) |
| 90 | + |
| 91 | +### 4. Start (@Start) |
| 92 | + |
| 93 | +- Prepare bean for use (e.g., load data, open connections) |
| 94 | +- Runs during STARTING state |
| 95 | +- Supports: |
| 96 | + - `fork=true`: Execute in separate virtual thread |
| 97 | + - `naturalOrder=true` (default): Run before dependants start |
| 98 | + - `naturalOrder=false`: Run after dependants start |
| 99 | + |
| 100 | +### 5. Stop (@Stop) |
| 101 | + |
| 102 | +- Cleanup and shutdown |
| 103 | +- Runs during STOPPING state |
| 104 | +- Supports: |
| 105 | + - `fork=true`: Execute in separate virtual thread |
| 106 | + - `naturalOrder=true` (default): Run AFTER dependants stop |
| 107 | + - `naturalOrder=false`: Run BEFORE dependants stop (pre-notification) |
| 108 | + |
| 109 | +## Dependency Ordering |
| 110 | + |
| 111 | +The framework maintains a dependency graph between beans. If Bean A depends on Bean B: |
| 112 | + |
| 113 | +| Phase | naturalOrder=true | naturalOrder=false | |
| 114 | +|-------|-------------------|-------------------| |
| 115 | +| Initialize | B first, then A | A first, then B | |
| 116 | +| Start | B first, then A | A first, then B | |
| 117 | +| Stop | A first, then B | B first, then A | |
| 118 | + |
| 119 | +This ensures: |
| 120 | +- Dependencies are ready before dependants during startup |
| 121 | +- Dependants release dependencies before cleanup during shutdown |
| 122 | + |
| 123 | +## Natural Order |
| 124 | + |
| 125 | +The `naturalOrder` attribute controls execution order relative to the dependency graph: |
| 126 | + |
| 127 | +```java |
| 128 | +// Default: This bean initializes BEFORE beans that depend on it |
| 129 | +@Initialize |
| 130 | +void init() { ... } |
| 131 | + |
| 132 | +// Coordinator pattern: This bean initializes AFTER beans that depend on it |
| 133 | +@Initialize(naturalOrder = false) |
| 134 | +void afterDependantsReady() { ... } |
| 135 | + |
| 136 | +// Default: This bean stops AFTER beans that depend on it have stopped |
| 137 | +@Stop |
| 138 | +void cleanup() { ... } |
| 139 | + |
| 140 | +// Pre-notification: This bean runs BEFORE dependants stop |
| 141 | +@Stop(naturalOrder = false) |
| 142 | +void notifyShutdown() { ... } |
| 143 | +``` |
| 144 | + |
| 145 | +The concept is borrowed from `Comparator.naturalOrder()` - the default, expected ordering for the operation type. |
| 146 | + |
| 147 | +## Forking with StructuredTaskScope |
| 148 | + |
| 149 | +The @Start and @Stop annotations support forking operations to run concurrently: |
| 150 | + |
| 151 | +```java |
| 152 | +@Start(fork = true) |
| 153 | +void loadDataAsync() { |
| 154 | + // Runs in a virtual thread |
| 155 | + // Application waits for completion before entering RUNNING |
| 156 | +} |
| 157 | + |
| 158 | +// Or manually fork multiple tasks: |
| 159 | +@Start |
| 160 | +void start(StartContext ctx) { |
| 161 | + ctx.fork(() -> loadUsers()); |
| 162 | + ctx.fork(() -> loadProducts()); |
| 163 | + // Both complete before RUNNING state |
| 164 | +} |
| 165 | +``` |
| 166 | + |
| 167 | +Implementation details: |
| 168 | +- Uses Java 21+ `StructuredTaskScope` with virtual threads |
| 169 | +- Joiner: `awaitAllSuccessfulOrThrow()` - all tasks must succeed |
| 170 | +- All forked tasks must complete before state transition |
| 171 | +- `interruptOnStopping=true` (default): Interrupt if stop requested during startup |
| 172 | + |
| 173 | +## LifecycleKind |
| 174 | + |
| 175 | +Beans have one of three lifecycle kinds: |
| 176 | + |
| 177 | +| Kind | Construction | Destruction | Start/Stop | |
| 178 | +|------|--------------|-------------|------------| |
| 179 | +| NONE | External (static beans) | N/A | Not supported | |
| 180 | +| UNMANAGED | By framework | Garbage collector | Not supported | |
| 181 | +| MANAGED | By framework | Explicit stop() | Fully supported | |
| 182 | + |
| 183 | +- **NONE**: Static beans with no instance lifecycle |
| 184 | +- **UNMANAGED**: Prototype-scoped beans, created on demand, not tracked after creation |
| 185 | +- **MANAGED**: Full lifecycle management, observable state, explicit cleanup |
| 186 | + |
| 187 | +## StopInfo |
| 188 | + |
| 189 | +When a bean or application stops, `StopInfo` provides the reason: |
| 190 | + |
| 191 | +```java |
| 192 | +@Stop |
| 193 | +void cleanup(StopContext ctx) { |
| 194 | + StopInfo info = ctx.stopInfo(); |
| 195 | + |
| 196 | + if (info.isFailed()) { |
| 197 | + Throwable cause = info.failure().get(); |
| 198 | + // Handle failure cleanup |
| 199 | + } |
| 200 | + |
| 201 | + if (info.isCancelled()) { |
| 202 | + // Shutdown was cancelled/interrupted |
| 203 | + } |
| 204 | + |
| 205 | + RunState stoppedFrom = info.stoppedFromState(); |
| 206 | + // RUNNING, STARTING, INITIALIZING, etc. |
| 207 | +} |
| 208 | +``` |
| 209 | + |
| 210 | +Predefined triggers: |
| 211 | +- `SHUTDOWN_HOOK` - JVM shutdown |
| 212 | +- `TIMEOUT` - Time limit exceeded |
| 213 | +- `NORMAL` - Normal completion |
| 214 | +- `FAILED_INTERNALLY` - Internal failure |
| 215 | + |
| 216 | +## Lifetime Concepts |
| 217 | + |
| 218 | +### ContainerLifetimeSetup |
| 219 | + |
| 220 | +Manages the lifetime of a container and its beans: |
| 221 | +- Holds list of beans in installation order |
| 222 | +- Maintains ordered operation lists for each phase |
| 223 | +- Creates runtime pool (`LifetimeStore`) for bean instances |
| 224 | + |
| 225 | +### BeanLifetimeSetup |
| 226 | + |
| 227 | +For beans with independent lifetimes (separate from container): |
| 228 | +- Single bean with its own lifecycle |
| 229 | +- Parent reference to container lifetime |
| 230 | + |
| 231 | +### LifetimeStore |
| 232 | + |
| 233 | +Runtime storage for bean instances: |
| 234 | +- Maps beans to indices for fast lookup |
| 235 | +- Created once per container lifetime |
| 236 | +- Passed to runners during lifecycle execution |
| 237 | + |
| 238 | +## Key Classes |
| 239 | + |
| 240 | +| Class | Purpose | |
| 241 | +|-------|---------| |
| 242 | +| `RunState` | State enum and utilities | |
| 243 | +| `RegionalManagedLifetime` | State machine and orchestration | |
| 244 | +| `ContainerRunner` | Coordinates start/stop execution | |
| 245 | +| `StartRunner` | Executes @Start methods | |
| 246 | +| `StopRunner` | Executes @Stop methods | |
| 247 | +| `ContainerLifetimeSetup` | Build-time lifetime configuration | |
| 248 | +| `LifetimeStore` | Runtime bean instance storage | |
| 249 | +| `LifecycleOperationHandle` | Operation handle hierarchy | |
0 commit comments