Introduction
Starting with Maven, plugins can be written in Java or any of a number of scripting languages. Plugins consists of one or more Mojos, each one being the implementation for one of the plugin's goals. Maven tries to stay out of the way of the programmer with its new Mojo API. This opens up the opportunity for many Mojos to be reused outside of Maven, or bridged into Maven from external systems like Ant.
NOTE: For now, we will limit the discussion to Java-based Mojos, since each scripting language will present these same basic requirements with various forms of implementation.
Although the requirements on Mojos are minimal by design, there are still a very few requirements that Mojo developers must keep in mind. Basically, these Mojo requirements are embodied by the org.apache.maven.plugin.Mojo interface, which the Mojo must implement (or else extend its abstract base class counterpart org.apache.maven.plugin.AbstractMojo ). This interface guarantees the correct execution contract for the Mojo: no parameters, void return type, and a throws clause that allows only org.apache.maven.plugin.MojoExecutionException and its derivatives. It also guarantees that the Mojo will have access to the standard Maven user-feedback mechanism, org.apache.maven.plugin.logging.Log , so the Mojo can communicate important events to the console or other log sink.
As mentioned before, each Plugin - or packaged set of Mojos - must provide a descriptor called plugin.xml under the path META-INF/maven inside the Plugin jar file. Fortunately, Maven also provides a set of Javadoc annotations (named Mojo Javadoc Tags), Java 5 annotations (named Maven Plugin Tools Java5 Annotations) and tools (named plugin-tools) to generate this descriptor, so developers don't have to worry about directly authoring or maintaining a separate XML metadata file.
To serve as a quick reference for the developer, the rest of this page will document these features (the API, along with the annotations) which are considered the best practice for developing Mojos.
API Documentation
org.apache.maven.plugin.Mojo
This interface forms the contract required for Mojos to interact with the Maven infrastructure. It features an execute() method, which triggers the Mojo's build-process behavior, and can throw a MojoExecutionException if an error condition occurs. See below for a discussion on proper use of this Exception class. Also included is the setLog(..) method, which simply allows Maven to inject a logging mechanism which will allow the Mojo to communicate to the outside world through standard Maven channels.
Method Summary:
- void setLog( org.apache.maven.plugin.logging.Log )
Inject a standard Maven logging mechanism to allow this Mojo to communicate events and feedback to the user.
- void execute() throws org.apache.maven.plugin.MojoExecutionException
Perform whatever build-process behavior this Mojo implements. This is the main trigger for the Mojo inside the Maven system, and allows the Mojo to communicate fatal errors by throwing an instance of MojoExecutionException.
The MojoExecutionException (and all error conditions inside the Mojo) should be handled very carefully. The simple wrapping of lower-level exceptions without providing any indication of a user-friendly probable cause is strictly discouraged. In fact, a much better course of action is to provide error handling code (try/catch stanzas) for each coherent step within the Mojo's execution. Developers are then in a much better position to diagnose the cause of any error, and provide user-friendly feedback in the message of the MojoExecutionException.
org.apache.maven.plugin.AbstractMojo
Currently, this abstract base class simply takes care of managing the Maven log for concrete derivations. In keeping with this, it provides a protected method, getLog():Log , to furnish Log access to these concrete implementations.
Method Summary:
- public void setLog( org.apache.maven.plugin.logging.Log )
[IMPLEMENTED]
Inject a standard Maven logging mechanism to allow this Mojo to communicate events and feedback to the user.
- protected Log getLog()
[IMPLEMENTED]
Furnish access to the standard Maven logging mechanism which is managed in this base class.
- void execute() throws org.apache.maven.plugin.MojoExecutionException
[ABSTRACT]
Perform whatever build-process behavior this Mojo implements. See the documentation for Mojo above for more information.
org.apache.maven.plugin.logging.Log
This interface supplies the API for providing feedback to the user from the Mojo, using standard Maven channels. There should be no big surprises here, although you may notice that the methods accept java.lang.CharSequence rather than java.lang.String . This is provided mainly as a convenience, to enable developers to pass things like StringBuffer directly into the logger, rather than formatting first by calling toString() .
Method Summary:
- void debug( java.lang.CharSequence )
Send a message to the user in the debug error level.
- void debug( java.lang.CharSequence, java.lang.Throwable )
Send a message (and accompanying exception) to the user in the debug error level. The error's stacktrace will be output when this error level is enabled.
- void debug( java.lang.Throwable )
Send an exception to the user in the debug error level. The stack trace for this exception will be output when this error level is enabled.
- void info( java.lang.CharSequence )
Send a message to the user in the info error level.
- void info( java.lang.CharSequence, java.lang.Throwable )
Send a message (and accompanying exception) to the user in the info error level. The error's stacktrace will be output when this error level is enabled.
- void info( java.lang.CharSequence )
Send an exception to the user in the info error level. The stack trace for this exception will be output when this error level is enabled.
- void warn( java.lang.CharSequence )
Send a message to the user in the warn error level.
- void warn( java.lang.CharSequence, java.lang.Throwable )
Send a message (and accompanying exception) to the user in the warn error level. The error's stacktrace will be output when this error level is enabled.
- void warn( java.lang.CharSequence )
Send an exception to the user in the warn error level. The stack trace for this exception will be output when this error level is enabled.
- void error( java.lang.CharSequence )
Send a message to the user in the error error level.
- void error( java.lang.CharSequence, java.lang.Throwable )
Send a message (and accompanying exception) to the user in the error error level. The error's stacktrace will be output when this error level is enabled.
- void error( java.lang.CharSequence )
Send an exception to the user in the error error level. The stack trace for this exception will be output when this error level is enabled.
The Descriptor and Annotations
In addition to the normal Java requirements in terms of interfaces and/or abstract base classes which need to be implemented, a plugin descriptor must accompany these classes inside the plugin jar. This descriptor file is used to provide metadata about the parameters and other component requirements for a set of Mojos so that Maven can initialize the Mojo and validate its configuration before executing it. As such, the plugin descriptor has a certain set of information that is required for each Mojo specification to be valid, as well as requirements for the overall plugin descriptor itself.
NOTE: In the following discussion, bolded items are the descriptor's element name along with a Mojo Javadoc tag (if applicable) supporting that piece of the plugin descriptor. A couple of examples are: someElement (@annotation parameterName="parameterValue") or someOtherElement (@annotation <rawAnnotationValue>) .
NOTE: since maven-plugin-plugin 3.0, it is now possible to use Maven Plugin Tools Java 5 Annotations equivalent to Mojo Javadoc tags. See Using annotations documentation.
The plugin descriptor (see descriptor reference) must be provided in a jar resource with the path: META-INF/maven/plugin.xml, and it must contain the following:
Descriptor Element | Required? | Notes |
---|---|---|
mojos | Yes | Descriptors for each Mojo provided by the plugin, each inside a mojo sub-element. Mojo descriptors are covered in detail below. Obviously, a plugin without any declared Mojos doesn't make sense, so the mojos element is required, along with at least one mojo sub-element. |
dependencies | Yes | A set of dependencies which the plugin requires in order to function. Each dependency is provided inside a dependency sub-element. Dependency specifications are covered below. Since all plugins must have a dependency on maven-plugin-api , this element is effectively required. Using the plugin toolset, these dependencies can be extracted from the POM's dependencies. |
Each Mojo specified inside a plugin descriptor must provide the following (annotations specified here are at the class level):
Descriptor Element | Mojo Javadoc tag | Required? | Notes | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
aggregator | @aggregator | No | Flags this Mojo to run it in a multi module way, i.e. aggregate the build with the set of projects listed as modules. | ||||||||||||||||||||||||||||||
configurator | @configurator <roleHint> | No | The configurator type to use when injecting parameter values into this Mojo. The value is normally deduced from the Mojo's implementation language, but can be specified to allow a custom ComponentConfigurator implementation to be used. NOTE: This will only be used in very special cases, using a highly controlled vocabulary of possible values. (Elements like this are why it's a good idea to use the descriptor tools.) | ||||||||||||||||||||||||||||||
execute |
|
No | When this goal is invoked, it will first invoke a parallel lifecycle, ending at the given phase. If a goal is provided instead of a phase, that goal will be executed in isolation. The execution of either will not affect the current project, but instead make available the ${executedProject} expression if required. An alternate lifecycle can also be provided: for more information see the documentation on the build lifecycle. | ||||||||||||||||||||||||||||||
executionStrategy | @executionStrategy <strategy> | No | Specify the execution strategy. NOTE: Currently supports once-per-session, always. | ||||||||||||||||||||||||||||||
goal | @goal <goalName> | Yes | The name for the Mojo that users will reference from the command line to execute the Mojo directly, or inside a POM in order to provide Mojo-specific configuration. | ||||||||||||||||||||||||||||||
inheritByDefault | @inheritByDefault <true|false> | No. Default: true | Specify that the Mojo is inherited. | ||||||||||||||||||||||||||||||
instantiationStrategy | @instantiationStrategy <per-lookup> | No. Default: per-lookup | Specify the instantiation strategy. | ||||||||||||||||||||||||||||||
phase | @phase <phaseName> | No | Defines a default phase to bind a mojo execution to if the user does not explicitly set a phase in the POM. Note: This annotation will not automagically make a mojo run when the plugin declaration is added to the POM. It merely enables the user to omit the <phase> element from the surrounding <execution> element. | ||||||||||||||||||||||||||||||
requiresDependencyResolution | @requiresDependencyResolution <requiredClassPath> | No | Flags this Mojo as requiring the dependencies in the specified class path to be resolved before it can execute. The matrix below illustrates which values for <requiredClassPath> (first column) are supported and which dependency scopes (first row) they will request to resolve:
|
||||||||||||||||||||||||||||||
requiresDependencyCollection | @requiresDependencyCollection <requiredClassPath> | No | Flags this mojo as requiring information about the dependencies that would make up the specified class path. As the name suggests, this annotation is similar to @requiresDependencyResolution and supports the same values for <requiredClassPath>. The important difference is that this annotation will not resolve the files for the dependencies, i.e. the artifacts associated with a Maven project can lack a file. As such, this annotation is meant for mojos that only want to analyze the set of transitive dependencies, in particular during early lifecycle phases where full dependency resolution might fail due to projects which haven't been built yet. A mojo may use both this annotation and @requiresDependencyResolution at the same time. The resolution state of any dependency that is collected but not requested to be resolved is undefined. Since Maven 3.0. | ||||||||||||||||||||||||||||||
requiresDirectInvocation | @requiresDirectInvocation <true|false> | No. Default: false | Flags this Mojo to be invoke directly. | ||||||||||||||||||||||||||||||
requiresOnline | @requiresOnline <true|false> | No. Default: false | Flags this Mojo to require online mode for its operation. | ||||||||||||||||||||||||||||||
requiresProject | @requiresProject <true|false> | No. Default: true | Flags this Mojo to run inside of a project. | ||||||||||||||||||||||||||||||
requiresReports | @requiresReports <true|false> | No. Default: false | Flags this Mojo to require reports. Unsupported since Maven 3.0. | ||||||||||||||||||||||||||||||
threadSafe | @threadSafe <true|false> | No. Default: false | Marks this mojo as being thread-safe, i.e. the mojo safely supports concurrent execution during parallel builds. Mojos without this annotation will make Maven output a warning when used during a parallel build session. The short-hand notation @threadSafe without a tag value is equivalent to @threadSafe true. Since Maven 3.0. | ||||||||||||||||||||||||||||||
description | none (detected) | No | The description of this Mojo's functionality. Using the toolset, this will be the class-level Javadoc description provided. NOTE: While this is not a required part of the Mojo specification, it SHOULD be provided to enable future tool support for browsing, etc. and for clarity. | ||||||||||||||||||||||||||||||
implementation | none (detected) | Yes | The Mojo's fully-qualified class name (or script path in the case of non-Java Mojos). | ||||||||||||||||||||||||||||||
language | none (detected) | No. Default: java | The implementation language for this Mojo (Java, beanshell, etc.). | ||||||||||||||||||||||||||||||
deprecated | @deprecated <deprecated-text> | No | Specify the version when the Mojo was deprecated to the API. Similar to Javadoc deprecated. This will trigger a warning when a user tries to configure a parameter marked as deprecated. | ||||||||||||||||||||||||||||||
since | @since <since-text> | No | Specify the version when the Mojo was added to the API. Similar to Javadoc since. |
Each Mojo specifies the parameters that it expects in order to work. These parameters are the Mojo's link to the outside world, and will be satisfied through a combination of POM/project values, plugin configurations (from the POM and configuration defaults), and System properties.
NOTE[1]: For this discussion on Mojo parameters, a single annotation may span multiple elements in the descriptor's specification for that parameter. Duplicate annotation declarations in this section will be used to detail each parameter of an annotation separately.
NOTE[2]: In many cases, simply annotating a Mojo field with @parameter will be enough to allow injection of a value for that parameter using POM configuration elements. The discussion below shows advanced usage for this annotation, along with others.
Each parameter for a Mojo must be specified in the plugin descriptor as follows:
Descriptor Element | Mojo Javadoc tag | Required? | Notes |
---|---|---|---|
alias | @parameter alias="myAlias" | No | Specifies an alias which can be used to configure this parameter from the POM. This is primarily useful to improve user-friendliness, where Mojo field names are not intuitive to the user or are otherwise not conducive to configuration via the POM. |
configuration | @component role="..." roleHint="..." | No | Populates the field with an instance of a Plexus component. This is like declaring a requirement in a Plexus component. The default requirement will have a role equal to the declared type of the field, and will use the role hint "default". You can customise either of these by providing a role and/or roleHint parameter. e.g. @component role="org.apache.maven.artifact.ArtifactHandler" roleHint="ear". Note: This is identical to the deprecated form of parameter: @parameter expression="${component.yourpackage.YourComponentClass#roleHint}". |
configuration | maven-plugin-plugin 2.x: @parameter expression="${aSystemProperty}" default-value="${anExpression}" maven-plugin-plugin 3.x: @parameter property="aSystemProperty" default-value="${anExpression}" |
No | Specifies the expressions used to calculate the value to be injected into this parameter of the Mojo at buildtime. The expression given by default-value is commonly used to refer to specific elements in the POM, such as ${project.resources}, which refers to the list of resources meant to accompany the classes in the resulting JAR file. Of course, the default value need not be an expression but can also be a simple constant like true or 1.5. And for parameters of type String one can mix expressions with literal values, e.g. ${project.artifactId}-${project.version}-special. The system property given by property in maven-plugin-plugin 3.x or expression in maven-plugin-plugin 2.x enables users to override the default value from the command line via -DaSystemProperty=value. NOTE: If neither default-value nor property or expression are specified, the parameter can only be configured from the POM. The use of '${' and '}' in default value is required to delimit actual expressions which may be evaluated. |
editable | @readonly | No | Specifies that this parameter cannot be configured directly by the user (as in the case of POM-specified configuration). This is useful when you want to force the user to use common POM elements rather than plugin configurations, as in the case where you want to use the artifact's final name as a parameter. In this case, you want the user to modify <build><finalName/></build> rather than specifying a value for finalName directly in the plugin configuration section. It is also useful to ensure that - for example - a List-typed parameter which expects items of type Artifact doesn't get a List full of Strings. NOTE: Specification of this annotation flags the parameter as non-editable; there is no true/false value. |
required | @required | No | Whether this parameter is required for the Mojo to function. This is used to validate the configuration for a Mojo before it is injected, and before the Mojo is executed from some half-state. NOTE: Specification of this annotation flags the parameter as required; there is no true/false value. |
description | none (detected) | No | The description of this parameter's use inside the Mojo. Using the toolset, this is detected as the Javadoc description for the field. NOTE: While this is not a required part of the parameter specification, it SHOULD be provided to enable future tool support for browsing, etc. and for clarity. |
name | none (detected) | Yes | The name of the parameter, to be used in configuring this parameter from the Mojo's declared defaults (discussed below) or from the POM. Using the toolset, this is detected as the Java field name. |
type | none (detected) | Yes | The Java type for this parameter. This is used to validate the result of any expressions used to calculate the value which should be injected into the Mojo for this parameter. Using the toolset, this is detected as the class of the Java field corresponding to this parameter. |
deprecated | @deprecated <deprecated-text> | No | Specify the version when the Mojo was deprecated to the API. Similar to Javadoc deprecated. This will trigger a warning when a user tries to configure a parameter marked as deprecated. |
since | @since <since-text> | No | Specify the version when the Mojo was added to the API. Similar to Javadoc since. |
The final component of a plugin descriptor is the dependencies. This enables the plugin to function independently of its POM (or at least to declare the libraries it needs to run). Dependencies are taken from the runtime scope of the plugin's calculated dependencies (from the POM). Dependencies are specified in exactly the same manner as in the POM, except for the <scope> element (all dependencies in the plugin descriptor are assumed to be runtime, because this is a runtime profile for the plugin).
Plugin Tools
By now, we've mentioned the plugin tools several times without telling you what they are or how to use them. Instead of manually writing (and maintaining) the metadata detailed above, Maven ships with some tools to aid in this task. In fact, the only thing a plugin developer needs to do is declare his project to be a plugin from within the POM. Once this is done, Maven will call the appropriate descriptor generators, etc. to produce an artifact that is ready for use within Maven builds. Optional metadata can be injected via Javadoc annotation (and possibly JDK5 annotations in the future) as described above, enabling richer interactions between the Mojo and the user. The section below describes the changes to the POM which are necessary to create plugin artifacts.
Project Descriptor (POM) Requirements
From the POM, Maven plugin projects look quite similar to any other project. For pure Java plugins, the differences are even smaller than for script-based plugins. The following details the POM elements which are necessary to build a Maven plugin artifact.
POM Element | Required for Java Mojos? | Sample Declaration | Notes |
---|---|---|---|
packaging | Yes | <packaging> maven-plugin </packaging> | The POM must declare a packaging element which describes this project as a Maven plugin project. |
scriptSourceDirectory | No | <scriptSourceDirectory> src/main/scripts </scriptSourceDirectory> | In the case of script-based Mojos (which are not covered in detail within this document), the POM must include an additional element to distinguish script sources from (optional) Java supporting classes. This element is scriptSourceDirectory, inside the build section. This directory is included in the list of resources which accompany any compiled code in the resulting artifact. It is specified separately from the resources in the build section to denote its special status as an alternate source directory for scripts. |
After making the changes above, the developer can simply call
mvn install
to install the plugin to the local repository. (Any of the other standard lifecycle targets like package, deploy, etc. are also available in like fashion.)
IDE integration
If you're using JetBrains IntelliJ IDEA to develop your plugin, you can use the following to configure the javadoc annotations as live templates.
- Download this file, and place it in $USER_HOME/.IntelliJIdea/config/templates
- (re)startup IntelliJ IDEA (templates are loaded on startup)
- add the following list to Settings -> IDE -> Errors -> General -> Unknown javadoc tags -> Additional javadoc tags
- aggregator, execute, goal, phase, requiresDirectInvocation, requiresProject, requiresReports, requiresOnline, parameter, component, required, readonly