Skip to content

Commit 523a974

Browse files
committed
Refactoring
1 parent 288f118 commit 523a974

File tree

118 files changed

+458
-394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+458
-394
lines changed

architecture/lifecycle/Overview.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
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 |

modules/packed-incubator/packed-incubator-cli/src/main/java/app/packed/cli/other/Spawn.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import app.packed.container.ContainerConfiguration;
2424
import app.packed.container.Wirelet;
2525
import app.packed.operation.Op;
26-
import sandbox.lifetime.old.ApplicationConfiguration;
26+
import sandbox.lifetime2.old.ApplicationConfiguration;
2727

2828
/**
2929
*

modules/packed-incubator/packed-incubator-concurrent/src/main/java/app/packed/concurrent/DaemonJobContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import app.packed.binding.Key;
2222
import app.packed.context.Context;
2323
import app.packed.extension.BaseExtension;
24-
import internal.app.packed.bean.scanning.IntrospectorOnAutoService;
24+
import internal.app.packed.bean.introspection.IntrospectorOnAutoService;
2525
import internal.app.packed.concurrent.daemon.DaemonJobSidehandle;
2626
import internal.app.packed.extension.base.BaseExtensionBeanIntrospector;
2727

modules/packed/src/main/java/app/packed/application/ApplicationContext.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ public interface ApplicationContext extends Context<BaseExtension> {
8181
/** {@return the desired state of the application.} */
8282
RunState desiredState();
8383

84-
/**
85-
* @return
86-
*/
84+
/** {@return whether or not the application is managed} */
8785
boolean isManaged();
8886

8987
/**

modules/packed/src/main/java/app/packed/application/ApplicationInstaller.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@
2727
*/
2828
public sealed interface ApplicationInstaller<H extends ApplicationHandle<?, ?>> permits PackedApplicationInstaller {
2929

30-
default ApplicationInstaller<H> expectsResult(Class<?> resultType) {
31-
throw new UnsupportedOperationException();
32-
}
33-
3430
/**
3531
* Add the specified tags to the application.
3632
*
@@ -45,6 +41,10 @@ default ApplicationInstaller<H> expectsResult(Class<?> resultType) {
4541
*/
4642
ApplicationInstaller<H> componentTag(String... tags);
4743

44+
default ApplicationInstaller<H> expectsResult(Class<?> resultType) {
45+
throw new UnsupportedOperationException();
46+
}
47+
4848
/**
4949
* Installs the new application represented by the specified assembly.
5050
* <p>
@@ -58,7 +58,13 @@ default ApplicationInstaller<H> expectsResult(Class<?> resultType) {
5858
*/
5959
H install(Assembly assembly, Wirelet... wirelets);
6060

61-
// Yes naming, my friend
61+
/**
62+
* Sets the name of the application
63+
*
64+
* @param name
65+
* the name of the application
66+
* @return this installer
67+
*/
6268
ApplicationInstaller<H> named(String name);
6369

6470
/**

modules/packed/src/main/java/app/packed/application/ApplicationMirror.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919
import app.packed.container.ContainerMirror;
2020
import app.packed.extension.Extension;
2121
import app.packed.extension.ExtensionMirror;
22-
import app.packed.lifetime.CompositeLifetimeMirror;
2322
import app.packed.namespace.NamespaceHandle;
2423
import app.packed.namespace.NamespaceMirror;
2524
import app.packed.operation.OperationMirror;
2625
import app.packed.service.ServiceContract;
2726
import app.packed.util.TreeView;
2827
import internal.app.packed.bean.BeanSetup;
29-
import internal.app.packed.bean.scanning.IntrospectorOnAutoService;
28+
import internal.app.packed.bean.introspection.IntrospectorOnAutoService;
3029
import internal.app.packed.container.ContainerSetup;
3130
import internal.app.packed.extension.base.BaseExtensionBeanIntrospector;
3231
import internal.app.packed.operation.OperationSetup;
@@ -130,11 +129,11 @@ public ContainerMirror container() {
130129
public TreeView<ContainerMirror> containers() {
131130
return new PackedTreeView<>(handle.application.container(), null, c -> c.mirror());
132131
}
133-
134-
/** {@return a collection of all entry points the application may have.} */
135-
public Stream<OperationMirror> entryPoints() {
136-
return container().lifetime().entryPoints();
137-
}
132+
//
133+
// /** {@return a collection of all entry points the application may have.} */
134+
// public Stream<OperationMirror> entryPoints() {
135+
// return container().lifetime().entryPoints();
136+
// }
138137

139138
/** {@inheritDoc} */
140139
@Override
@@ -163,11 +162,11 @@ public final int hashCode() {
163162
public boolean isExtensionUsed(Class<? extends Extension<?>> extensionClass) {
164163
return container().isExtensionUsed(extensionClass);
165164
}
166-
167-
/** {@return the application's lifetime. Which is identical to the root container's.} */
168-
public CompositeLifetimeMirror lifetime() {
169-
return container().lifetime();
170-
}
165+
//
166+
// /** {@return the application's lifetime. Which is identical to the root container's.} */
167+
// public CompositeLifetimeMirror lifetime() {
168+
// return container().lifetime();
169+
// }
171170

172171
/**
173172
* Returns the name of the application.

modules/packed/src/main/java/app/packed/lifetime/Main.java renamed to modules/packed/src/main/java/app/packed/application/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package app.packed.lifetime;
16+
package app.packed.application;
1717

1818
import java.lang.annotation.Annotation;
1919
import java.lang.annotation.Documented;

modules/packed/src/main/java/app/packed/lifetime/MainOperationConfiguration.java renamed to modules/packed/src/main/java/app/packed/application/MainOperationConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package app.packed.lifetime;
16+
package app.packed.application;
1717

1818
import app.packed.operation.OperationConfiguration;
1919
import app.packed.operation.OperationHandle;

0 commit comments

Comments
 (0)