How to Use the Maven Shade Plugin if Your Project Uses Java Platform Module System
Tags: java, tutorial, programming, maven
Published: 2022-10-05 14:18:36

The [Apache Maven Shade Plugin](https://maven.apache.org/plugins/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)](https://en.wikipedia.org/wiki/Java_Platform_Module_System) 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](#basic-configuration)
* [Configuration if You Use JPMS Modules](#configuration-if-you-use-jpms-modules)
* [Live Example](#live-example) from one of my projects on GitHub
* [Where You Can Find Me](#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:

```XML
<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. 

```XML
<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](https://github.com/cicirello/Chips-n-Salsa/blob/master/pom.xml) of one of my projects. Here is the GitHub repository:

{% github cicirello/Chips-n-Salsa %}


## Where You Can Find Me

Follow me here on DEV:

{% user cicirello %}

Follow me on GitHub:

{% github cicirello/cicirello %}

Or visit my website:

{% embed https://www.cicirello.org/ %}