Apache Maven 1.x has reached its end of life, and is no longer supported. For more information, see the announcement. Users are encouraged to migrate to the current version of Apache Maven.

Developing Plugins

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:

  • A project.xml file to describe the plugin project (required);
  • A plugin.jelly file containing Jelly script for the plugin (required);
  • A plugin.properties file to define any property defaults (optional);
  • Plugin resources for adding to the classpath when the plugin is run (optional);
  • Java sources, which are accessed from the Jelly script (optional).

How to create your first plugin

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.

Using Plugin Properties

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.

Using Plugin Resources

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.

Plugin dependencies

In maven 1.x, there are only two classloaders :

  • root : Where are loaded ant, commons-logging, log4j.
  • root.maven (child of root) : Where are loaded (by default) all others dependencies needed by maven, its plugins and the user project.

This design can create 2 problems when you write your own plugin :

  1. You won't be able to use your own version of a dependency. If this one is already loaded by maven or one of its plugins in a different version you'll automatically use it. To avoid having this sort of problem, you are invited to use in priority the libraries versions already used by maven or by the bundled plugins.
  2. 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>

Providing Reports in a Plugin

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 site
  • hello:deregister - disables the report so that a project can override a previous enabling command

Registering 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.

Sharing Your Plugin

Please see Sharing Your Plugin for guidelines on how to share your Maven plugins with the community.

Getting More Information

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.