The Apache Maven Shade Plugin is perhaps the easiest way to produce a jar-with-dependencies
, a single jar file containing everything your Java application needs to run, including all of its dependencies. These are sometimes also referred to as "uber jars" or "fat jars." There are some issues that may arise though if you are not careful in how you configure it. For example, if your project uses Java Platform Module System (JPMS) modules, then if you are not careful you may end up with a jar-with-dependencies
that doesn't function for those who wish to consume it in their projects.
My intention in this post is not to encourage the use of such uber jars. Ideally, consumers of your library should use a tool for dependency management, such as build tools like Maven (or Gradle) that handles importing transitive dependencies. However, for those that don't use a dependency management tool, offering them a jar-with-dependencies
provides a convenient option to ensure that they download everything they need. Another scenario where I see a potential benefit is for applications, where distributing your application as a single jar file simplifies things for your users.
Table of Contents: The remainder of this post is organized as follows:
- Basic Configuration
- Configuration if You Use JPMS Modules
- Live Example from one of my projects on GitHub
- Where You Can Find Me
Basic Configuration
This is not a general tutorial on Maven. I'm assuming that you know the basics of configuring your Maven pom.xml
. Here is a minimal example of configuring the Maven Shade Plugin to build a jar-with-dependencies
during the package
phase. Someplace inside of <build><plugins>
and </plugins></build>
insert the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jar-with-dependencies</shadedClassifierName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
Now whenever you run mvn package
or any other command that includes the package
phase, in addition to the regular jar
, you will get a jar-with-dependencies
.
Configuration if You Use JPMS Modules
First, be aware that if your project uses JPMS modules that the resulting jar-with-dependencies
will only be usable on the classpath. In fact, to get it to work, we need to filter out the module-info.class
of our library, as well as the module-info.class
of every dependency. Why? There is a one-to-one mapping of JPMS module to jar file. The JPMS specification doesn't allow a single jar to contain multiple JPMS modules. If we attempt to use the configuration in the previous section above, our jar-with-dependencies
will include one module-info.class
file, either from our library (or application) or from one of its dependencies. We'll essentially end up with whichever module-info.class
is the last that the Maven Shade Plugin attempts to include.
The problem with having an arbitrarily chosen module-info.class
is that it controls what Java packages are exported from the module, as well as what Java packages are required by the module. To deal with this, we need to configure the Maven Shade Plugin to exclude all of the module-info.class
files. The Maven Shade Plugin provides a filtering feature that we can use to accomplish this. So adjust the configuration to use a filter to exclude the module-info.class
file from all artifacts that you are combining into the jar-with-dependencies
with the following.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jar-with-dependencies</shadedClassifierName>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
With the above, the jar-with-dependencies
can be used by consumers of our library on the classpath in a non-JPMS project. The regular jar file is not affected by this, and will continue to include the module-info.class
that defines our JPMS module, enabling appropriate use within JPMS projects.
Live Example
To see a live example, you can consult the pom.xml of one of my projects. Here is the GitHub repository:
cicirello / Chips-n-Salsa
A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms
Copyright (C) 2002-2024 Vincent A. Cicirello.
Website: https://chips-n-salsa.cicirello.org/
API documentation: https://chips-n-salsa.cicirello.org/api/
How to Cite
If you use this library in your research, please cite the following paper:
Cicirello, V. A., (2020). Chips-n-Salsa: A Java Library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms. Journal of Open Source Software, 5(52), 2448, https://doi.org/10.21105/joss.02448 .
Overview
Chips-n-Salsa is a Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms. The library includes implementations of several stochastic local search algorithms, including simulated annealing, hill climbers, as well as constructive search algorithms such as stochastic sampling. Chips-n-Salsa now also includes genetic algorithms as well as evolutionary algorithms more generally. The library very extensively supports simulated annealing. It includes several classes for representing solutions to a variety of optimization problems…
Where You Can Find Me
Follow me here on DEV:
Follow me on GitHub:
Vincent A Cicirello
View My Detailed GitHub Activity
If you want to generate the equivalent to the above for your own GitHub profile, check out the cicirello/user-statistician GitHub Action.
Or visit my website:
Top comments (0)