Vidocq Runtime borrows the extension idea popularised by Quarkus, but implements it with a deliberately simple, 100 % JDK mechanism: a ServiceLoader-discovered lifecycle interface on top of CDI Lite via Vauban and static code generation (Class-File API, JEP 484). This page defines the terms used throughout the documentation.

Extension

An extension is a Maven module that contributes to the runtime by implementing the io.vidocq.runtime.spi.VidocqExtension interface from vidocq-runtime-spi. It manifests as:

  • an implementation class registered with provides io.vidocq.runtime.spi.VidocqExtension with … in its module-info.java (or a META-INF/services entry on the class path);

  • a set of lifecycle hooks invoked by the runtime core in a fixed order;

  • optionally an APT codegen companion (vidocq-runtime-<x>-extension-codegen) that generates bean metadata at compile time instead of relying on runtime reflection.

Reflection-heavy work is pushed to compile time through APT and the Class-File API; at runtime the extension only wires already-generated artifacts into the lifecycle.

The VidocqExtension lifecycle

VidocqExtension declares seven methods. Two identify and order the extension:

  • name() — unique extension name (e.g. rest-cassini);

  • priority() — execution order, lower runs first (default 1000).

Four are lifecycle hooks, all with empty default implementations so an extension overrides only what it needs:

  1. configure(VidocqConfiguration) — called before CDI boot. The extension reads its configuration keys (config.property("vidocq.rest.context-path", "/"), config.portFor(name, defaultPort), …).

  2. beforeStart(VaubanContainerBuilder) — enrich the Vauban container builder before the container exists: add synthetic beans, packages or interceptors.

  3. onStart(ExtensionContext) — the CDI container is up. The extension resolves beans, mounts HTTP handlers, opens listeners.

  4. onStop() — server shutdown; release resources. Extensions are stopped in reverse priority order.

The seventh method, configKeys(), lets an extension declare the vidocq. keys (or prefix. patterns) it consumes, so the runtime’s ConfigKeyAudit can warn at startup about configured keys that no extension reads.

ExtensionContext

ExtensionContext is handed to onStart and gives the extension:

  • container() — the initialized Vauban VaubanContainer;

  • beanManager() — shortcut to the CDI BeanManager;

  • configuration() — the legacy VidocqConfiguration (string properties);

  • config() — the typed VidocqConfig API (Optional-returning getValue(key, type), aligned with MicroProfile Config concepts).

Discovery and ordering

There is no build-step graph and no bytecode recording: discovery is plain java.util.ServiceLoader. At boot, ExtensionLoader loads every VidocqExtension visible on the module path, sorts the list by ascending priority(), then VidocqBootstrap drives each phase across the sorted list — configure, then beforeStart, then container start, then onStart; onStop walks the list backwards.

Priorities express real ordering constraints. In the HTTP stack, for example, ChappeEngineExtension (priority 100) prepares the web server engine before contributing extensions such as CassiniExtension (priority 500) mount their handlers, and ChappeServerBootstrap (priority 10 000) opens the listeners at the very end of the chain.

Notable differences from Quarkus

Aspect Quarkus Vidocq Runtime

Extension model

Build-step graph + bytecode recorders

VidocqExtension lifecycle interface, ServiceLoader discovery

Runtime CDI

ArC (custom)

Vauban (CDI 4.1 Lite, TCK 774/774)

Bytecode generation

Gizmo (ASM)

JDK Class-File API (JEP 484)

HTTP transport

Vert.x

Chappe on virtual threads

JSON

Jackson

Champollion (JSON-B 3.0)

Persistence

Hibernate ORM/Reactive

Mansart (Jakarta Data 1.0 + JDBC)

Minimum JDK

17

25 (LTS)

Native Java Modules

No

Yes — module-info.java everywhere

Composition of the six bricks

Vidocq Runtime invents no runtime; it composes the six foundational bricks through the same lifecycle. The vidocq-runtime-cassini-rest-extension, for example:

  • reads vidocq.rest.context-path and vidocq.rest.listener in configure;

  • in onStart, resolves the JAX-RS Application bean from the Vauban container, then mounts the Cassini dispatcher (whose routing was generated statically at compile time) on a mount point of the Chappe server contributed by vidocq-runtime-chappe-webserver-extension;

  • relies on priority() so the Chappe engine exists before the mount and the listeners open after it.

This shared lifecycle grammar keeps the ecosystem homogeneous while every brick remains self-contained in its own repo.

Next steps

  • Internals — boot sequence step by step

  • SPI — how to write an extension

  • Reference — public SPI types and Maven goals