Creating executable jar file with Maven

Building an executable jar file with maven-jar-plugin is fairly easy. However, it has one disadvantage that the Maven dependencies are not packaged together inside the resulting jar file but have to be stored separately on the file system and added to the class path. Often it is not an issue but sometimes you may want to create a single self-contained jar file which does not depend on anything external. Below you can find an example how to do it.

Consider we have a very simple application with main method:

package com.example.runnablejar;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.codec.digest.DigestUtils;

public class Main {
    
    public static void main(String[] args) throws IOException  {
        if (args.length == 0) {
            System.err.println("Usage: cryptohash file1 ...");
            return;
        }
        
        for (String arg : args) {
            Path file = Paths.get(arg);
            calculateSha256CommonsIO2(file);
        }
    }
        
    private static void calculateSha256CommonsIO2(Path path) throws IOException {
        try (InputStream is = Files.newInputStream(path)) {
            String hashString = DigestUtils.sha256Hex(is);
            System.out.printf("SHA256(%s) = %s%n", path, hashString);
        }
    }

}

This application uses Apache Commons Codec library which is distributed as a separate jar file. Storing this jar file inside our application jar file would not work because it would not be present on the class path.

To overcome this issue we can use maven-assembly-plugin in POM 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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>runnablejar</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>runnablejar</name>

    <build>
        <plugins>      
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>           
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <appendAssemblyId>false</appendAssemblyId>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                            <archive>
                                <manifest>
                                    <mainClass>com.example.runnablejar.Main</mainClass>
                                </manifest>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>        
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
            <type>jar</type>
        </dependency>
    </dependencies>
</project>

Looking from the high level perspective the Apache Commons Codec jar will be merged together with our application jar and the new MANIFEST.MF file will be added:

$ jar tf runnablejar-1.0-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
(...)
org/apache/commons/codec/digest/DigestUtils.class
(...)
com/
com/example/
com/example/runnablejar/
com/example/runnablejar/Main.class
(...)

The final application can be run using following command:

$ java -jar runnablejar-1.0-SNAPSHOT.jar

without manually downloading any dependencies and without configuring class path.

As usual you can find the source code of the example at GitHub.

Advertisement

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Java, Maven and tagged , . Bookmark the permalink.

4 Responses to Creating executable jar file with Maven

  1. Jirka Pinkas says:

    Also even with Maven you can use good old export functionality in Eclipse (possibly with other IDEs as well).
    But your approach is much better when you want to create a JAR file using a script or from command line.

  2. John says:

    You can also take a look at Apache maven-shade-plugin. It provides the functionality to package the artifact into an uber-jar with all dependencies.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.