Overview
- Introduction
- What is Maven?
- Why use Maven instead of compiling with javac?
- Maven alternatives
- Installing Maven
- Verify installation
- Generate a new project with Maven using an archetype
- What is the pom.xml?
- Running common Maven goals
- Installing a jar file to local repository manually
- Other common pom.xml settings
- Adding dependencies to a project
- Specify resource directories
- Output effective pom.xml
- Running a class
- Recommended, but optional plugins
- Maven Compiler Plugin - Specify Java version
- Maven Source Plugin - Create a JAR with source files
- Maven JavaDoc Plugin - Autogenerate documentation JAR
- Maven GPG Plugin - GPG sign your pre
- Maven JAR Plugin - Create a JAR file
- Maven Shade Plugin - Create a fat/uber/shaded JAR
- Launch4j Maven Plugin - Create a Windows .exe
- AppBundle Maven Plugin - Create a Mac .app file
- Debian Maven Plugin - Create a Debian/Ubuntu .deb package
- Publish packages to Maven Central Repository
- Conclusions
- Helpful links
Introduction
In this tutorial, we'll walk through basics of using Maven to create projects, compile Java source pre, 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 pre to a jar
- Sign your pre 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.
# Debian/Ubuntun
sudo apt install maven
# Fedora
sudo dnf install maven
# Mac
brew 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 pre inside src directory. Inside the src dir you will find a .java file with Hello World pre. 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 pre 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 pre and push it to the central repo. Signing allows you to verify the pre 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.
Specify resource directories
Sometimes your application needs to include static resources like images or icons. You can specify special directories for Maven to look for resources. These resources will be packed in to the JAR. The default directory which requires no configuration is src/main/resources/.
To read and write files from the resource directory, you want to use a special function, java.lang.Class.getResource(). Here is a simple example:
java
// Get a URL that points to the file you want
java.net.URL filepath = getClass().getResource("/src/main/resources/test.txt");
// Then pass the URL to something that will open the file like `javax.imageio.ImageIO.read()`
// You can also use `java.lang.ClassLoader.getResourceAsStream()` to get a `java.io.InputStream`
java.io.InputStream instream = getClass().getResourceAsStream("/src/main/resources/largefile.bin");
// Then use something like `java.io.BufferedReader` to read the stream.
If you want to change or add more directories, you can add a configuration in the build section of your pom.xml.
<!-- In your pom.xml -->
<project>
<!-- ... -->
<build>
<!-- ... -->
<resources>
<resource>
<directory>src/main/extra-resources/</directory>
</resource>
<resource>
<directory>src/global/resources/</directory>
</resource>
</resources>
<!-- ... -->
</build>
<!-- ... -->
</project>
Read more on the official Oracle documentation about Location-independent Access to Resources.
Output effective pom.xml
When you use a very simple pom.xml file with no plugins, it is still loading some plugins by default without you specifying any. For example, the Maven compiler plugin is automatically included, and so is the Maven JAR plugin. Sometimes you want to override the default settings, like changing the compiler source code level or specifying the main class in a JAR.
To see your full effective JAR including all the default settings and plugins, you can print out the "effective" pom.xml file. This will let you grab the plugin XML so you can customize the configuration in your own pom.
# Print to stdout
mvn help:effective-pom
# Output to file
mvn help:effective-pom -Doutput-file=pom-effective.xml
Running a class
If you want to execute a specific class using Maven you can call mvn exec:java with the -Dexec.mainClass option, like this:
# Example usage
mvn exec:java -Dexec.mainClass=HelloWorld
# Another example
mvn exec:java -Dexec.mainClass=com.devdungeon.myapp.Main
This will let you run a class without having to worry about setting all of the class path for project classes and other dependencies. If you want to configure the main class for the packaged JAR, do that by configuring the Maven JAR plugin in your pom.xml
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>
Publish packages to Maven Central Repository
If you want to push your packages or applications to the Maven central repository there are a few steps:
- Register a namespace in Sonatype JIRA
- Package your project with GPG signature, javadoc, license info, etc.
- Use Maven to deploy to the repository
The full process is documented in its own tutorial: Publish Java Packages to Maven Central Repository.
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
- Official Maven documentation - https://maven.apache.org/index.html
- Maven Central repository - https://search.maven.org
- Maven Plugin Developers Centre - https://maven.apache.org/plugin-developers/index.html