How Maven Plugins Are Defined in the API Layer: The Complete Guide

Maven plugins are defined in the API layer through the @Mojo annotation that declares goal metadata and the PluginDescriptor infrastructure that parses plugin.xml into runtime model objects.

In the Apache Maven codebase, plugin semantics are formally specified entirely within the API layer through two complementary mechanisms: annotations that developers use to declare goals, and descriptor classes that Maven's core uses to interpret those declarations at runtime. This architecture ensures that plugin metadata flows from source code annotations through build-time generation to runtime execution models.

The @Mojo Annotation API

The primary mechanism for defining Maven plugins in the API layer begins with the @Mojo annotation located in org.apache.maven.api.plugin.annotations.Mojo. This annotation transforms a standard Java class into a Maven goal by declaring its identity and execution requirements.

When developers extend org.apache.maven.api.plugin.AbstractMojo (or implement org.apache.maven.api.plugin.Mojo) and annotate their class with @Mojo, each attribute maps directly to corresponding fields in MojoDescriptor:

@Mojo attribute MojoDescriptor method
name setGoal()
defaultPhase setPhase()
projectRequired setProjectRequired()
aggregator setAggregator()
onlineRequired setOnlineRequired()
configurator setComponentConfigurator()
dependencyCollection setDependencyCollectionRequired()

During compilation, Maven Plugin Tools reads these annotations and generates a plugin.xml descriptor that mirrors the annotation values exactly, bridging the gap between compile-time metadata and runtime configuration.

Plugin Descriptor Infrastructure

At runtime, Maven loads plugin JARs and uses PluginDescriptorBuilder located in org.apache.maven.plugin.descriptor.PluginDescriptorBuilder to transform the XML descriptor into in-memory model objects. This builder serves as the factory for creating the runtime representation of plugin metadata.

The builder process follows three distinct steps:

  1. Namespace Detection: Identifies the XML namespace (PLUGIN_2_0_0) to determine the descriptor format version.
  2. Parsing Strategy: Uses the StAX-based PluginDescriptorStaxReader for the new format, or falls back to the legacy Plexus configuration path for older plugins.
  3. Model Population: Constructs a PluginDescriptor instance containing:
    • Basic coordinates (groupId, artifactId, version, goalPrefix)
    • A collection of MojoDescriptor objects created via buildComponentDescriptor()
    • Dependency information, lifecycle mappings, and metadata (description, required Maven/Java versions)

The PluginDescriptor class acts as the central API model that Maven's core (DefaultMavenPluginManager) uses to locate and execute goals. It provides helper methods such as getMojo(String goal) to retrieve specific MojoDescriptor instances by name.

From Annotation to Runtime Execution

Maven plugins are defined in the API layer through a complete metadata pipeline that transforms source annotations into executable components:


@Mojo-annotated class → Maven Plugin Tools → plugin.xml
                              ↓
                 PluginDescriptorBuilder parses plugin.xml
                              ↓
               PluginDescriptor (API model) stored in Maven core
                              ↓
                Maven resolves goal, creates Mojo instance,
                injects @Parameter fields, and executes

This flow demonstrates how the API layer serves as the single source of truth for plugin semantics. The annotation describes what a goal is, while the descriptor classes define how Maven should treat that goal during the build lifecycle.

Practical Implementation Examples

Minimal Mojo Definition

The following example demonstrates the simplest valid plugin definition using the API layer annotations:

package com.example.maven;

import org.apache.maven.api.plugin.AbstractMojo;
import org.apache.maven.api.plugin.annotations.Mojo;
import org.apache.maven.api.plugin.annotations.Parameter;

/**
 * Prints a greeting.
 */
@Mojo(name = "greet", defaultPhase = "process-resources")
public class GreetMojo extends AbstractMojo {

    @Parameter(property = "greet.message", defaultValue = "Hello, Maven!")
    private String message;

    @Override
    public void execute() {
        getLog().info(message);
    }
}

When executing mvn com.example.maven:greet, Maven's core performs these API layer operations:

  1. Loads the plugin JAR and locates plugin.xml
  2. Uses PluginDescriptorBuilder to parse the descriptor and create MojoDescriptor instances
  3. Finds the MojoDescriptor for the "greet" goal via getMojo("greet")
  4. Instantiates GreetMojo, injects the message parameter, and invokes execute()

Advanced Configuration Attributes

For complex build scenarios, the @Mojo annotation supports advanced execution constraints that map directly to MojoDescriptor flags:

@Mojo(
    name = "verify",
    defaultPhase = "verify",
    aggregator = true,
    onlineRequired = true,
    dependencyCollection = true,
    configurator = "my.custom.Configurator"
)
public class VerifyMojo extends AbstractMojo {
    // implementation details
}

This configuration triggers specific behaviors in Maven's core:

  • aggregator = true → Sets MojoDescriptor.setAggregator(true), receiving the entire multi-module project context
  • onlineRequired = true → Sets MojoDescriptor.setOnlineRequired(true), failing fast if Maven runs in offline mode
  • dependencyCollection = true → Sets MojoDescriptor.setDependencyCollectionRequired(), ensuring full dependency resolution before execution

Summary

Maven defines plugins in the API layer through a rigorous annotation-to-model architecture:

  • @Mojo annotations in org.apache.maven.api.plugin.annotations provide compile-time metadata that describes goal identity, default phases, and execution requirements
  • PluginDescriptorBuilder transforms plugin.xml (generated from annotations) into PluginDescriptor and MojoDescriptor runtime models
  • PluginDescriptor serves as the central API contract that Maven's core uses to locate goals, instantiate Mojos, and manage plugin lifecycle
  • All metadata flows from source annotations through XML descriptors to in-memory models, ensuring consistency across the build lifecycle

Frequently Asked Questions

What is the difference between @Mojo and MojoDescriptor?

The @Mojo annotation is a compile-time construct located in org.apache.maven.api.plugin.annotations.Mojo that developers use to annotate their Java classes. MojoDescriptor is a runtime model class in org.apache.maven.plugin.descriptor that represents the parsed metadata as a Java object. The PluginDescriptorBuilder transforms the annotation data into MojoDescriptor instances when Maven loads a plugin.

Where does Maven store the metadata from @Mojo annotations?

Maven stores the metadata in a file named plugin.xml inside the plugin's JAR file under META-INF/maven/. The Maven Plugin Tools processor generates this XML file during the plugin's build process by reading the @Mojo and @Parameter annotations. At runtime, the PluginDescriptorBuilder class parses this XML to create the PluginDescriptor object model.

How does AbstractMojo relate to the API layer definition?

org.apache.maven.api.plugin.AbstractMojo provides the base implementation that connects user-written plugins to Maven's execution engine. While the @Mojo annotation defines the plugin's metadata for the descriptor system, AbstractMojo provides the execute() contract and getLog() utility that the runtime uses after the PluginDescriptor identifies the correct goal class to instantiate.

Can a plugin work without the @Mojo annotation?

Technically, older plugins using the legacy Plexus container system could define metadata purely through plugin.xml descriptors without annotations. However, in modern Maven (4.x and current 3.x), the @Mojo annotation is the standard and recommended mechanism for defining Maven plugins in the API layer, as it provides type-safe metadata that the PluginDescriptorBuilder can reliably parse into the MojoDescriptor model.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →