Creating a Fat Jar using maven and gradle

It’s very common that we might need to create a Java application and generate the jar executable, but we need to use third-party libraries that are also needed as part of our application. So, one solution for that scenario is to create a fat/uber jar.

Basically, a fat/uber jar is a self-sufficient file that contains all the classes and dependencies to run the application. In other words, a unique jar that contains everything, therefore, we don’t need to take care of sharing, copying and placing third party libraries with out executable.

This approach is very useful for desktop applications and also to micro-services in Java.

Maven

With maven, we can achieve that using the plugin maven-shade-plugin and to the phase package we set the goal shade. Optionally, using the same plugin we can indicate the main class for the application.

<project>
    <dependencies>
         ...
    </dependencies>
	<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.orthanc.mypackage.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Gradle

With Gradle is even easier, we only need to use the jar plugin included as part of the tool. Optionally, we can also indicate the main-class for our application.

dependencies {
  //my dependencies
}

jar {
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    manifest {
        attributes "Main-Class": "com.orthanc.mypackage.Main"
    }
}

There are some snipped code on internet and stackOverflow using configurations.compile.collect . However it doesn’t work properly with the transitive and module dependencies, when the dependency is added using implementation or api instead of compile. Gradle 6 has marked the compile scope as deprecated and Gradle 7 is not supporting it anymore.

Therefore, using configurations.runtimeClasspath.collect is a better approach and includes the transitive and module dependencies.

References

Leave a Reply

Your email address will not be published. Required fields are marked *