Maven Basics for Java Developers

Overview

Introduction

In this tutorial, we'll walk through basics of using Maven to create projects, compile Java source code, and package our application as a standalone .jar with dependencies embedded, a Windows .exe, a Mac .app, and a Debian or Ubuntu based .deb package. I'll also mention some common settings and my recommended plugins.

This tutorial assumes you already have the Java 8 JDK installed.

What is Maven?

Maven is a build tool that automates the compiling, dependency management, packaging, and even deployment of Java applications.

Why use Maven instead of compiling with javac?

It's good to know how to compile something manually using javac, but when projects get larger and require third-party libraries, it becomes tedious to manage dependencies and build scripts. Maven offers several benefits:

  • Generate pre-built templates for different types of projects
  • It's widely used and supported by the Java community and is an Apache project
  • Maven manages building the .jar, which requires manifest files and other tedious configuration if done by hand
  • Very extensible with many community plugins
  • Easy to run on the command line, and supported by all major IDEs.
  • Other people can easy load and build your project using Maven
  • Install packages to your local repository
  • Deploy packages to remote repositories

Here are some of the other cool things it can do for you with optional plugins:

  • Generate JavaDoc documentation in HTML format
  • Attach source code to a jar
  • Sign your code using GPG
  • Create Windows .exe files and installers
  • Create Mac .app files
  • Create Debian/Ubuntu based .deb packages

Maven alternatives

Ant

Maven is the sucessor of the Apache Ant build tool. I recommend to learn just enough Ant to run builds from projects that still use it, but not for new projects. It's generally as simple as running ant in the root folder with the build.xml file.

It is similar to the C Make program. Maven can run Ant tasks with the maven-antrun-plugin.

Gradle

Gradle is the newest build tool on the block. Android has adopted Gradle as their build tool, but a large portion of projects are still using Maven as well. Gradle has a much more compact syntax, which makes it attractive to many people. Even if you decide to move to Gradle later, it is worth understanding Maven well.

Installing Maven

Installing may not be necessary if your IDE comes with Maven bundled. For example, Maven comes bundled with NetBeans and IntelliJ IDEA. It is also worth noting that in Debian and Ubuntu based distributions you can simply install maven using apt. We will focus on manually installing Maven and using it from the command line in this tutorial.

# Install maven in Debian/Ubuntu based distributions using apt
sudo apt install maven

To install manually, first go to the Maven download page at https://maven.apache.org/download.cgi.

Download the zipped binary package and then unzip it. The bin directory of the unzipped Maven download will contain the primary tool mvn. Add the Maven bin directory to your PATH environment variable. Also set the JAVA_HOME environment variable to the root directory of your JDK.

Verify installation

To make sure it is installed properly, try running Maven from the terminal with:

# Check Maven version
mvn --version

Generate a new project with Maven using an archetypes

Maven has a concept called archetypes, which are essentially prebuilt project templates that include folder structure and files needed to get started for a specific type of project. There are tons of archetypes out there that come with templates for building things like command line apps, empty projects, gui apps, and web apps. You can generate a new project in interactive mode where you are prompted to answer a series of questions, or by passing in the arguments as command line options. There is at least one archetype worth memorizing and that is the maven-archetype-quickstart. This generates the simplest possible project with one source file ready for us to edit.

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

It will ask you to answer a few questions.

  • groupId - Your namespace. Your website, or commonly a GitHub account. Examples: com.devdungeon, com.github.nanodano
  • artifactId - Name of the package within your namespace(groupId). Example: mytools
  • version - Your desired version number. Default provided of 1.0-SNAPSHOT
  • package - Name of the package for the one generated .java file. Default of groupId provided.

It will create a directory with the name of the artifactId you provided with a skeleton project ready to go. There will be a pom.xml file in the root directory and the code inside src directory. Inside the src dir you will find a .java file with Hello World code. This is where you can start coding.

Optionally, if you want to skip the interactive mode, you can provide the command line arguments, like the groupId, artifactId, and version of your project as command line options like this:

mvn archetype:generate -DgroupId=com.github.nanodano -DartifactId=myproject -Dversion=1.0.0 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

The example just above also includes the optional -DinteractiveMode=false flag, which disables the prompt for you to press Y and review the information. Leave interactiveMode set to true if you do want to review the information before it creates the project.

Other simple archetypes

There are a few other archetypes worth mentioning:

  • maven-archetype-quickstart - Basic skeleton Java project
  • maven-archetype-plugin - Template for building a custom Maven plugin
  • maven-archetype-archetype - Template for building your own archetypes
  • maven-archetype-webapp - Template for building a simple servlet

Example usage of different archetypes

# Basic Java App template we just looked at
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

# Web app template
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

# Maven plugin template
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-plugin

# Maven archetype template
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-archetype
# Will list available archetypes to choose from
mvn archetype:generate

What is the pom.xml?

After generating a project with mvn archetype:generate there will be a pom.xml file in the root directory of the project. This is the Project Object Model and provides all the configuration for the build. This is the file where you specify what plugins to use and output file names, etc.

Running common Maven goals

Maven has several actions that can be run, named goals. Some are built in, like compile, while others are provided by optional plugins. Here are some of the most important commands you should know. You need to run these commands from the root directory of your project, the directory with the pom.xml file.

# Compile files (compiled .class files will be in the <strong>target</strong> directory.)
mvn compile

# Remove all files that were built by a Maven build
mvn clean

# package as jar (unless otherwise specified, can also do wars, ear, and others) output jar will be in the target dir
mvn package

# Package project and store it in your local repository for use in other projects
mvn install

# Package and push the jar to a remote repository like Maven Central for others to use
mvn deploy

# You can also run more than one goal in order
mvn clean package

# Download sources of dependencies
mvn dependency:sources

# Download javadocs of dependencies
mvn dependency:resolve -Dclassifier=javadoc

Other common settings in the pom.xml

You can also include additional information about your project including but not limited to: description of project, developer contact information, source control info, and licensing information.

<project>
    ...
    <!-- Additional information about project (optional) -->
    <description>This is my cool project</description>

    <developers>
        <developer>
            <name>Your Name</name>
            <email>youremail@example.com</email>
            <organization>Your Organization</organization>
            <organizationUrl>https://www.github.com/myusername</organizationUrl>
        </developer>
    </developers>

    <scm>
        <connection>scm:git:git://github.com/MyUsername/myproject.git</connection>
        <developerConnection>scm:git:ssh://github.com:MyUsername/myproject.git</developerConnection>
        <url>https://github.com/MyUsername/myproject/tree/master</url>
    </scm>

    <licenses>
        <license>
            <name>The Apache License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
        </license>
    </licenses>
    ...
</project>

Adding external dependencies to your project

To add external dependencies to your project, define a section for dependencies in the pom.xml file like this:

<project>
    ...
    <dependencies>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.4</version>
        </dependency>

    </dependencies>
    ...
</project>

Maven looks for dependencies in your default repository in your home directory in ~/.m2/repository. You can override the path of the local repostiory by modifying your settings.xml file in ~/.m2/repository to add a localRepository tag like this:

<!-- ~/.m2/settings.xml -->
<settings>
    <localRepository>/path/to/local/repo/</localRepository>
</settings>

You can even set up your settings.xml file to have different profiles that use different repositories and settings for different projects.

If you don't have a dependency or plugin needed when Maven runs a goal, it will automatically try to download it from the central repository. You can even push your own projects to maven central making them available to everyone on the internet.

Maven central is more secure than other central repositories like npm because the namespaces are controlled by owners and all code is required to be signed with a GPG key that is publicly available. Someone would have to gain control of the repository credentials and the private gpg key to sign code and push it to the central repo. Signing allows you to verify the code came from the same author you expect it to.

Read more about the Maven settings.xml file at https://maven.apache.org/settings.html and check out the central repository at https://search.maven.org.

Installing a jar file to local repository manually

There is a goal called install-file that will take a jar file and install it to your local repository.You do not need to be inside a project or have a pom.xml in your directory to run this command, all it requires is the jar you want to put in your repository.

# Install a jar file to your local repo
mvn install:install-file -Dfile=someLibrary.jar

Recommended, but optional plugins

Maven is built on a plugin architecture. even the built in commands like compile are still just plugins. you can override default plugin settings or enable third party plugins by creating entries in the project's pom.xml file. Here are some examples of plugins that are worth being familiar with. An example usage of each plugin is provided, but this is where you should put the plugins in the pom.xml:

<project>
    ...
    <build>
        <plugins>

            <!-- Add plugins here -->

        </plugins>
    </build>
    ...
</project>

After setting up these plugins, they will automatically hook in to the appropriate goal. For example, if you set up all of them, then running mvn package will automatically generate the javadoc and source jars, GPG sign your files, create the shaded jar, and package the .exe, .app, and .deb packages.

Maven Compiler Plugin - Specify Java version

<!-- maven-compiler-plugin - Compiler plugin is already included by default, we are just
        overriding the settings, telling it to compile to 1.8 Java target. -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>

Maven Source Plugin - Attach sources as JAR

<!-- maven-source-plugin - This plugin generates a jar with the source files attached.
        Required for pushing to Maven Central-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>3.0.1</version>
    <executions>
        <execution>
            <id>attach-sources</id>
            <goals>
                <goal>jar-no-fork</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Maven JavaDoc Plugin - Autogenerate documentation as JAR

<!-- maven-javadoc-plugin - Generates a jar with the autogenerated JavaDoc documentation.
        Required for pushing to Maven Central -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <id>attach-javadocs</id>
            <goals>
                <goal>jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Maven GPG Plugin - GPG sign your files

<!-- The maven-gpg-plugin - Signs the generated files with your GPG key
    leaving a .asc file that others can verify.
    Required for pushing to Maven Central. -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
                <goal>sign</goal>
            </goals>
            <configuration>
                <!-- GPG keyname variable set in the ~/.m2/settings.xml  file -->
                <keyname>${gpg.keyname}</keyname>
            </configuration>
        </execution>
    </executions>
</plugin>
<!-- Sample ~/.m2/settings.xml file with GPG key specified:
    <settings>
        <servers>
            <server>
                <id>ossrh</id>
                <username>myusername</username>
                <password>**********</password>
            </server>
        </servers>
        <profiles>
            <profile>
                <id>sonatype-oss-release</id>
                <properties>
                    <gpg.keyname>MyUserGpgKeyName</gpg.keyname>
                    <gpg.passphrase>**********</gpg.passphrase>
                </properties>
            </profile>
        </profiles>
    </settings> -->

Maven Jar Plugin - Create a JAR file

<!-- maven-jar-plugin - Included by default, but configuration can be overriden.
        Defines the main class for the jar -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>com.github.me.myproject.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

Maven Shade Plugin - Create a fat/uber/shaded JAR with dependencies

<!-- maven-shade-plugin - Create a "fat"/"shaded" jar with the dependencies included.
        Makes the jar file much larger, but standalone.-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <finalName>${project.artifactId}-${project.version}-shaded</finalName>
    </configuration>
</plugin>

Launch4j Maven Plugin - Create a Windows .exe file

 <!-- launch4j-maven-plugin - Create a Windows .exe with the jar inside. 
         JRE can optionally be embedded. -->
<plugin>
    <groupId>com.akathist.maven.plugins.launch4j</groupId>
    <artifactId>launch4j-maven-plugin</artifactId>
    <version>1.7.21</version>
    <executions>
        <execution>
            <id>l4j-clui</id>
            <phase>package</phase>
            <goals>
                <goal>launch4j</goal>
            </goals>
            <configuration>
                <headerType>gui</headerType><!--gui|console|jniGui32|jniConsole32-->
                <jar>${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar</jar>
                <outfile>${project.build.directory}/myapp.exe</outfile>
                <downloadUrl>http://java.com/download</downloadUrl>
                <classPath>
                    <mainClass>com.github.me.myproject.Main</mainClass>
                    <preCp>anything</preCp>
                </classPath>
                <icon>src/main/resources/myWindowsIcon.ico</icon>
                <jre>
                    <minVersion>1.6.0</minVersion>
                    <jdkPreference>preferJre</jdkPreference>
                </jre>
                <versionInfo>
                    <fileVersion>1.0.0.0</fileVersion>
                    <txtFileVersion>${project.version}</txtFileVersion>
                    <fileDescription>${project.name}</fileDescription>
                    <copyright>2018 Me</copyright>
                    <productVersion>1.0.0.0</productVersion>
                    <txtProductVersion>1.0.0.0</txtProductVersion>
                    <productName>${project.name}</productName>
                    <companyName>John Q Smith</companyName>
                    <internalName>myapp</internalName>
                    <originalFilename>myapp.exe</originalFilename>
                </versionInfo>
            </configuration>
        </execution>
    </executions>
</plugin>

AppBundle Maven Plugin - Create a Mac .app

<!-- sh.tak.appbundler - Create a Mac .app file -->
<plugin>
    <groupId>sh.tak.appbundler</groupId>
    <artifactId>appbundle-maven-plugin</artifactId>
    <version>1.2.0</version>
    <configuration>
        <mainClass>com.github.me.myproject.Main</mainClass>       
        <iconFile>myMacIcons.icns</iconFile>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>bundle</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Debian Maven Plugin - Create a Debian/Ubuntu .deb package

<!-- net.sf.debian-maven - Creates a .deb package for Debian/Ubuntu based distros 
        http://debian-maven.sourceforge.net/usage.html -->
<plugin>
    <groupId>net.sf.debian-maven</groupId>
    <artifactId>debian-maven-plugin</artifactId>
    <version>1.0.6</version>
    <configuration>
        <packageName>my-package</packageName>
        <packageVersion>1.0.0</packageVersion>
    </configuration>
</plugin>

Conclusion

With the knowledge you have now, you should be able to start working on projects confidently with Maven. There is plenty more to learn about Maven since it can be heavily customized and there are lots of plugins available online. Keep learning and refer to some of the links below for further reading.

Helpful Links