Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Maven 3 Plugin Development

5.00/5 (1 vote)
29 Jul 2015CPOL4 min read 16.2K  
New generation, annotation based maven 3 plugin development. Get rid of javadocs !

Introduction

First of all, it has been a lot since my last article on codeproject. It will excited for me to write it down here :)

In general, maven supports plugin development all the time. And it wasn't complicated all the time. But since Java 5, we have the opportunity of annotations, therefore maven decided to support plugin development with annotations in version 3. 

There will be some expressions such as mojo, threadSafe etc. I will explain them in the corresponding sections.

Background

The basic idea behind the maven plugin development is developing a tool which hepls you to tidy some mess ups. For example if you are doing some post-build or pre-build staff, plugins are what you are looking for.

For me, i was needed to develop a plugin for one of our products. The product had four or more maven plugins to assemble a final War file, and it was very complicated to create one. My job was creating a plugin to do 4-5 plugins jobs at once.  

In the meantime, you can learn more about maven 3 plugin development here

Using the code

Mojo basically means Pojos.  And since maven 3 and Java 5, we can easily write a mojo with annotations. They are simply look likes spring components for me. But of course there are no similarity except views of spring components and mojos.

Here's the requirements of writing a maven 3 plugin:

  • Of course mojos should be in maven projects. Create a classic maven project for it. For a quick setup you can use the following archetype, but i recommend you to follow my other steps to understand fully all development. 
Java
maven-archetype-plugin
  • To use annotations, you have to add following dependency into your pom.xml
XML
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version> <!-- This is my version, please check latest -->
<scope>provided</scope> <!-- It comes from maven itself, that's why we are making it provided -->
  • We need to reach some of the maven variables, such as project (includes almost all information about project) etc. Thus, we can reach dependencies, build path infos etc of project which is using our plugin.
XML
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.2.3</version> <!-- This is my version, please check latest -->
  • Our plugin maven project packaging should be as:
XML
<packaging>maven-plugin</packaging>
  • (Optional) In my maven plugin, i was needed to execute another plugins to proceed, so I've added following dependency to my pom.xml. Therefore i was able to execute another plugins from inside of my plugin.
XML
<groupId>org.twdata.maven</groupId>
<artifactId>mojo-executor</artifactId>
<version>2.2.0</version> <!-- This is my version, please check latest -->
  • Lastly, we have to override maven project's default descriptor execution to process-classes. There's no clear explanation of why we are overriding it. But you can explain more here
XML
<build>
<plugins>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-plugin-plugin</artifactId>
    <version>3.4</version>
    <executions>
        <execution>
        <id>default-descriptor</id>
        <phase>process-classes</phase>
        </execution>
    </executions>
</plugin>
</plugins>
</build>

Now, everything is ready to write a mojo. We can specify a class as Mojo by annotating it with @Mojo annotation. And it has to be extended from AbstractMojo class. With this method we will override a method and maven will now what to do when executing the plugin.

A simple mojo will look like as below. 

Java
@Mojo(name = "my-goal", threadSafe = true, defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.TEST)
public class MyMojo extends AbstractMojo{

}

The properties are described as below.

  • name - your plugin's goal, you will write it into <execution><goals><goal> tag
  • threadSafe - maven 3 supports parallel builds, with setting this property true, you make maven run this plugin synchronized.
  • defaultPhase - this is the default phase of your plugin, it can be overriden by the plugin user.
  • requiresDependencyResolution - it's your plugins dependency coverage section. TEST means you can access all dependencies. For further info please examine here

The next thing that we should do is overriding execute function of AbstractMojo.

Java
@Override
public void execute() throws MojoExecutionException, MojoFailureException{
    getLog().info( "Now working my first plugin..." );
}

You can access all project properties by adding following dependency to your mojo.

Java
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;

Here's an example:

Java
for( Dependency dependency : project.getDependencies() ){
    getLog().info( dependency.getGroupId() );
}

// prints all dependencies group ids.

Parameter annotation lets you to define maven parameters. With defaultValue property we are accessing a maven property, and setting it readonly to be sure noone can overwrite it.

For last I  want to show how to execute a maven plugin in your plugin code. This was my requirement but its fully optional to do so. Everything in the execute method is your requirement, please keep it in your mind.

Java
executeMojo(
        plugin( groupId( "org.apache.maven.plugins" ), artifactId( "maven-resources-plugin" ), version( "2.7" ) ),
        goal( "copy-resources" ),
        configuration(
                element( name( "outputDirectory" ), project.getBuild().getDirectory() + "/test" ),
                element( name( "overwrite" ), "true" ),
                element(
                        name( "resources" ),
                        element(
                                name( "resource" ),
                                element( name( "directory" ), project.getBuild().getDirectory() + "/" ),
                                element( name( "excludes" ), element( name( "exclude" ), "**/com/sercanozdemir/**" ),
                                        element( name( "exclude" ), "**/WEB-INF/**" ) ) ) ) ), executionEnvironment( project, mavenSession, pluginManager ) );

mavenSession and pluginManager variables are coming from the maven itself.

Java
@Parameter(defaultValue = "${session}", readonly = true)
private MavenSession mavenSession;

@Component
private BuildPluginManager pluginManager;

@Component annotation is for configuring injection of Plexus components or Maven context components.

Quote:

Plexus is a platform consisting of an Inversion-of-Control container along with a number of utility libraries, including the Classworlds class loader framework. Maven was built on it but, a large amount of work has gone into replacing it with Guice.

Plexus also provides a number of utility libraries which are heavily used by the core Maven plugins -- plexus-utilsplexus-archiver and more. ~/.m2/repository/org/codehaus/plexus/ will be fairly busy on any machine that's run Maven.

And finally, here's an example usage of this simple plugin:

XML
<build>
    <plugins>
        <plugin>
            <groupId>com.sercanozdemir.plugin</groupId>
            <artifactId>mymojo</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <executions>
                <execution>
                    <goals>
                        <goal>my-goal</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Furthermore, you can extend your code by getting more parameters from the user or changing the default phase of your plugin.

Points of Interest

There's a lack of source in the internet for maven 3 plugin development currently, i hope this can help someone out there.

Thanks !

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)