# Use Maven to manage your Java dependencies I've used dozens (hundreds?) of libraries that are well beyond my skill or interest as a programmer. For a lot of people, myself included, one of the things that makes a programming language "good" are the libraries available for it. An open source library represents hours and days you don't have to work on a problem that's either not central to your project or, conversely, so central that it would be otherwise out of reach. Better still, it's code you don't necessarily have to maintain (unless it's so important to you that you decide to contribute to it.) Open source libraries are a vital component of open source programming, and most programming languages have a convenient way to ensure they're easy to include in your codebase. There are several ways to manage libraries in Java, but the one I use is Maven. [Maven](http://maven.apache.org) is a code management framework that helps you keep track of dependencies, build targets, reporting, and documentation from a central location. That central location is a Project Object Model (POM) file, which is written in XML to describe the requirements and expectations of your project. When you decide to update a library, you can update just your `pom.xml` file. Should you add a debug build to your project, you add it as a target in `pom.xml`. When you upgrade to a new JVM, you do it in `pom.xml`. If that sounds easy, you'll be pleased to know that it's just as easy to get started. ## Install Maven is available on most Linux distributions from your package manager. On Fedora, Mageia, and similar distributions: ```bash $ sudo dnf install maven ``` On Elementary, Mint, and other Debian-based distributions: ```bash $ sudo apt install maven ``` On macOS, use [MacPorts](https://opensource.com/article/20/11/macports) or [Homebrew](https://opensource.com/article/20/6/homebrew-mac). On Windows, use [Chocolatey](https://opensource.com/article/20/3/chocolatey). ## Configuring On most systems, Maven is already configured after installation. If you have a complex Java setup, though, with different versions installed for different projects, then you may need to set your `JAVA_HOME` [environment variable](https://opensource.com/article/19/8/what-are-environment-variables). If you don't know what that is, read my article [Find and set your JAVA_HOME](LINK TO ARTICLE). Verify that Maven is installed and is using the version of Java you intend: ```bash $ mvn -version Apache Maven x.y.z Maven home: /usr/share/maven Java version: X.Y.Z, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-XX-openjdk-XX ``` ## Your first POM file Maven bases everything it does off of a project's POM file. You can think of `pom.xml` as Maven's equivalent of [Autotool's Makefile](https://opensource.com/article/18/8/what-how-makefile) or CMake's [CMakeLists.txt](https://opensource.com/article/21/5/cmake) file. You put instructions that are meaningful to Maven in `pom.xml` and it executes those instructions when prompted. A [good IDE](https://opensource.com/article/20/7/ide-java), like [Eclipse](https://opensource.com/article/19/10/cloud-ide-che) or [NetBeans](LINK TO MY NEW NETBEAN ARTICLE), might provide a boilerplate `pom.xml` file when creating a Maven project. Depending on how comfortable you are in XML, it might look more complex than it actually is. There's a distinct advantage to it being in XML, so have a look even if you're not familiar with XML. ```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>com.example</groupId> <artifactId>example4Maven</artifactId> <version>1.0</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> </project> ``` The first elements (`<?xml?>` and `<project>`) contain information about the file itself, identifying the document as XML written in accordance to the Maven POM schema. The next group of elements correspond to the Maven schema used and the project settings I provided to NetBeans. The `<packaging>` element specifies that this project is meant to be packaged up as a JAR file. The `<properties>` element tells Maven about the [as-yet non-existent] source code in my project. It's written in UTF-8 encoding for OpenJDK version 11. All of that was auto-generated by NetBeans for me when I created an example project. If you don't use an IDE that integrates with Maven, or you just prefer to write yours from scratch, you can find the skeleton outline of a POM file at [maven.apache.org/pom.html#Quick_Overview](https://maven.apache.org/pom.html#Quick_Overview). It's clearly commented, and has the XML declarations already populated. ## Adding a dependency to your POM Long ago when you wrote a Java application and decided to use an external library, you could download a JAR of the library, save it to your codebase, and then use an `import` statement to reference it in your Java code. It was a manual system, and while it worked as well as using, say, a C or C++ library, it sometimes got to be a lot of work to maintain. Maven uses repositories, much as Linux uses repositories for [software installation](https://opensource.com/article/21/2/linux-packaging), and how Python uses [pip](https://opensource.com/article/19/11/python-pip-cheat-sheet). The default repository is Sonatype's [Central Repository](https://central.sonatype.org), which you can search from [search.maven.org](https://search.maven.org). From Central, you can find libraries and the appropriate metadata you need to fill in the required dependency fields for Maven. In practise, though, most Java libraries provide that information in their README files or documentation. Suppose you have a simple Java script that lists files in a directory, but you decide you want it to list only files endingi in `jpg` or `png`. The Apache `fileUtils` library offers a filename filter for exactly that kind of operation, but it's not included in the Java distribution. ```java package com.example.example4maven; import java.io.File; import java.util.Iterator; import java.util.List; // external libs import org.apache.commons.io.FileUtils; public class Main { private static final File myDir = new File("/home/tux/img"); private static final String[] ext = new String[] { "jpg","png" }; public static void main(String[] args) { List<File> images = (List<File>) FileUtils.listFiles(myDir, ext, false); for (Iterator<File> i = images.iterator(); i.hasNext();) { System.out.println(i.next().getName()); } } } ``` You could search Central for the library, or you can visit the project's [website](https://commons.apache.org/proper/commons-io/dependency-info.html) and get the dependency info there. Dependency information looks like this: ```xml <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> ``` Enter that between `<dependencies>` and `</dependencies>` tags in your `pom.xml` file, and you've added the Apache `commons.io` library to your project. ```java <?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.example</groupId> <artifactId>example4Maven</artifactId> <version>1.0</version> <packaging>jar</packaging> <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>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> <type>jar</type> </dependency> </dependencies> </project> ``` Now when you build your project, you can have Maven automatically downloads any libraries you don't have in your local codebase. ## Build a JAR with Maven You can create JAR files with tools like [fastjar or gjar](https://opensource.com/article/21/8/fastjar), but Maven has plugins that enable it to perform common build tasks, including packaging. For a standard JAR package, which contains your source code, you can use `maven-jar-plugin`. To have Maven create an "Uber JAR" for you, which contains your code plus all the dependent libraries your code requires, you can use `maven-assembly-plugin`. The two plugins are similar in both purpose and configuration, and they're both build plugins so they're entered into a `<build>` section of your `pom.xml`. As with libraries, Maven metadata for plugins is available from the plugin's homepage. You include `maven-assembly-plugin` by listing its `groupId`, `artifactId`, and `version` between `<plugin>` tags. There are additional configuration options, including a set of preset behaviours, the ability to change the name of the output based on project name rather than your project's codename, and the ability to append your own entries into `MANIFEST.MF`. This is useful when your main class isn't called `Main.class`. To tell the plugin when it's meant to be invoked, you must also define what keyword in your Maven build triggers it. For `maven-assembly-plugin`, you probably want it to activate when you use `mvn package`. ```xml <build> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <finalName>Lister-${project.version}</finalName> <appendAssemblyId>false</appendAssemblyId> <archive> <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ``` Test your build: ```bash $ mvn clean package ``` After your project builds, you can find your Uber JAR in the auto-generated `targets` directory: ```bash $ java -jar targets/Lister-1.0.jar foo.png foo.jpg bar.jpg bar.png ``` It works as expected. ## Build with Maven Maven makes managing your Java project easy and resilient. With Maven, contributors can bootstrap their environment quickly, getting all the libraries they need quickly, and you can create predictable builds for your users. Its XML configuration keeps syntax simple and lintable, and it makes adding new options easy. Try Maven with your next Java project.