skip to content

Writing a Plugin (Mojo)

Writing your own mojo with @Mojo and @Parameter, generating the plugin descriptor, and integration-testing it with the invoker plugin. Asked at senior level to see whether you extend the build or only configure it.

on this pageshow

explore

questions

5

What is a Mojo in Maven, and what are the minimum pieces you write to create a custom plugin goal?

level: middleimportance: must knowfreq 55%

answer

  1. Mojo = Maven plain Old Java Object
  2. extends AbstractMojo, implement execute()
  3. @Mojo(name) + @Parameter
  4. packaging maven-plugin
  5. ExecutionException vs FailureException

basics

~10 s

A Mojo is the Java class behind one Maven goal. You extend AbstractMojo, annotate the class with @Mojo(name = "..."), implement execute(), and package it as a maven-plugin artifact so Maven can run it.

solid answer

~40 s

Mojo = "Maven plain Old Java Object" — the implementation of a single goal. You write a class extending `AbstractMojo` (which gives you `getLog()` and forces you to implement `execute()`), annotate it `@Mojo(name = "greet")`, and expose configurable inputs as fields annotated `@Parameter`. The project that contains it must have `<packaging>maven-plugin</packaging>` and depend on `maven-plugin-api` plus `maven-plugin-annotations`. The `maven-plugin-plugin` scans those annotations at build time and generates `META-INF/maven/plugin.xml` (the plugin descriptor) so Maven knows which goals exist, their parameters, and lifecycle binding. `execute()` throws `MojoExecutionException` (unexpected/build-breaking error) or `MojoFailureException` (expected failure, e.g. a rule violation). Users then run it as `groupId:artifactId:version:goal` or via a short prefix.

code

java · 10 lines
java
@Mojo(name = "greet", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, threadSafe = true)
public class GreetMojo extends AbstractMojo {
    @Parameter(property = "greet.name", defaultValue = "world")
    private String name;

    @Override
    public void execute() throws MojoExecutionException {
        getLog().info("Hello, " + name + "!");
    }
}

go deeper

for a junior

Knows a Mojo is the Java class for a goal and that you extend AbstractMojo and implement execute().

for a middle

Wires the full plugin project: maven-plugin packaging, plugin-api/annotations deps, @Mojo/@Parameter, and the two exception types.

for a senior

Chooses sensible defaults (threadSafe, defaultPhase, requiresDependencyResolution) and uses the right exception for the right failure mode.

for a principal

Sets conventions for in-house plugins: naming, versioning, descriptor generation, testing strategy, and when a plugin vs. a script is the right tool.

## What a Mojo is **Mojo** stands for "**M**aven plain **O**ld **J**ava **O**bject". A Maven *plugin* is a JAR that bundles one or more Mojos; each Mojo implements exactly one **goal** (the unit of work you invoke, e.g. `compiler:compile`). So `org.apache.maven.plugins:maven-compiler-plugin` is the plugin, and `compile` / `testCompile` are goals, each backed by a Mojo class. ## The minimum pieces 1. **A plugin project** with `<packaging>maven-plugin</packaging>`. 2. **Dependencies**: `maven-plugin-api` (the `AbstractMojo` base class, exceptions, the `Log`) and `maven-plugin-annotations` (provided scope — only needed at compile/descriptor-generation time). 3. **The maven-plugin-plugin** configured in the build so it generates the descriptor. 4. **A Mojo class** extending `AbstractMojo`, annotated `@Mojo(name = "...")`, implementing `execute()`. ## The Mojo class - Extend `org.apache.maven.plugin.AbstractMojo` — it supplies `getLog()` (a `Log` for `info`/`warn`/`error`) and declares the abstract `execute()`. - `@Mojo(name = "greet")` names the goal. Common attributes: `defaultPhase` (auto-bind to a lifecycle phase), `requiresDependencyResolution`, `threadSafe = true`. - `@Parameter` on fields exposes configuration; `@Parameter(defaultValue = "${project}", readonly = true)` injects Maven objects like the `MavenProject`. ## Errors - `MojoExecutionException` — something went genuinely wrong (I/O error, bad state); fails the build with a stack trace. - `MojoFailureException` — an *expected* failure such as a checkstyle/enforcer violation; cleaner message, no "internal error" framing. ## Example ```java @Mojo(name = "greet", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, threadSafe = true) public class GreetMojo extends AbstractMojo { @Parameter(property = "greet.name", defaultValue = "world") private String name; @Override public void execute() throws MojoExecutionException { getLog().info("Hello, " + name + "!"); } } ``` Run it directly: `mvn com.example:greeting-maven-plugin:1.0.0:greet -Dgreet.name=Ada`. ## How Maven finds the goal At plugin build time, `maven-plugin-plugin`'s `descriptor` goal scans the annotations and writes `META-INF/maven/plugin.xml`. At consume time Maven reads that descriptor, maps the goal name to your class, injects the parameters from POM configuration / `-D` properties, and calls `execute()`.

  • What is the difference between MojoExecutionException and MojoFailureException?
    ExecutionException signals an unexpected internal error (I/O, bad state) and Maven shows it as a build error with stack trace; FailureException signals an expected, user-facing failure like a rule violation and is reported more cleanly. Both fail the build.
  • Why do you depend on maven-plugin-annotations in provided scope?
    The annotations are only read at compile time by the descriptor generator (APT/scanning); they aren't needed at runtime, so provided scope keeps them off the consumer's classpath.

A plugin is a toolbox JAR; each Mojo is one tool (goal) inside it. AbstractMojo is the standard handle every tool shares.

saying these in an interview costs you the question

  • Saying a Mojo must implement an interface other than extending AbstractMojo / implementing Mojo
  • Confusing plugin (the JAR) with goal (one Mojo)
  • Claiming you must hand-write plugin.xml
  • Thinking both exceptions mean the same thing

context

open as a page

How do you expose configuration to a Mojo, and how does a user set those values from the POM or command line?

level: middleimportance: must knowfreq 50%

basics

~10 s

You annotate fields with @Parameter. Users set them in the plugin's <configuration> block in the POM using the field name as the XML element, or via -D using the parameter's property name.

open as a page

How does Maven know what goals a plugin offers, and how do you bind a goal to a lifecycle phase?

level: juniorimportance: should knowfreq 30%

basics

~20 s

The plugin ships a descriptor file, META-INF/maven/plugin.xml, generated from your @Mojo/@Parameter annotations by the maven-plugin-plugin. To bind a goal to a phase, set defaultPhase on @Mojo or add an <execution> with a <phase> in the consumer POM.

open as a page

How do you write integration tests for a Maven plugin, and what role does the maven-invoker-plugin play?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Unit tests check the Mojo class in isolation, but integration tests run the plugin inside a real Maven build. The maven-invoker-plugin executes small sample projects under src/it/, runs goals against your freshly-installed plugin, and verifies the results.

open as a page

What does requiresDependencyResolution on a Mojo do, and when would you need it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

It tells Maven to resolve the project's dependencies (for a given scope) before running the Mojo, so the Mojo can read the resolved artifacts/classpath. You need it whenever your goal inspects or uses the project's dependencies.

open as a page