Use Maven build directory in your plugin

Filed under: Automation, Maven, — Tags: ${project.build.directory}, POJO, mojo — Thomas Sundberg — 2014-05-21

How can you get the build directory in your maven plugin?

The answer to this question may seem simple, just refer to ./target in your plugin and everything should work. This works in the simple case. It does, however, not work when you are using you plugin in a multi module project. Your plugin will not know where it is executed and cannot refer to a subdirectory relative to the directory where Maven is invoked. It may be executed in a multi module build or a single module.

The directory . will refer to the execution directory and therefore will ./target refer to a target directory in the execution directory. This is not where the target directory lives in a multi module project. The target directory will typically be ./moduleName/target. Maven have the build directory, it is stored in the property ${project.build.directory}. So far so good, but how do you access this property from the Java code you are writing your plugin in? It turns out that you can specify a parameter, with any name you wish, and set its default value to ${project.build.directory}. The example below will log the build directory to the console when it is executed.

src/main/java/se/thinkcode/BuildDirMojo

package se.thinkcode;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * Will log the absolute path to the target directory in the execution log
 */
@Mojo(name = "get-build-dir", defaultPhase = LifecyclePhase.VALIDATE)
public class BuildDirMojo extends AbstractMojo {
    @Parameter(defaultValue = "${project.build.directory}")
    private String projectBuildDir;

    @Override
    public void execute() {
        Log log = getLog();
        log.info("");
        log.info("Project build dir: " + projectBuildDir);
        log.info("");
    }
}

When Maven populates the parameter projectBuildDir, it will expand the expression ${project.build.directory} to the actual value. This may be very reasonable if your background is shell scripting. As a developer I found the syntax a bit strange. There is no apparent reason why a String that should contain a default value is expanded into something else. If I set something to default then I expect to see that value when I look at it later, given that I haven't changed the value.

The implication of this is that if you need access to any Maven property in your plugin, just define a parameter and set its default value to the property you need.

If you are new to Maven plugin development, there are a few things that could be worth mentioning.

I subclass an AbstractMojo that will give me access to a Maven logger.

I am using two specific Maven annotations to define some metadata about the plugin.

First I define that this class should be a Mojo. A Mojo is the Maven equality to a a Pojo, Plain Old Java Object. Mojo stands for "Maven plain Old Java Object". Using the Mojo annotation I define that this plugin should be called using the goal "get-build-dir" from another Maven project. Using the same annotation, I define that the default execution phase should be validate. An example is defined later.

Second, I define a parameter that this Mojo should use. This parameter will be populated by Maven before the execution of the goal. In this case, it will be populated by expanding the default value defined into the current build directory using a Maven property.

The last part of this class is defining a method that will be executed. It is called execute and will be called when the goal get-build-dir is called.

To be able to build the example above, I used the pom below:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode.blog</groupId>
    <artifactId>parameter-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>maven-plugin</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
                </configuration>
                <executions>
                    <execution>
                        <id>mojo-descriptor</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>descriptor</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.2</version>
        </dependency>
    </dependencies>
</project>

I define that the packaging type should be maven-plugin. I also define that the maven-plugin-plugin should be used. It will help me to package the plugin and expand the annotations used above to something Maven can interpret and use. I also need two dependencies to get access to the AbstractMojo and the annotations.

To test the plugin, I defined a pom like the one below and call it using:
mvn validate

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode.blog</groupId>
    <artifactId>test-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>se.thinkcode.blog</groupId>
                <artifactId>parameter-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>get-build-dir</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

You should now be able to get any Maven property into your plugin and be able to expand the Maven functionality with a Maven plugin when you find a need for it.

Think of one thing before you start writing you own plugin,though. There may be a Maven way to do things that you should use instead. It is probably easier and many of the habits Maven try to enforce onto your project are actually really good.

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS