How to Create a Fat Jar Using Maven

Maven is a popular build tool for Java projects. Using Maven it is possible specify all the third party dependencies of a project. During Java compilation, Maven adds these dependency library jars  automatically to the classpath. Maven can also be used to package a project as a distributable jar library. However when you package a project as a jar using maven, the dependent jars are not packaged. This means that in order to run the jar on a different machine, you need to provide all the dependent jars on the classpath.

The advantage of the above approach is that your distributable jars are small. However the downside is that the application won't run without providing the dependencies explicitly on the classpath. This is a problem with many cloud java runtime providers such as Amazon Web Services (AWS lambda) which expect the jar to be self contained with all dependencies.

Luckily Maven provides a plugin named Apache Maven Shade Plugin for creating a distributable jar with all the dependencies. Such a jar is know as a fat jar (also called uber jar) since it is substantially larger than the original. Maven Shade Plugin works by modifying the maven package task.

How to Create a Fat Jar Using Maven

To enable fat jar creation, just add the following xml snippet to your maven pom file. This will ensure that whenever you call the maven package task, all the dependent library code is also added to the generated fat jar.

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
</build>

Following are the major features of Apache Maven Shade Plugin,

  • It is possible to relocate the dependent classes to avoid conflicts with the environment where the jar is running. Shade plugin rewrites the entire byte code under the relocated package. This ensures that a completely private copy of the dependency jar classes are added to the project!
  • It is possible to filter classes or resources from the dependency jars. This gives fine grained control over how the fat jar is created.
  • Apache maven shade plugin also contains a number of resource transformers for merging resources from several dependency jars. For example, ApacheLicenseResourceTransformer can be used to prevent duplicate license files.
  • Apache maven shade plugin also has support for creating executable jar files.

Example: Generating Fat/Uber Jar Using Shade Plugin

Following is  a simple project which uses Apache commons lang library for string comparison. Following is our main class which uses the third party class StringUtils.

package com.quickprogrammingtips.demo.shadedemo;

import java.util.Scanner;

import org.apache.commons.lang3.StringUtils;

public class App {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Please enter your name: ");
        String name = scanner.nextLine();

        if (StringUtils.isBlank(name)) {
            System.out.println("Sorry, cannot proceed without a name!");
        }else {
            System.out.println("Hello "+name);
        }

        scanner.close();
    }
}

Following is our original pom file for the project. Notice the dependency of commons-lang3 library.

<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.quickprogrammingtips.demo</groupId>
    <artifactId>shadedemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>shadedemo</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
    </dependencies>
</project>

To create an ordinary jar, run the following command on the terminal. The jar file is created in the target subfolder.

mvn package

Now try to run our program from the terminal from the console (after switching to target folder),

java -cp shadedemo-0.0.1-SNAPSHOT.jar com.quickprogrammingtips.demo.shadedemo.App

Here is the output of the program,

Please enter your name: QPT

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils

    at com.quickprogrammingtips.demo.shadedemo.App.main(App.java:13)

Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils

    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)

    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)

    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

    ... 1 more

Our program throws a runtime exception since it not able to load the commons-lang3 classes. Let us now modify our pom.xnl file to use the apache maven shade plugin,

<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.quickprogrammingtips.demo</groupId>
    <artifactId>shadedemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>shadedemo</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Run the following command on the terminal to create the distributable fat jar,

mvn package

Now the run program again with the following command. Now the application works without any runtime errors!

java -cp shadedemo-0.0.1-SNAPSHOT.jar com.quickprogrammingtips.demo.shadedemo.App

Please enter your name: QPT

Hello QPT

Apache maven shade plugin also supports executable jar files. Let us modify the pom.xml again to configure an executable jar file,

<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.quickprogrammingtips.demo</groupId>
    <artifactId>shadedemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>shadedemo</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <transformers>
                        <transformer
                            implementation= "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>com.quickprogrammingtips.demo.shadedemo.App</mainClass>
                        </transformer>
                    </transformers>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Now you can directly run the jar file without specifying a class name,

java -jar shadedemo-0.0.1-SNAPSHOT.jar

To verify the inclusion of the dependent class files in the fat jar, you can extract the jar and inspect its contents.

contents-of-apache-shaded-jar