skip to content

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