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
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
</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
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.
To see a live example, you can consult the pom.xml of one of my projects. Here is the GitHub repository:
A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms
Copyright (C) 2002-2022 Vincent A. Cicirello.
API documentation: https://chips-n-salsa.cicirello.org/api/
|Publications About the Library|
|Packages and Releases|
|JaCoCo Test Coverage|
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 .
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. For…
Follow me here on DEV:
Follow me on GitHub:
Vincent A Cicirello
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: