Adding functionality to Maven is done through the Maven plugin mechanism. Maven comes shipped with numerous plugins, and there are several 3rd party plugins. Plugins can be used to do virtually any task desired. For example, they can generate reports, create and deploy software distributions, run unit tests, and much more. This section of the document describes how to write your own plugin.
A plugin can contain the following:
project.xml
file to describe the plugin project (required);plugin.jelly
file containing Jelly script for the plugin (required);plugin.properties
file to define any property defaults (optional);To start creating a plugin, you must first create a Maven project. This is the same as creating any other project, for example one that builds a JAR.
In a new directory, create a project.xml
file like so:
<project> <pomVersion>3</pomVersion> <artifactId>hello-maven-plugin</artifactId> <groupId>hello</groupId> <name>Maven Hello World Plugin</name> <currentVersion>1.0</currentVersion> <!-- You might want to include additional information here eg the developers, organisation, and dependencies --> <build> <!-- This is only required if you have Java code --> <sourceDirectory>src/main/java</sourceDirectory> <!-- These are only required if you have unit tests for your Java code --> <unitTestSourceDirectory>src/test/java</unitTestSourceDirectory> <unitTest> <includes> <include>**/*Test.java</include> </includes> </unitTest> <!-- This section is compulsory --> <resources> <resource> <directory>${basedir}/src/plugin-resources</directory> <targetPath>plugin-resources</targetPath> </resource> <resource> <directory>${basedir}</directory> <includes> <include>plugin.jelly</include> <include>plugin.properties</include> <include>project.properties</include> <include>project.xml</include> </includes> </resource> </resources> </build> </project>
With just that, you can actually create the plugin (though it will do nothing!)
maven plugin
This command will create a JAR target/hello-maven-plugin-1.0.jar
, but it is not
installed yet. First, let's add some functionality.
Currently, all plugins must have a plugin.jelly
file. This is a Jelly script, and
looks identical to a maven.xml
file in an individual project. Here is a script with
a single, simple goal.
<project xmlns:ant="jelly:ant"> <goal name="hello:hello" description="Say Hello"> <ant:echo>Hello from a Maven Plug-in</ant:echo> </goal> </project>
(For an explanation of what this means, please refer to Customising Maven, or the Scripting Reference).
Now that you can run the following command to install this into Maven's home directory:
maven plugin:install
You can prove the goal exists by running:
maven -P hello
Finally, run the goal from the plugin:
maven hello:hello
As mentioned, you can do anything in this script that you can do in maven.xml
.
For more information, please refer to Scripting in Maven.
While you can always reference the POM information in a Jelly script using ${pom.artifactId}
,
for example, a plugin will often need to create new properties so that it can be customised.
The creation of these properties simply involves creating a plugin.properties
file.
Note: While plugins can also have a project.properties
file, these do not specify
properties exposed by the plugin. Instead, this is used to control building the plugin itself, like for
any other project.
As an example, create a plugin.properties
file in the same directory as
plugin.jelly
:
hello.greeting=Hi
Edit the plugin.jelly
file so that the <ant:echo>
line reads:
<ant:echo>${hello.greeting} from a Maven Plug-in</ant:echo>
Now, install the new plugin and run it (note, you do not need to run maven plugin
first):
maven plugin:install maven hello:hello
Notice the the new greeting. Now, to customise it:
maven -Dhello.greeting=Goodbye hello:hello
This property will be read from a variety of different places: project.properties
for the
current project, build.properties
from your home directory, or a system property as above.
The order of precedence is given in the Properties Reference.
You can use plugin resources to be able to load external files from inside a plugin.
Let's say you wanted to copy an image into every site. First, add a file called
src/plugin-resources/hello-report.xml
to the source tree that looks like this:
<document> <body> <section name="Hello"> <p>Hi!</p> </section> </body> </document>
Next, you can create a new goal called hello:report
in plugin.jelly
.
... <goal name="hello:report"> <ant:copy todir="${maven.gen.docs}" file="${plugin.resources}/hello-report.xml" /> </goal> ...
The plugin resources become most useful when you have templates for generate site reports based on the project, which is discussed in the next section. This report goal will be used to "generate" the Hello Report.
In maven 1.x, there are only two classloaders :
This design can create 2 problems when you write your own plugin :
If you try to use an Ant task which requires another dependency, the ant task doesn't find it even though you defined it correctly in your plugin's pom. This due to the fact that ant is loaded in the classloader "root" and your dependencies are visible in the classloader "root.maven". To solves this, you must specify to maven to load this dependency in the root classloader with :
<dependency> <properties> <classloader>root</classloader> </properties> </dependency>
Plugins are often used to provide one or more reports using a certain tool based on the information in a project. The standard reports are shown under "Project Reports" in the web site of any Maven project that has not disabled them. For example, the Unit Test Report shows which unit tests have passed and failed.
While any goal can perform a task, and transform the results into the web site, there are
special steps to take to signify a report can be generated so that it can be listed on the
web site, and enabled via the <reports>
section in the POM.
For information on how to actually generate the report, it is easiest to refer to the Jelly
source code of a report that is similar to what you want. The end result should be to create
an XDoc file in the directory specified by
${maven.gen.docs}
.
To register a report withing your project, you should define three goals (using hello as the example):
hello:report
- generates the report content. This was the goal we created in the
previous section.hello:register
- registers the report with Maven for inclusion on the sitehello:deregister
- disables the report so that a project can override a previous
enabling commandRegistering a report is done as follows:
<goal name="hello:register"> <doc:registerReport pluginName="hello" name="Hello" link="hello-report" description="Greeting report." /> </goal>
This introduces the doc
tag library, so the following change must be made to the project
declaration:
<project xmlns:ant="jelly:ant" xmlns:doc="doc">
The parameters to the registerReport
tag are:
pluginName |
This is the start of the goal name to run, and represents this plugin.
The report goal run will be ${pluginName}:report .
|
name | The name of the report, which will be used to list in the report in the navigation bar of the web site. |
link |
This is a relative link to the final output of the plugin, with the extension omitted.
When generating, the link should be relative to maven.gen.docs , so that it will
end up as relative to maven.docs.dest . eg ${maven.gen.docs}/hello-report.xml
and ${maven.docs.dest}/hello-report.html .
|
description |
This should be a one line description of the output
produced by your plugin. It is included on the
auto-generated maven-reports.xml overview
document.
|
target |
This should be a target for the html link generated.
This parameter is often used to open the report page in a new window
of the browser (target="_blank" ).
|
A plugin may specify more than one report. It is entirely possible to register additional reports as shown below (taken from the JavaDoc plugin included with Maven):
<goal name="maven-javadoc-plugin:register"> <j:if test="${sourcesPresent == 'true'}"> <doc:registerReport name="JavaDocs" link="apidocs/index" target="_blank" description="JavaDoc API documentation."/> <doc:registerReport name="JavaDoc Report" link="javadoc" description="Report on the generation of JavaDoc."/> </j:if> </goal>
Notice that the above plugin does not register its reports if the project does not have any sources. It is encouraged that plugin developers use conditional logic to prevent their reports from registering if they don't apply to the user's project.
The final goal is the deregistration goal. This goal is optional, but should be included to allow users to force its removal from their site.
The name
parameter should correspond to the original registration name
parameter.
<goal name="hello:deregister"> <doc:deregisterReport name="Hello"/> </goal>
The final step is to add the report to your project to use it, using what was specified as
pluginName
when registering the report:
<reports> <report>hello</report> </reports>
You can add this to any project, but for now we'll just reuse the same plugin project to test the plugin on.
Add the above section to the project.xml
you've created, then run:
maven site
Notice the final result of a target/docs/hello-report.html
file. You can view this in your
browser. When doing so, you'll also see the "Hello" item under the "Project Reports" menu in the navigation.
Please see Sharing Your Plugin for guidelines on how to share your Maven plugins with the community.
A good source of information is the source code for existing plugins.
You can find the expanded plugin sources in ~/.maven/cache
.
For more information on scripting, refer to the Scripting Reference.