Bootstrap
View MarkdownStatus: Stable Package:
dev.vertique.bootstrapArtifact:vertique-bootstrapDepends on:vertique-core,io.vertx:vertx-core,org.slf4j:slf4j-apiAPI reference: javadoc.io
Pre-Vertx bootstrap kernel. Provides the VertxBuilderContributor ServiceLoader SPI and the
machinery to discover, order, and drive it before a Vertx instance exists. Any framework module
that needs to customise Vertx.builder() — metrics factories, tracers, custom cluster managers —
implements this SPI without depending on vertique-launcher or any other application-entrypoint
module.
This module is the correct dependency for bridge adapters (e.g. Spring Boot or Quarkus integration modules) that must participate in pre-Vertx wiring. It carries no standalone launcher, no Vert.x application entrypoint, and no config-resolution machinery.
When To Use It
Section titled “When To Use It”Add vertique-bootstrap to a module that needs to hook into Vertx.builder() before the Vertx
instance is created. The module ships a VertxBuilderContributor implementation and registers it
via META-INF/services/. No application code changes are required — adding the dependency to the
classpath is the full integration.
vertique-launcher already depends on this module. Applications using VertiqueApplication as
their entry point receive the contribution chain automatically; the bootstrap module itself does not
need to be listed as an application-level dependency.
Core Concepts
Section titled “Core Concepts”The ServiceLoader seam
Section titled “The ServiceLoader seam”The Vertique framework is otherwise Dagger-first: every component in the dependency graph requires a
live Vertx instance. VertxBuilderContributor is the deliberate exception — it runs before the
Dagger graph exists and is therefore discovered via java.util.ServiceLoader rather than Dagger
multibinding.
Contribution chain
Section titled “Contribution chain”ContributorRunner loads contributors from the classpath, sorts them by OrderedExtension
ordering (phase → priority → orderKey), and threads each contributor’s returned VertxBuilder
into the next contributor. The chain is single-use: a given ContributorRunner instance drives
contributeAll exactly once. Calling it a second time throws IllegalStateException.
Contributors receive:
BootstrapContext.config()— a defensive copy of the bootstrap configuration, so contributors cannot mutate each other’s view.BootstrapContext.vertxOptions()— the live, sharedVertxOptionsinstance. Mutations made by one contributor are immediately visible to subsequent contributors and take effect when theVertxinstance is built.
Shutdown hooks
Section titled “Shutdown hooks”After a successful contribution, each contributor’s onShutdown() is called in reverse
contribution order when the application stops. Only contributors that successfully completed their
contribute call receive an onShutdown callback — a contributor that threw or returned null is
excluded. runShutdownHooks() is idempotent; the second and subsequent calls are no-ops.
Key Classes
Section titled “Key Classes”VertxBuilderContributor
Section titled “VertxBuilderContributor”The SPI interface. Implementations extend OrderedExtension (from vertique-core) and are
discovered via ServiceLoader. The two methods:
| Method | Contract |
|---|---|
contribute(VertxBuilder, BootstrapContext) |
Must return a non-null VertxBuilder. May return the received builder or a re-wrapped one. May mutate context.vertxOptions() as a side effect. Throwing any exception aborts startup. |
onShutdown() |
Default no-op. Called once in reverse contribution order on shutdown. Failures are logged and never rethrown. |
BootstrapContext
Section titled “BootstrapContext”Read-only view of bootstrap state shared across the contribution chain.
| Method | Contract |
|---|---|
config() |
Returns a defensive copy of the bootstrap config each time it is called. Mutations do not affect other contributors or the stored tree. Never null. |
vertxOptions() |
Returns the live, shared VertxOptions instance. Mutations are immediately visible to subsequent contributors. |
The asymmetry is intentional: config is read-only input (defensive copies prevent aliasing);
VertxOptions is a mutable accumulator that contributors cooperate on.
ContributorRunner
Section titled “ContributorRunner”Discovers, sorts, and drives the contributor chain.
| Method | Behaviour |
|---|---|
ContributorRunner.discover() |
Static factory: loads contributors via ServiceLoader, sorts them with OrderedExtension.comparator(), and returns a runner over the immutable sorted list. |
ContributorRunner(List<VertxBuilderContributor>) |
Constructor seam: sorts a defensive copy of the provided list. Use this in tests to bypass ServiceLoader. |
contributeAll(VertxBuilder, BootstrapContext) |
Single-use: drives the chain and returns the final VertxBuilder. Throws IllegalStateException on a second call. Throws ContributorFailureException on any contributor failure. |
runShutdownHooks() |
Idempotent: runs onShutdown() on the contributed prefix in reverse order. Second and subsequent calls are no-ops. |
Invariants & Gotchas
Section titled “Invariants & Gotchas”contributeAllis guarded by anAtomicBoolean; the first call runs the chain, any subsequent call throwsIllegalStateExceptionwithout invoking any contributor.runShutdownHooksis guarded independently by a secondAtomicBoolean; both guards are independent and both must fire on the shutdown path.- A contributor that throws is wrapped in a
ContributorFailureExceptionwhose message includes the contributor’s FQCN; the original exception is preserved as the cause. - A contributor that returns
nullfromcontributetriggers aContributorFailureExceptionwhose message states “returned null builder” — the chain aborts immediately. contributedCounttracks how many contributors completed successfully;runShutdownHooksconfines its reverse walk to that prefix only.
ContributorFailureException
Section titled “ContributorFailureException”Extends ConfigurationException (from vertique-core). Thrown by contributeAll when a
contributor throws an exception (cause is set) or returns a null builder (no cause). The message
always contains the contributor’s fully-qualified class name.
Framework seams
Section titled “Framework seams”DefaultBootstrapContext and ContributorRunner are the framework implementations behind
BootstrapContext and the contributor lifecycle, and their Javadoc marks them INTERNAL. They are
outside this module’s compatibility promise: an application implements VertxBuilderContributor
and receives a BootstrapContext, never constructing either.
Extension Points
Section titled “Extension Points”VertxBuilderContributor (ServiceLoader SPI)
Section titled “VertxBuilderContributor (ServiceLoader SPI)”Register an implementation by creating a service descriptor file on the classpath:
META-INF/services/dev.vertique.bootstrap.VertxBuilderContributorcontaining the fully-qualified class name of your implementation.
Ordering: contributors are sorted by OrderedExtension.comparator() — ascending phase()
(default APPLICATION), then ascending priority() (default 0), then ascending orderKey()
(default the implementation class name) as a stable tie-break.
Example — enabling Micrometer metrics before Vertx is created:
package com.example.telemetry;
import dev.vertique.bootstrap.BootstrapContext;import dev.vertique.bootstrap.VertxBuilderContributor;import io.micrometer.core.instrument.MeterRegistry;import io.vertx.core.VertxBuilder;import io.vertx.micrometer.MicrometerMetricsOptions;import io.vertx.micrometer.backends.BackendRegistries;
public class MetricsContributor implements VertxBuilderContributor {
@Override public VertxBuilder contribute(VertxBuilder builder, BootstrapContext context) { MicrometerMetricsOptions options = new MicrometerMetricsOptions() .setEnabled(true) .setRegistryName("default"); return builder.withMetrics(options); }
@Override public void onShutdown() { MeterRegistry registry = BackendRegistries.getDefaultNow(); if (registry != null) { registry.close(); } }}src/main/resources/META-INF/services/dev.vertique.bootstrap.VertxBuilderContributor:
com.example.telemetry.MetricsContributorPackaging note: @VertiqueApp applications package via Jib, which operates on the unpacked
classpath — each JAR’s META-INF/services/ file remains separate and ServiceLoader finds them all.
No ServicesResourceTransformer is needed. Any custom packaging that bundles JARs into a fat-jar
via the Maven Shade Plugin must include ServicesResourceTransformer to merge META-INF/services/
entries; without it, only one contributor registration survives the shading step:
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>See docs/packaging.md for Jib configuration.
Test seam: use new ContributorRunner(List<VertxBuilderContributor>) in unit tests to wire
contributors directly, bypassing ServiceLoader. ServiceLoader registrations are classpath-global
within a module’s test run, so the constructor seam avoids polluting parallel test runs.
Dependencies
Section titled “Dependencies”vertique-core—OrderedExtensionandExtensionPhase(the ordering contract used to sort contributors);ConfigurationException(the supertype ofContributorFailureException).io.vertx:vertx-core—VertxBuilder,VertxOptions.org.slf4j:slf4j-api— shutdown hook failure logging (contributor class names only; config contents are never logged).
This module must never depend on Dagger, vertique-launcher, vertique-config-core, or any module
that assumes a live Vertx instance is available.