NeuroAgent

Complete Guide: Executable JAR with Maven

Master creating executable JARs with Maven using Shade, Assembly, or Exec JAR plugins. Includes configuration examples and troubleshooting tips for seamless distribution.

Question

How can I create an executable JAR with all dependencies included using Maven for easy distribution of my Java application?

NeuroAgent

To create an executable JAR with all dependencies included using Maven, you can use either the Maven Assembly Plugin, Maven Shade Plugin, or Maven Exec JAR Plugin. The most common approach is using the Maven Shade Plugin, which creates a “fat JAR” by packaging all dependencies into a single executable JAR file, allowing you to run your application with a simple java -jar your-app.jar command.

Contents


Using Maven Shade Plugin

The Maven Shade Plugin is the most popular choice for creating executable JARs with all dependencies included. It packages all dependencies into a single JAR file and can also create an executable manifest.

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.YourMainClass</mainClass>
                            </transformer>
                        </transformers>
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

The Shade Plugin offers additional features like merging service files and handling duplicate classes through the <filters> section.


Using Maven Assembly Plugin

The Maven Assembly Plugin is another option that creates distribution packages including your JAR and all dependencies in a directory structure.

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.7.1</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.YourMainClass</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

The Assembly Plugin creates a JAR with “-with-dependencies” suffix, which includes all dependencies in the same structure as they appear in your project.


Using Maven Exec JAR Plugin

The Maven Exec JAR Plugin is simpler and focuses specifically on creating executable JARs with proper classpath configuration.

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>com.example.YourMainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>3.6.1</version>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>false</overWriteSnapshots>
                        <overWriteIfNewer>true</overWriteIfNewer>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

This approach creates a separate lib directory for dependencies rather than packaging them inside the JAR, which can be useful for debugging and dependency management.


Configuring the Main Class

Regardless of which plugin you choose, you need to specify the main class that should be executed when the JAR is run. This is configured in the manifest:

xml
<manifest>
    <mainClass>com.example.YourMainClass</mainClass>
</manifest>

Alternatively, you can specify the main class in your pom.xml properties:

xml
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <mainClass>com.example.YourMainClass</mainClass>
</properties>

Important: Make sure the main class you specify actually exists in your project and has a public static void main(String[] args) method.


Common Issues and Solutions

Duplicate Classes

When using the Shade Plugin, you might encounter duplicate class conflicts. You can handle this with:

xml
<configuration>
    <filters>
        <filter>
            <artifact>com.example:conflicting-library</artifact>
            <includes>
                <include>com/example/RequiredClass.class</include>
            </includes>
        </filter>
    </filters>
</configuration>

Security Files Exclusion

The Shade Plugin might fail due to signature files. Exclude them:

xml
<filters>
    <filter>
        <artifact>*:*</artifact>
        <excludes>
            <exclude>META-INF/*.SF</exclude>
            <exclude>META-INF/*.DSA</exclude>
            <exclude>META-INF/*.RSA</exclude>
        </excludes>
    </filter>
</filters>

Manifest Configuration Issues

Ensure your manifest is properly configured with the main class. You can verify the manifest content using:

bash
jar tf your-app.jar | grep META-INF/MANIFEST.MF

Best Practices

  1. Use the Shade Plugin for true fat JARs - It’s the most reliable way to create a single executable JAR with all dependencies.

  2. Exclude unnecessary files - Remove signature files and META-INF entries to reduce JAR size and avoid conflicts.

  3. Use appropriate Java version - Ensure your Maven configuration matches the Java version you’re using for development.

  4. Test your JAR - Always test the generated JAR on the target deployment environment before distribution.

  5. Consider native images - For performance-critical applications, consider using GraalVM to create native executables instead of JARs.

  6. Use dependency management - Properly manage your dependencies in the dependencyManagement section of your pom.xml to avoid version conflicts.


Building and Running Your JAR

Building the JAR

Once configured, build your executable JAR using Maven:

bash
mvn clean package

The generated JAR will typically be in the target/ directory with a name like your-app-1.0.0.jar or your-app-1.0.0-jar-with-dependencies.jar.

Running the JAR

Run your executable JAR with:

bash
java -jar target/your-app-1.0.0-jar-with-dependencies.jar

Verifying Dependencies

You can inspect the contents of your JAR to verify dependencies are included:

bash
jar tf target/your-app-1.0.0-jar-with-dependencies.jar | head -20

For more detailed inspection:

bash
jar -tf target/your-app-1.0.0-jar-with-dependencies.jar | grep com/example

Conclusion

Creating an executable JAR with all dependencies included using Maven is straightforward with the right plugin configuration. The Maven Shade Plugin is generally the best choice for creating true fat JARs, while the Assembly Plugin offers more flexibility in packaging options, and the Exec JAR Plugin provides a simpler approach with separate dependency directories.

Key recommendations:

  • Use the Maven Shade Plugin for most applications requiring a single executable file
  • Always configure the main class properly in your manifest
  • Test your JAR thoroughly before distribution
  • Consider the trade-offs between different approaches based on your deployment needs

By following these practices, you can easily distribute your Java applications as self-contained executable JAR files that run on any system with Java installed.

Sources

  1. Apache Maven Shade Plugin Documentation
  2. Apache Maven Assembly Plugin Documentation
  3. Apache Maven JAR Plugin Documentation
  4. Java Executable JAR Best Practices