This page consolidates Heisenberg’s public surface: Maven artifacts, Java modules, full table of MP FT 4.1 annotations with the canonical composition order, and the MicroProfile Config keys recognised through Ravel.
Maven artifacts
groupId |
artifactId |
Role |
|---|---|---|
|
|
Stable public SPI: |
|
|
Pure Java 25 policy engines without CDI: |
|
|
Single CDI interceptor ( |
|
|
Modularised repackage of |
|
|
JMH benchmarks (vs SmallRye Fault Tolerance). Not for production. |
|
|
Usage examples, standalone or integrated. |
|
|
Official MP FT 4.1 TCK runner — in-reactor, gated by the |
All versions at 0.4.0-SNAPSHOT (latest release on Maven Central: 0.3.0).
Java modules
| Module | Contents |
|---|---|
|
|
|
Internal implementations: engines, |
|
CDI integration: |
io.vidocq.heisenberg.internal is invisible to application modules (qualified export to io.vidocq.heisenberg.cdi.vauban only). Any application class depending on it indicates a regression to fix.
On a class path NEW
heisenberg-cdi-vauban is an explicit bean archive. It ships a META-INF/beans.xml with bean-discovery-mode="annotated", so the interceptors, the state registries and the metrics recorders are discovered even by a container that does not scan implicit archives, such as Weld SE by default. Without it, @Retry, @Timeout and the other annotations were silently ignored there (BUG-004).
On a class path, provides clauses are ignored. HeisenbergExtension and HeisenbergAutoDiscovery are therefore also listed in META-INF/services.
Other CDI containers NEW
Heisenberg does not need Vauban. The jars run unchanged under another CDI container, on a class path, and two integration-test modules, grouped under heisenberg-it-other-containers, prove it on every build (heisenberg#25):
| Module | What it runs |
|---|---|
|
Weld SE 6.0 (CDI 4.1), class path, no Vauban, Ravel for MicroProfile Config: |
|
A WAR on Open Liberty 26.0.0.10, MicroProfile 7 distribution (CDI 4.0, Jakarta REST 3.1, MicroProfile Config 3.1), with Liberty’s |
Neither module is published. Three things make this work:
-
vauban-apiis a runtime dependency ofheisenberg-cdi-vauban, under any container. The Vauban build weaves aprotectedconstructor takingio.vidocq.vauban.api.ProxyLinkinto each normal-scoped bean (the state registries, the metrics recorders); a container that cannot load that type cannot load the bean class, and Weld drops the bean with an INFO message (WELD-000119), then fails the deployment on the interceptor’s unsatisfied injection points.vauban-apiholds API types only (13 KB): no Vauban code runs outside Vauban. Its Jakarta CDI dependencies are excluded, so the container’s own CDI API stays the only one. -
Names come from the class the application wrote — metric names, configuration keys and fallback methods are keyed by the bean class, and the interceptor sees the subclass the container generated:
FooIntercepted` under Vauban, `Foo$Proxy$__WeldSubclassunder Weld and Open Liberty,FooOwbInterceptProxy0` under OpenWebBeans. Heisenberg walks up past every class whose name holds `, which javac never produces. -
HeisenbergAutoDiscoverystays out of the way — it forwards to the first otherConfigProviderResolveron the path (Ravel under Weld); Open Liberty’s mpConfig feature installs its own resolver, which wins.
|
Deploying on an application server
|
MicroProfile Fault Tolerance 4.1 annotations
Summary table of the six annotations, in wrapping order from the outside in.
| Annotation | Level | Effect | Status |
|---|---|---|---|
|
Method |
Runs the method on a virtual thread. Return type: |
✅ |
|
Method |
Provides an alternative result on failure. Parameters: |
✅ |
|
Method or class |
Bounded retries with delay, jitter, |
✅ |
|
Method or class |
CLOSED/OPEN/HALF_OPEN breaker over a sliding window. |
✅ |
|
Method or class |
Bounds an attempt’s duration — virtual thread + |
✅ |
|
Method or class |
Isolation via semaphore (sync) or semaphore-bounded waiting queue (async). |
✅ |
Detailed parameters
@Retry
| Parameter | Default | Effect |
|---|---|---|
|
3 |
Maximum number of retries after the first attempt. -1 = unbounded (use with |
|
0 |
Base delay between attempts. |
|
|
|
|
180000 |
Total maximum duration (across all attempts). |
|
|
|
|
200 |
Random variation added to |
|
|
|
|
|
Exceptions that trigger a retry. |
|
None |
Exceptions that abort immediately (take precedence over |
@Timeout
Parameter |
Default |
Effect |
|
1000 |
Maximum duration of an attempt. |
|
|
|
Beyond that, TimeoutException is raised. For @Asynchronous, the underlying virtual thread is interrupted (best-effort).
@CircuitBreaker
Parameter |
Default |
Effect |
|
20 |
Sliding window size. |
|
0.5 |
Opening threshold (failure proportion). |
|
5000 |
OPEN state duration before transition to HALF_OPEN. |
|
|
|
|
1 |
Number of consecutive OK probes to re-close. |
|
|
Exceptions counted as failure. |
|
None |
Exceptions not counted (take precedence over |
@Bulkhead
Parameter |
Default |
Effect |
|
10 |
Maximum concurrency (semaphore permits). |
|
10 |
Waiting queue size ( |
@Fallback
Parameter |
Default |
Effect |
|
|
|
|
|
Name of a private/package-local method on the class. |
|
|
Exceptions that trigger the fallback. |
|
None |
Exceptions that skip the fallback. |
Note: value and fallbackMethod are mutually exclusive.
NEW On a strict module path, a fallbackMethod of any access modifier is reached with no opens when the bean’s package has a generated _VaubanComponents; otherwise opens <pkg> to io.vidocq.heisenberg.core is enough. See Fallback methods on the module path.
@Asynchronous
No parameter. The method must return CompletionStage<T> or Future<T>.
NEW The method runs on its own virtual thread with the CDI request context active, as MicroProfile Fault Tolerance requires: a @RequestScoped bean used there lives for that call. Heisenberg activates it through the standard RequestContextController when it is not already active, and deactivates it after, on any CDI container.
MicroProfile Config keys
All keys are read through ConfigProvider.getConfig(). Precedence: method > class > global > annotation default (spec §12).
| Key | Effect |
|---|---|
|
Global value. |
|
Class override. |
|
Method override — highest precedence. |
|
Boolean. |
|
Boolean. |
|
Interceptor priority ( |
Examples with their parameters:
# Global
Retry/maxRetries=5
Timeout/value=2000
CircuitBreaker/failureRatio=0.6
# Per class
io.example.CatalogClient/Retry/maxRetries=2
# Per method (highest precedence)
io.example.CatalogClient/fetch/Retry/maxRetries=1
io.example.CatalogClient/fetch/Retry/delay=500
io.example.CatalogClient/fetch/CircuitBreaker/delay=30000
io.example.CatalogClient/fetch/Bulkhead/value=20
Exceptions
| Exception | Case |
|---|---|
|
|
|
Call rejected in OPEN state. |
|
Bulkhead saturated. |
|
Generic runtime error. |
|
Invalid annotation detected at startup ( |
Vidocq SPI exposed by heisenberg-api
The public surface of heisenberg-api is deliberately small — two types, plus the re-exported spec annotations.
| Type | Role |
|---|---|
|
Metrics recorder SPI, implementable by a third-party module. Provided implementations (in |
|
Base Heisenberg exception for configuration errors detected at container startup — distinct from the spec-defined runtime exceptions above. |
The shared state registries (CircuitBreakerStateRegistry, BulkheadStateRegistry), the policy engines and the policy configuration records are internal (io.vidocq.heisenberg.internal, exported only to the CDI module) — they are not extension points.
Compatibility
-
Java 25 (LTS), Maven 3.9.16.
-
CDI 4.1 Lite (Vauban) or any CDI 4.0 Lite-compatible container — proven on Weld and Open Liberty, see Other CDI containers NEW.
-
MicroProfile Config 3.1 (Ravel) in
provided. -
OpenTelemetry 1.39 (Humboldt) in
requires static— optional. -
MicroProfile Metrics 5.x (Dirac) — optional.