Skip to content

Creating a Shaded Jar

Liudmila Molkova edited this page Jan 10, 2022 · 4 revisions

Java applications of a certain complexity often run into dependency conflicts due to their nature of bringing in many external dependencies, each with their own set of transitive dependencies. One way of resolving this issue is to introduce a 'shaded Jar' - a single jar file including the library code, as well as the code of all of its dependencies, with the dependency package names renamed into a different namespace to avoid conflicts.

The following guide will shade your application using apache shade plugin. This plugin can be used to create a self-contained version of the application, which is a single Uber JAR that contains the application package and all of its dependency packages (both immediate and transitive dependencies). The same plugin can be used to relocate the conflicting dependency packages in this Uber JAR to prevent their path names from conflicting with those that ADB bring into the class-path. The resulting JAR after applying relocation to Uber JAR is called Shaded JAR.

Creating Uber JAR, example

To create Uber version of your application and to relocate conflicting Jackson dependency in the Uber JAR, add the following shaded plugin entry under <build><plugins> section of application POM.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass><!-- YOUR_APPLICATION_MAIN_CLASS --></mainClass>
          </transformer>
          <!--Transforms META-INF/services (essential if you relocate com.azure classes)-->
          <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
        </transformers>
        <finalName>${project.arifactId}-${project.version}-shaded</finalName>
          <filters>
            <filter>
              <artifact>*:*</artifact>
              <excludes>
              <exclude>META-INF/maven/**</exclude>
              <exclude>META-INF/*.SF</exclude>
              <exclude>META-INF/*.DSA</exclude>
              <exclude>META-INF/*.RSA</exclude>
            </excludes>
          </filter>
        </filters>
        <relocations>
          <relocation>
           <pattern>com.fasterxml.jackson</pattern>
           <shadedPattern>org.example.shaded.com.fasterxml.jackson</shadedPattern>
          </relocation>
          <relocation>
            <!--Environment like Databricks may bring its own version of azure-core which may be incompatible with your Azure client libraries.
                Relocate azure-core to avoid collisions with it-->
            <pattern>com.azure</pattern>
            <shadedPattern>org.example.shaded.com.azure</shadedPattern>
          </relocation>

          <!--In Databricks 10.2 you nay also need to relocate reactor netty packages-->
          <relocation>
            <pattern>io.netty</pattern>
            <shadedPattern>ms.shaded.io.netty</shadedPattern>
          </relocation>
          <relocation>
            <pattern>reactor</pattern>
            <shadedPattern>ms.shaded.reactor</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

then run:

mvn package

This will generate a shaded version of the application in target directory. For example: if your application POM has following maven co-ordinates defined:

<artifactId>adb-app</artifactId>
<version>1.0-SNAPSHOT</version>

then the shaded jar will be named as:

adb-app-1.0-SNAPSHOT-shaded.jar

This name is derived from the finalName entry in shaded plugin ${project.arifactId}-${project.version}-shaded.

The Jackson relocation configuration defined in the plugin will result in renaming the com.fasterxml.jackson package in the Uber JAR to com.microsoft.shaded.com.fasterxml.jackson and update all references to the classes from the original package.

Shading specific library

If you have a conflict within your application, shading dependency into the application JAR would not resolve a version conflict - it only forces a single shaded version of that dependency for all components.

In this case, you can create a new separate module wrapping library causing conflict directly or though one of its transitive dependencies and shading (relocating) conflicting dependency into it. The following steps show an example of shading and relocating Jackson libraries under a new JAR:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>${maven-shade-plugin-version}</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <!--Create shaded JAR only-->
        <shadedArtifactAttached>false</shadedArtifactAttached>
        <!--Remove original replaced dependencies-->
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <!--Promotes transitive dependencies of removed dependencies to direct-->
        <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
        <relocations>
          <relocation>
            <pattern>com.fasterxml.jackson</pattern>
            <shadedPattern>org.example.shaded.com.fasterxml.jackson</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>
Clone this wiki locally