This guide is intended to assist users in developing Java plugins for Maven 2.0.
In this section we will build a simple plugin which takes no parameters and simply displays a message on the screen when run. Along the way, we will cover the basics of setting up a project to create a plugin, the minimal contents of a Java mojo, and a couple ways to execute the mojo.
At its simplest, a Java mojo consists simply of a single class. There is no requirement for multiple classes like EJBs, although a plugin which contains a number of similar mojos is likely to use an abstract superclass for the mojos to consolidate code common to all mojos.
When processing the source tree to find mojos, the class org.apache.maven.tools.plugin.extractor.java.JavaMojoDescriptorExtractor looks for classes with a "goal" annotation on the class. Any class with this annotation are included in the plugin configuration file.
Listed below is a simple mojo class which has no parameters. This is about as simple as a mojo can be. After the listing is a description of the various parts of the source.
package sample.plugin; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; /** * Says "Hi" to the user. * @goal sayhi */ public class GreetingMojo extends AbstractMojo { public void execute() throws MojoExecutionException { getLog().info( "Hello, world." ); } }
All Mojo annotations are described by the Mojo API Specification.
Once the mojos have been written for the plugin, it is time to build the plugin. To do this properly, the project's descriptor needs to have a number of settings set properly:
groupId | This is the group ID for the plugin, and should match the common prefix to the packages used by the mojos |
artifactId | This is the name of the plugin |
version | This is the version of the plugin |
packaging | This should be set to "maven-plugin" |
dependencies | A dependency must be declared to the Maven Plugin Tools API to resolve "AbstractMojo" and related classes |
Listed below is an illustration of the sample mojo project's pom with the parameters set as described in the above table:
<project> <modelVersion>4.0.0</modelVersion> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <name>Sample Parameter-less Maven Plugin</name> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>2.0</version> </dependency> </dependencies> </project>
There are few goals which are defined with the Maven plugin packaging as part of a standard build lifecycle:
compile | Compiles the Java code for the plugin and builds the plugin descriptor |
test | Runs the plugin's unit tests |
package | Builds the plugin jar |
install | Installs the plugin jar in the local repository |
deploy | Deploys the plugin jar to the remote repository |
The most direct means of executing your new plugin is to specify the plugin goal directly on the command line. To do this, you need to configure the hello-maven-plugin plugin in you project:
... <build> <plugins> <plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> </plugin> </plugins> </build> ...
And, you need to specify a fully-qualified goal in the form of:
mvn groupId:artifactId:version:goal
For example, to run the simple mojo in the sample plugin, you would enter "mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi" on the command line.
Tips: version is not required to run a standalone goal.
There are several ways to reduce the amount of required typing:
<pluginGroups> <pluginGroup>sample.plugin</pluginGroup> </pluginGroups>
At this point, you can run the mojo with "mvn hello:sayhi".
You can also configure your plugin to attach specific goals to a particular phase of the build lifecycle. Here is an example:
<build> <plugins> <plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <executions> <execution> <phase>compile</phase> <goals> <goal>sayhi</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
This causes the simple mojo to be executed whenever Java code is compiled. For more information on binding a mojo to phases in the lifecycle, please refer to the Build Lifecycle document.
To create a new plugin project, you could using the Mojo archetype with the following command line:
mvn archetype:generate \ -DgroupId=sample.plugin \ -DartifactId=hello-maven-plugin \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-mojo
It is unlikely that a mojo will be very useful without parameters. Parameters provide a few very important functions:
Defining a parameter is as simple as creating an instance variable in the mojo and adding the proper annotations. Listed below is an example of a parameter for the simple mojo:
/** * The greeting to display. * * @parameter expression="${sayhi.greeting}" default-value="Hello World!" */ private String greeting;
The portion before the annotations is the description of the parameter. The parameter annotation identifies the variable as a mojo parameter. The default-value parameter of the annotation defines the default value for the variable. This value can include expressions which reference the project, such as "${project.version}" (more can be found in the "Parameter Expressions" document). The expression parameter can be used to allow configuration of the mojo parameter from the command line by referencing a system property that the user sets via the -D option.
Configuring the parameter values for a plugin is done in a Maven 2 project within the pom.xml file as part of defining the plugin in the project. An example of configuring a plugin:
<plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <configuration> <greeting>Welcome</greeting> </configuration> </plugin>
In the configuration section, the element name ("greeting") is the parameter name and the contents of the element ("Welcome") is the value to be assigned to the parameter.
Note: More details can be found in the Guide to Configuring Plugins.
Listed below are the various types of simple variables which can be used as parameters in your mojos, along with any rules on how the values in the POM are interpreted.
This includes variables typed boolean and Boolean. When reading the configuration, the text "true" causes the parameter to be set to true and all other text causes the parameter to be set to false. Example:
/** * My boolean. * * @parameter */ private boolean myBoolean;
<myBoolean>true</myBoolean>
This includes variables typed byte, Byte, int, Integer, long, Long, short, and Short. When reading the configuration, the text in the XML file is converted to an integer value using either Integer.parseInt() or the valueOf() method of the appropriate class. This means that the strings must be valid decimal integer values, consisting only of the digits 0 to 9 with an optional - in front for a negative value. Example:
/** * My Integer. * * @parameter */ private Integer myInteger;
<myInteger>10</myInteger>
This includes variables typed double, Double, float, and Float. When reading the configuration, the text in the XML file is converted to binary form using the valueOf() method for the appropriate class. This means that the strings can take on any format specified in section 3.10.2 of the Java Language Specification. Some samples of valid values are 1.0 and 6.02E+23.
/** * My Double. * * @parameter */ private Double myDouble;
<myDouble>1.0</myDouble>
This includes variables typed Date. When reading the configuration, the text in the XML file is converted using one of the following date formats: "yyyy-MM-dd HH:mm:ss.S a" (a sample date is "2005-10-06 2:22:55.1 PM") or "yyyy-MM-dd HH:mm:ssa" (a sample date is "2005-10-06 2:22:55PM"). Note that parsing is done using DateFormat.parse() which allows some leniency in formatting. If the method can parse a date and time out of what is specified it will do so even if it doesn't exactly match the patterns above. Example:
/** * My Date. * * @parameter */ private Date myDate;
<myDate>2005-10-06 2:22:55.1 PM</myDate>
This includes variables typed File. When reading the configuration, the text in the XML file is used as the path to the desired file or directory. If the path is relative (does not start with / or a drive letter like C:), the path is relative to the directory containing the POM. Example:
/** * My File. * * @parameter */ private File myFile;
<myFile>c:\temp</myFile>
This includes variables typed URL. When reading the configuration, the text in the XML file is used as the URL. The format must follow the RFC 2396 guidelines, and looks like any web browser URL (scheme://host:port/path/to/file). No restrictions are placed on the content of any of the parts of the URL while converting the URL.
/** * My URL. * * @parameter */ private URL myURL;
<myURL>http://maven.apache.org</myURL>
Listed below are the various types of composite objects which can be used as parameters in your mojos, along with any rules on how the values in the POM are interpreted. In general, the class of the object created to hold the parameter value (as well as the class for each element within the parameter value) is determined as follows (the first step which yields a valid class is used):
Once the type for the element is defined, the text in the XML file is converted to the appropriate type of object
Array type parameters are configured by specifying the parameter multiple times. Example:
/** * My Array. * * @parameter */ private String[] myArray;
<myArray> <param>value1</param> <param>value2</param> </myArray>
This category covers any class which implements java.util.Collection such as ArrayList or HashSet. These parameters are configured by specifying the parameter multiple times just like an array. Example:
/** * My List. * * @parameter */ private List myList;
<myList> <param>value1</param> <param>value2</param> </myList>
For details on the mapping of the individual collection elements, see Mapping Lists.
This category covers any class which implements java.util.Map such as HashMap but does not implement java.util.Properties. These parameters are configured by including XML tags in the form <key>value</key> in the parameter configuration. Example:
/** * My Map. * * @parameter */ private Map myMap;
<myMap> <key1>value1</key1> <key2>value2</key2> </myMap>
This category covers any map which implements java.util.Properties. These parameters are configured by including XML tags in the form <property><name>myName</name> <value>myValue</value> </property> in the parameter configuration. Example:
/** * My Properties. * * @parameter */ private Properties myProperties;
<myProperties> <property> <name>propertyName1</name> <value>propertyValue1</value> <property> <property> <name>propertyName2</name> <value>propertyValue2</value> <property> </myProperties>
This category covers any class which does not implement java.util.Map, java.util.Collection, or java.util.Dictionary. Example:
/** * My Object. * * @parameter */ private MyObject myObject;
<myObject> <myField>test</myField> </myObject>
Please see Mapping Complex Objects for details on the strategy used to configure those kind of parameters.
You are not restricted to using private field mapping which is good if you are trying to make you Mojos resuable outside the context of Maven. Using the example above we could name our private fields using the underscore convention and provide setters that the configuration mapping mechanism can use. So our Mojo would look like the following:
public class MyQueryMojo extends AbstractMojo { /** * @parameter property="url" */ private String _url; /** * @parameter property="timeout" */ private int _timeout; /** * @parameter property="options" */ private String[] _options; public void setUrl( String url ) { _url = url; } public void setTimeout( int timeout ) { _timeout = timeout; } public void setOptions( String[] options ) { _options = options; } public void execute() throws MojoExecutionException { ... } }
Note the specification of the property name for each parameter which tells Maven what setter and getter to use when the field's name does not match the intended name of the parameter in the plugin configuration.