How to Create a Maven Plugin

How to Create a Maven Plugin

Learn more about how to create a Maven plugin.

!Apache Maven is a widely-adopted build automation tool in the Java space, popular for its plugin options. The tool simplifies your build process by making it easy to find an existing plugin that satisfies almost all your product’s needs, such as verifying binary compatibility between versions and confirming your source files have license headers.

In this post, we are going to create a Maven Plugin that will run ‘git rev-parse’. You’ll need to have Java 8 and Apache Maven installed in order to get started; you can use SDKMAN for that.

Create a New Maven Project

It shouldn’t be a surprise that I’m going to use Maven to build a new Maven plugin. You can use your favorite IDE to create a new project, but to keep things simple, I’m just going to create a new pom.xml file manually (in a new directory named example-maven-plugin:

<?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>com.okta.example</groupId>
    <artifactId>example-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>maven-plugin</packaging>
		
    <name>Example Maven Plugin</name>
    <description>An Example Maven Plugin</description>
		
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

This is as simple as it gets; I’ve defined the Maven GAV (Group ID, Artifact ID, Version), a name, and most importantly, I’ve set the packaging to maven-plugin. While the group ID could be just about anything, it is strongly recommended to be in reverse domain name notation, similar to Java packages.

Add Maven Dependencies

Next up, I need to define a few dependencies on maven-coremaven-plugin-api, and maven-plugin-annotations. These are all scoped as provided, which means when the plugin runs, the actual version used will depend on the version of Apache Maven you have installed.

  <dependencies>
        <dependency>
            <!-- plugin interfaces and base classes -->
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <!-- needed when injecting the Maven Project into a plugin  -->
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-core</artifactId>
            <version>3.6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <!-- annotations used to describe the plugin meta-data -->
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
Plugins Build Plugins

Plugins are what actually give Maven its power, at its core Maven is just a plugin framework, so naturally, I will use a Maven plugin to build a Maven plugin with the Maven Plugin Plugin. Turtles all the way down!

The maven-plugin-plugin is actually defined automatically because I used the packaging type of maven-plugin above; to use a newer version, I can update the plugin in the pluginManagment section:

 <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-plugin-plugin</artifactId>
                    <version>3.6.0</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
</project>

I’ve also included the Maven Site Plugin; this is optional — more on that later in the post.

That is it! If you want to copy and paste the whole file all at once, you can grab it from GitHub.

Write the Maven Plugin Code

On to the fun part: writing the code! A Maven plugin is actually a collection of one or more “goals.” Each goal is defined by a Java class referred to as a “Mojo” (Maven plain Old Java Object).

Create a new class: src/main/java/com/okta/example/maven/GitVersionMojo.java

package com.okta.example.maven;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

/**
 * An example Maven Mojo that resolves the current project's git revision and adds 
 * that a new {@code exampleVersion} property to the current Maven project.
 */
@Mojo(name = "version", defaultPhase = LifecyclePhase.INITIALIZE)
public class GitVersionMojo extends AbstractMojo {

    public void execute() throws MojoExecutionException, MojoFailureException {
        // The logic of our plugin will go here
    }
}

There isn’t much to it. Now, I have a new Maven Plugin, which has a single goal named version. This goal will execute when the project is initialized. There are a few lifecycles to pick from; for this example, I’m using “initialize” because I want my plugin to run before other plugins. If you were creating a plugin to create new files, you would likely want to use the “generate-resources” phase. Take a look at the lifecycle reference documentation for descriptions of other phases.

At this point, I could build the project with mvn install and then execute the plugin using:

# mvn ${groupId}:${artifactId}:${goal}
mvn com.okta.example:example-maven-plugin:version

However, since the execute method is empty, it won’t actually do anything yet.

Adding Maven Parameters

To make this plugin actually do something, I’m going to add a couple of parameters. Maven parameters are defined as fields in the MOJO class:

/**
 * The git command used to retrieve the current commit hash.
 */
@Parameter(property = "git.command", defaultValue = "git rev-parse --short HEAD")
private String command;

@Parameter(property = "project", readonly = true)
private MavenProject project;

It’s worth noting the Javadoc is important for Maven Plugins, as it will be used when we generate the plugin-specific documentation. Since we are all great developers, we never forget to add the doc, right?

The Parameter annotation tells Maven to inject a value into the field. This is similar to Spring’s Value annotation. For the command field, I’ve set property value to be git.command. This allows the user to change the value on the command line with the standard -D notation:

mvn com.okta.example:example-maven-plugin:version \
    -Dgit.command="git rev-parse --short=4 HEAD"

It’s also common to inject the MavenProject in order to read or modify something in the project directly. For example, the MavenProject gives you access to the dependencies and anything defined in a pom.xml. In my case, I’m going to add an additional property that can be used later in the build.

Execute a Command in Java

Now we have the command parameter, we need to execute it! Define a new getVersion method to handle this logic:

public String getVersion(String command) throws MojoExecutionException {
    try {
        StringBuilder builder = new StringBuilder();

        Process process = Runtime.getRuntime().exec(command);
        Executors.newSingleThreadExecutor().submit(() ->
            new BufferedReader(new InputStreamReader(process.getInputStream()))
                .lines().forEach(builder::append)
        );
        int exitCode = process.waitFor();

        if (exitCode != 0) {
            throw new MojoExecutionException("Execution of command '" + command 
                + "' failed with exit code: " + exitCode);
        }

        // return the output
        return builder.toString();

    } catch (IOException | InterruptedException e) {
        throw new MojoExecutionException("Execution of command '" + command 
            + "' failed", e);
    }
}

This uses Java’s built-in Runtime.exec() and captures the output text. Any exceptions are rethrown as a MojoExecutionException (which will cause a build failure.)

Next update the execute() method:

public void execute() throws MojoExecutionException, MojoFailureException {

    // call the getVersion method
    String version = getVersion(command);

    // define a new property in the Maven Project
    project.getProperties().put("exampleVersion", version);

    // Maven Plugins have built in logging too
    getLog().info("Git hash: " + version);
}

That is it. Now, we just need to use the plugin!

Usage of a Maven Plugin

Up until now, I’ve been executing the plugin directly using the command:

mvn com.okta.example:example-maven-plugin:version

Usually, plugins are added to a pom.xml so they are run automatically as part of a build. To demonstrate this, I’ll create a new Maven project with the following pom.xml (in a different directory):

<?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>com.okta.example</groupId>
    <artifactId>example-usage</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
		
    <build>
        <plugins>
            <plugin>
                <groupId>com.okta.example</groupId>
                <artifactId>example-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <configuration>
                    <!-- optional, the command parameter can be changed here too -->
                    <command>git rev-parse --short=4 HEAD</command>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>version</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
						
            <plugin>
                <groupId>com.github.ekryd.echo-maven-plugin</groupId>
                <artifactId>echo-maven-plugin</artifactId>
                <version>1.2.0</version>
                <inherited>false</inherited>
                <executions>
                    <execution>
                        <id>end</id>
                        <goals>
                            <goal>echo</goal>
                        </goals>
                        <phase>process-resources</phase>
                        <configuration>
                            <message>${line.separator}${line.separator}
                                The project version is ${project.version}-${exampleVersion}
                                ${line.separator}
                            </message>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Running mvn package on this project result gives the output:

The [INFO] Git hash: 1ab3 line shows my plugin as it executes, and the new exampleVersion property defined by the plugin is used by the echo-maven-plugin.

Dependency Injection in Maven Plugins

Our plugin is great and all, but all of the code is crammed into one file. I like to break up my code into easily testable chunks. Enter Sisu, the container Maven is built on. Sisu is an IoC container built on top of Guice, an alternative to Spring.

What this all really means is that I can use the standard JSR-330 (@Inject) annotations to break up my code and not worry about the details of the IoC container!

Create a new interface in src/main/java/com/okta/example/maven/VersionProvider.java:

package com.okta.example.maven;

import org.apache.maven.plugin.MojoExecutionException;

public interface VersionProvider {
    String getVersion(String command) throws MojoExecutionException;
}

And move the Runtime.exec logic out of GitVersionMojo into a new class src/main/java/com/okta/example/maven/RuntimeExecVersionProvider.java:

package com.okta.example.maven;

import org.apache.maven.plugin.MojoExecutionException;

import javax.inject.Named;
import javax.inject.Singleton;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Executors;

@Named
@Singleton
public class RuntimeExecVersionProvider implements VersionProvider {
    @Override
    public String getVersion(String command) throws MojoExecutionException {
        try {
            StringBuilder builder = new StringBuilder();

            Process process = Runtime.getRuntime().exec(command);
            Executors.newSingleThreadExecutor().submit(() ->
                new BufferedReader(new InputStreamReader(process.getInputStream())).lines().forEach(builder::append)
            );
            int exitCode = process.waitFor();

            if (exitCode != 0) {
                throw new MojoExecutionException("Execution of command '" + command + "' failed with exit code: " + exitCode);
            }

            // return the output
            return builder.toString();

        } catch (IOException | InterruptedException e) {
            throw new MojoExecutionException("Execution of command '" + command + "' failed", e);
        }
    }
}

I’ve added the standard Java @Named and @Singleton annotations to mark the class as a singleton that is managed by the IoC container. This is the equivalent of using Spring’s @Component.

Now just update the GitVersionMojo to inject the VersionProvider:

@Inject
private VersionProvider versionProvider;

public void execute() throws MojoExecutionException, MojoFailureException {
    String version = versionProvider.getVersion(command);
    project.getProperties().put("exampleVersion", version);
    getLog().info("Git hash: " + version);
}

That is it! You could build and run the plugin as before, and get the same results.

One More Thing, Documentation!

One of my favorite things about Maven is that plugins have consistent documentation structures. To generate the documentation, add a new reporting section to the pom.xml:

<reporting>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-plugin-plugin</artifactId>
            <reportSets>
                <reportSet>
                    <reports>
                        <report>report</report>
                    </reports>
                </reportSet>
            </reportSets>
        </plugin>
    </plugins>
</reporting>

You should also add more metadata to your project, but this is optional. For example, I’ll add the organization and prerequisites, as these are included in the generated site:

<organization>
    <name>Example, Inc</name>
    <url>https://google.com/search?q=example.com</url>
</organization>
<prerequisites>
    <maven>3.5.0</maven>
</prerequisites>

Now, just run mvn site to generate the documentation! Open the target/site/plugin-info.html in your browser. See why all of that Javadoc was so important?

If you liked this tutorial, share it with all of your programming buddies!

Free Online Eclipse Tutorial For Beginners : Learn Java IDE in 10 Steps | Simpliv

Free Online Eclipse Tutorial For Beginners : Learn Java IDE in 10 Steps | Simpliv

Free Online Eclipse Tutorial For Beginners: Learn Java IDE in 10 Steps

Description
Eclipse is the most popular Open Source Java IDE. More developers use Eclipse than any other tool for Java Programming.

In this course, we take you on a fun ride with Eclipse.

By the end of this course, you would have a number of Eclipse tips and tricks up your sleeve. You will also understand various features of Eclipse - Views, Perspectives, Debugger, Save Actions, Plugins and Code Generation.

You will improve your productivity with Eclipse Shortcuts . You will also learn to import projects from Git and the basics of Maven projects.

Course is Organised into 11 Steps : Experienced Eclipse Developers can give Step 0 a skip

Step 0 : Eclipse Basics - Workspace & Your First Java File
Step 1 : Most Important Editing Shortcuts ctrl-1 and ctrl-space
Step 2 : Debugging Java Programs
Step 3 : Eclipse Short Cuts
Step 4 : Refactoring with Eclipse
Step 5 : Code Generation with Eclipse
Step 6 : Automate with Save Actions
Step 7 : Eclipse Views
Step 8 : Eclipse Perspectives
Step 9 : Plugins
Step 10 : Setting up Projects From Git and Maven
Who is the target audience?

Students who want to learn Programming with Eclipse
Students who have some experience with Eclipse but who want to learn a lot more about Eclipse
Students Learning Java
Basic knowledge
Some programming experience with Java will be handy
What will you learn
Be more productive with Eclipse
Understand Eclipse Basics : Views, Perspectives, Shortcuts, Code Generation & Plugins
Make use of Eclipse Save Actions Features
To continue:

Learn Java Programming | Java Complete Tutorial for Beginners | Simpliv

Learn Java Programming | Java Complete Tutorial for Beginners | Simpliv

This program on Java programing from Simpliv’s experts uses Java and an Integrated Development Environment (IDE). Just download this, with their help of course, and open your way to hundreds of lines of source code, and hundreds of lines of comments. That is some Java programming learning!

Description
Taught by a Stanford-educated, ex-Googler, husband-wife team
This course will use Java and an Integrated Development Environment (IDE). Never fear, we have a detailed video on how to get this downloaded and set up.
Hundreds of lines of source code, and hundreds of lines of comments - just download and open in your IDE!
A Java course for everyone - accessible yet serious, to take you from absolute beginner to an early intermediate level

Let’s parse that.

This is a Java course for everyone. Whether you are a complete beginner (a liberal arts major, an accountant, doctor, lawyer) or an engineer with some programming experience but looking to learn Java - this course is right for you.
The course is accessible because it assumes absolutely no programming knowledge, and quickly builds up using first principles alone
Even so, this is a serious Java programming class - the gradient is quite steep, and you will go from absolute beginner to an early intermediate level
The course is also quirky. The examples are irreverent. Lots of little touches: repetition, zooming out so we remember the big picture, active learning with plenty of quizzes. There’s also a peppy soundtrack, and art - all shown by studies to improve cognition and recall.
What's Covered:

Programming Basics: What programming is, and a carefully thought-through tour of the basics of any programming. Installing and setting up an IDE and writing your first program
The Object-Oriented Paradigm: Classes, Objects, Interfaces, Inheritance; how an OO mindset differs from a functional or imperative programming mindset; the mechanics of OO - access modifiers, dynamic dispatch, abstract base classes v interfaces. The underlying principles of OO: encapsulation, abstraction, polymorphism
Threading and Concurrency: A deep and thorough study of both old and new ways of doing threading in Java: Runnables, Callables, Threads, processes, Futures, Executors.
Reflection, Annotations: The how, what and why - also the good and bad
Lambda Functions: Functional constructs that have made the crossover into the mainstream of Java - lambda functions, aggregate operators.
Modern Java constructs: Interface default methods; properties and bindings too. Also detailed coverage of Futures and Callables, as well as of Lambda functions, aggregation operators. JavaFX as contrasted with Swing.
Packages and Jars: The plumbing is important to understand too.
Language Features: Serialisation; why the Cloneable interface sucks; exception handling; the immutability of Strings; the Object base class; primitive and object reference types; pass-by-value and pass-by-object-reference.
Design: The MVC Paradigm, Observer and Command Design Patterns.
Swing: Framework basics; JFrames, JPanels and JComponents; Menus and menu handling; Trees and their nuances; File choosers, buttons, browser controls. A very brief introduction to JavaFX.
Programming Drills (code-alongs, with source code included)

Serious stuff:
A daily stock quote summariser: scrapes the internet, does some calculations, and outputs a nice, formatted Excel spreadsheet.
A News Curation app to summarise newspaper articles into a concise email snippet using serious Swing programming
Simple stuff:
Support with choosing a programming environment; downloading and setting up IntelliJ.
Simple hello-world style programs in functional, imperative and object-oriented paradigms.
Maps, lists, arrays. Creating, instantiating and using objects, interfaces
Who is the target audience?

Yep! Folks with zero programming experience - liberal arts majors, doctors, accountants, lawyers
Yep! Engineering students from non-CS majors looking to learn fairly serious programming
Nope! Experienced Java programmers - this class will be boring for you:)
Yep! Computer Science students or software engineers with no experience in Java, but experience in Python, C++ or even C#. You might need to skip over some bits, but in general the class will still have new learning to offer you :-)
Basic knowledge
No prior programming experience needed:)
The class will make use of Java and an IDE - never fear, we have a detailed video to walk you through the process of setting this up
What will you learn
Write Java programs of moderate complexity and sophistication (at an early to middling intermediate level)
Understand Object-Oriented programming concepts at the level where you can have intelligent design conversations with an experienced software engineer
Manage concurrency and threading issues in a multi-threaded environment
Create and modify files (including Excel spreadsheets) and download content from the internet using Java
Use Reflection, Annotations, Lambda functions and other modern Java language features
Build serious UI applications in Swing
Understand the Model-View-Controller paradigm, the Observer and Command Design patterns that are at the heart of modern UI programming
Gain a superficial understanding of JavaFX and Properties and Bindings
Understand the nuances of Java specific constructs in serialisation, exception-handling, cloning, the immutability of strings, primitive and object reference types
To continue:

Complete Java Tutorial Step by Step - Become a Programmer

Complete Java Tutorial Step by Step - Become a Programmer

Simpliv LLC, a platform for learning and teaching online courses. We basically focus on online learning which helps to learn business concepts, software technology to develop personal and professional goals through video library by recognized industry experts and trainers.

Description
This course will help you to learn Java from grounds up, starting from the basic up to advance level, step-by-step. Not only video tutorials but the course contains lots of coding exercises that you will do to test your acquired knowledge practically side by side of watching video tutorials.

This course will help you to prepare for Job Interviews on Core Java language also you will gain confidence in doing programs using Java.

Who this course is for:

Anyone who want to build a career in programming, having Java in their curriculum in High School/ College
Basic knowledge
No prerequisite, a computer/laptop is needed only
What will you learn
Complete Java programming from the basic to the advance level
Input Output, Basic control structures - if-else, loops, switch-case
Methods, Strings, Arrays, ArrayList
Classes and Objects
Exception Handling

Know more: