Photo by Jed Dela Cruz / Unsplash

Maven is a build automation tool used for Java projects. Typically, it manages project dependencies and application configuration. It provides predefined steps where specific tasks can be executed like run unit and integration tests,  package the application for product, generate the project documentation, etc...

In this post, we will see how to set up a Java project with Maven.

Prerequisites

To follow this tutorial, you need to have the JDK greater or equal to version 8. I use the JDK 11. Check out this link to see how the install it on your operating system.

Install Maven

Maven can be used on Linux, MacOS, Windows. I will show the installation process for MacOS.

Go to the download section of the Maven website and click on the link to download the compressed version of Maven.

Page to download the Maven CLI

Once the download is completed, let's unpack the file in the home directory:

# we are inside the download directory

tar -xvf apache-maven-3.8.3-bin.tar.gz -C $HOME

You will find a folder called apache-maven-3.8.3 in your home directory.

The next step is to register Maven in the environment variables path. The file containing these variables can be .bash_profile, .profile or .zshrc if you use Oh My Zsh as the custom shell. Identify yours and open it:

nano .bash_profile

Append the following lines at the end of the file :

export M2_HOME=$HOME/apache-maven-3.8.3
export PATH=$PATH:$M2_HOME/bin

Save and exit, then reload your profile file:

source .bash_profile

Now verify that your system recognizes the Maven command by running. mvn -v

Maven CLI successfully installed! We also see the JDK version installed.

Create a Java project from Maven Template

The Maven Template is also known as Archetype, is a project with a predefined structure. It is helpful in quickly bootstrap a project in an organization where many teams are working on a project and need a standard structure to stay consistent in all the projects.

This page list some Maven archetypes; for a simple project, the Archetype maven-archetype-quickstart is enough. The command signature to create a project is:

mvn archetype:generate 
	-DgroupId={project-packaging}
	-DartifactId={project-name}
	-DarchetypeArtifactId={maven-template} 
	-DinteractiveMode=false

The interactive mode set to false prevents Maven from asking questions about project configuration properties like the application version.

Let's create the project with the command below; you can replace the groupId and the artifactId on your own.

mvn archetype:generate -DgroupId=com.tericcabrel -DartifactId=starter -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

The project was created successfully, and here is the structure.

Structure of a simple Java project generated with Maven

If we open the file pom.xml , we will see that it contains one dependency called Junit used to run unit tests. Let's update it to the latest version and also set the JDK version to operate. The Maven will now look like this:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tericcabrel</groupId>
  <artifactId>starter</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>starter</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.4.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Run the command below to update the dependency:

mvn dependency:resolve

Since the Junit version has been updated, the imports in the file AppTests.java are broken. Replace the content of the file with the code below:

package com.tericcabrel;

import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;

/**
 * Unit test for simple App.
 */
public class AppTest {
    @Test
    public void testApp()
    {
        System.out.println("Hello world from test!");
        assertTrue( true );
    }
}

Run mvn install to make sure there are no errors.

Build and run the project

After writing the code in your project, you need to package and distribute it. Run the command below to do that:

mvn package

Maven will run the tests, and if all pass, it will package the application as a JAR and make it available in the folder called target. The file has the name starter-1.0-SNAPSHOT.jar

To execute the generated file, run the command below:

java -cp target/starter-1.0-SNAPSHOT.jar com.tericcabrel.App
Run the JAR file in the command line

Work with external dependency

Let's say we want to add a library to improve logging into the application. We will install SLF4J who is one of the most popular libraries.

Update the pom.xml to add these two dependencies required for SLF4J:

<dependencies>
    ......
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.32</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.32</version>
    </dependency>
</dependencies>

Run mvn dependency:resolve to install the dependency.

Update the App.java to make it look like this:

package com.tericcabrel;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;


public class App 
{
    static Logger logger = LoggerFactory.getLogger(App.class);

    public static void main( String[] args )
    {
        logger.info("Hello world!");
    }
}

Package the application and run the launch the generated .jar file; we got the output below:

Error due to missing dependency in packaged file

We got this error because Maven, by default, doesn't package external dependencies. We need to add a plugin that will achieve this task.

Apache Maven Shade Plugin

This plugin provides the capability to package the artifact in an uber-jar, including its dependencies, and shade - i.e., rename - the packages of some of the dependencies. The goals for the Shade Plugin are bound to the package phase in the build lifecycle.

Update the pom.xml by adding the code below inside the project section:

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>com.tericcabrel.App</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

We define the phase to package, then in the goal, we indicate we want to "shade" i.e., include all the external dependencies used in the project.

Finally, we define the transformer to use and also the main class of the application.

Package the application and run the launch the generated .jar file; we got the output below:

The application was executed successfully.

Yesss!  It works as expected 🎉.

You have a simple starter for your Java project with Maven. Can't wait to see what you will build with Java.

You can find the code source on the GitHub repository.

Follow me on Twitter or subscribe to my newsletter to not miss the upcoming posts and the tips and tricks I share every week.