Code generation application
View MarkdownStatus: Stable Package:
dev.vertique.codegen.applicationArtifact:vertique-codegen-applicationDepends on:vertique-codegen-core(compile),vertique-application(processor classpath only) API reference: javadoc.io
vertique-codegen-application is an annotation processor that generates an application’s
VertiqueComponentFactory implementation and its META-INF/services SPI registration from a
single @VertiqueApp-annotated Dagger @Component interface. A standalone application using
this processor writes neither the factory class nor the SPI file.
This module has no runtime classpath presence. It is consumed exclusively via
<annotationProcessorPaths> and produces only source and resource files that are compiled into
the application’s own artifact.
When To Use It
Section titled “When To Use It”This processor owns application-factory generation when:
- The application uses the standalone launcher (
vertique-launcher) and wants to eliminate the hand-writtenVertiqueComponentFactoryandMETA-INF/servicesentry. - The
@Componentinterface already extendsVertiqueApplicationComponentand includesVertxModuledirectly or transitively through a component module’s@Module(includes = …)graph — for example, a component namingdev.vertique.starter.core.CoreApplicationModulesatisfies this becauseCoreApplicationModuleincludesVertxModule.
Applications that manage their own factory (the manual Phase-3 path) do not need this module.
Both paths satisfy the same VertiqueComponentFactoryLoader discovery contract, so the choice is
additive: switching to @VertiqueApp requires removing the hand-written factory and SPI entry.
Core Concepts
Section titled “Core Concepts”Zero-boilerplate standalone entry
Section titled “Zero-boilerplate standalone entry”The standard standalone entry sequence (described in dev.vertique:vertique-launcher) requires one
VertiqueComponentFactory registered in META-INF/services/dev.vertique.core.VertiqueComponentFactory.
Before this module existed, every application wrote:
// Hand-written factory (no longer needed with @VertiqueApp)public final class AppComponentVertiqueComponentFactory implements VertiqueComponentFactory<AppComponent> { @Override public AppComponent build(VertiqueRuntime runtime) { return DaggerAppComponent.builder() .vertxModule(new VertxModule(runtime.vertx(), runtime.config())) .build(); }}and a corresponding META-INF/services/dev.vertique.core.VertiqueComponentFactory file.
With @VertiqueApp, the processor generates both artifacts from the @Component interface itself:
@VertiqueApp@Singleton@Component(modules = { VertxModule.class, CoreLifecycleStepsModule.class, AppModule.class })interface AppComponent extends VertiqueApplicationComponent { // framework methods inherited from VertiqueApplicationComponent}The processor emits AppComponentVertiqueComponentFactory (same name pattern as the hand-written
form) and the SPI file. The generated factory’s .vertxModule(…) call references
DaggerAppComponent by name — the class Dagger generates in the same compilation — so the
reference is type-checked at the final javac compile with no reflection.
Multi-round safety
Section titled “Multi-round safety”VertiqueAppProcessor returns false from process(), ensuring Dagger’s own processor
continues to see the @Component element and emits DaggerAppComponent. The by-name reference in
the generated factory then resolves at the final compile, after both processors have written their
output.
Key Classes
Section titled “Key Classes”VertiqueApp (in vertique-application)
Section titled “VertiqueApp (in vertique-application)”Source-retained annotation that marks the application’s Dagger @Component interface.
@Retention(RetentionPolicy.SOURCE)@Target(ElementType.TYPE)public @interface VertiqueApp {}Defined in vertique-application (package dev.vertique.application), alongside
VertiqueApplicationComponent. No elements.
Invariants & Gotchas
Section titled “Invariants & Gotchas”- Exactly one
@VertiqueAppper compilation is allowed. The processor emits a compile error on each annotated element when more than one is present. - The annotation is
SOURCE-retained and absent at runtime; it carries no overhead in the deployed artifact.
VertiqueAppProcessor
Section titled “VertiqueAppProcessor”AbstractProcessor registered via
META-INF/services/javax.annotation.processing.Processor. Initialized with a CodegenContext
and a ComponentFactoryEmitter in init().
process(annotations, roundEnv) collects every @VertiqueApp-annotated TypeElement, enforces
the four validations (below), and delegates to ComponentFactoryEmitter.emit(component) for the
one valid survivor. Always returns false.
Validations — on any violation a Messager error is emitted on the offending element and no
output is generated:
| # | Rule | Error message |
|---|---|---|
| 1 | Target must be an interface | @VertiqueApp must be placed on an interface … |
| 2 | Target must be annotated with dagger.Component |
@VertiqueApp … must be annotated with @dagger.Component |
| 3 | Interface hierarchy must include VertiqueApplicationComponent (transitively) |
@VertiqueApp … must extend dev.vertique.application.VertiqueApplicationComponent |
| 4 | Exactly one @VertiqueApp per compilation |
exactly one @VertiqueApp per application; found also … |
Validation 1 is checked first; a class or enum annotated with @VertiqueApp gets a clear error
without running the subsequent checks. Validation 4 is checked before validations 1–3, so when
multiple annotated elements are present, all of them get the “exactly one” error and no output is
produced regardless of whether they are individually valid.
ComponentFactoryEmitter
Section titled “ComponentFactoryEmitter”JavaPoet emitter invoked by VertiqueAppProcessor for a validated @VertiqueApp component.
For a component AppComponent in package com.example, emits:
-
com.example.AppComponentVertiqueComponentFactory—public final classimplementingVertiqueComponentFactory<AppComponent>:public final class AppComponentVertiqueComponentFactoryimplements VertiqueComponentFactory<AppComponent> {@Overridepublic AppComponent build(VertiqueRuntime runtime) {return DaggerAppComponent.builder().vertxModule(new VertxModule(runtime.vertx(), runtime.config())).build();}} -
META-INF/services/dev.vertique.core.VertiqueComponentFactory— one line containingcom.example.AppComponentVertiqueComponentFactory.
On an IOException writing either artifact, a compiler error is emitted via Diagnostics and
the method returns without throwing.
Invariants & Gotchas
Section titled “Invariants & Gotchas”DaggerAppComponentis referenced by name ("Dagger" + componentSimpleName), never loaded. If Dagger is not on the processor classpath or@Componentis malformed, the reference will fail at the final compile with a standard “cannot find symbol” error pointing at the generated factory.- The
VertxModuleinclusion requirement is not validated by the processor.VertxModulemay be included directly in the component’smodules = {...}list or transitively through a component module’s@Module(includes = …)graph — for example, a component namingdev.vertique.starter.core.CoreApplicationModulesatisfies this becauseCoreApplicationModuleincludesVertxModule. A component whose complete module graph omitsVertxModulecompiles without error at the processor stage but fails at the final compile with a “cannot find symbol: method vertxModule” error on the generated factory’s builder chain. This is by design: the generated-code error is more precise than a processor-level error.
Generated Output
Section titled “Generated Output”For a component com.example.AppComponent, the processor emits two artifacts:
Source: com.example.AppComponentVertiqueComponentFactory
Section titled “Source: com.example.AppComponentVertiqueComponentFactory”package com.example;
import dev.vertique.core.VertiqueComponentFactory;import dev.vertique.core.VertiqueRuntime;import dev.vertique.core.VertxModule;
public final class AppComponentVertiqueComponentFactory implements VertiqueComponentFactory<AppComponent> { @Override public AppComponent build(VertiqueRuntime runtime) { return DaggerAppComponent.builder() .vertxModule(new VertxModule(runtime.vertx(), runtime.config())) .build(); }}Resource: META-INF/services/dev.vertique.core.VertiqueComponentFactory
Section titled “Resource: META-INF/services/dev.vertique.core.VertiqueComponentFactory”com.example.AppComponentVertiqueComponentFactoryThis is the exact resource VertiqueComponentFactoryLoader reads at standalone entry (see
dev.vertique:vertique-launcher).
Consumer Wiring
Section titled “Consumer Wiring”Applications inheriting vertique-app-parent declare vertique-application as a runtime
dependency and receive the complete processor facade automatically. Custom-parent applications
import vertique-bom and configure only the versionless Dagger and vertique-codegen-all
processor paths. Do not select processor leaves per feature. See docs/packaging.md for both
recipes, explicit Lombok opt-in, and the annotation-processing escape hatch.
Then annotate the @Component:
@VertiqueApp@Singleton@Component(modules = { VertxModule.class, CoreLifecycleStepsModule.class, AppModule.class // ... other modules})interface AppComponent extends VertiqueApplicationComponent { // no additional members required}No META-INF/services file and no factory class need to be written. A clean build is required
after removing @VertiqueApp from a component or deleting the component, because the processor
does not clean up previously generated files.
Dependencies
Section titled “Dependencies”vertique-codegen-core—CodegenContext,TypeResolver,Diagnostics; APT helper library shared across the codegen family.vertique-application—VertiqueAppannotation andVertiqueApplicationComponent(referenced by FQN in the processor; on the processor classpath so the types resolve during annotation processing). Not on the application runtime classpath.
The module has no dependency on vertique-launcher, vertique-core, or any runtime module.