0

In this Q&A post we'll try to answer the following questions -

  1. What is shading in maven?
  2. What is maven-shade-plugin, and what is used for ?
  3. How to configure the maven-shade-plugin to achieve shading ?
ARK
  • 3,734
  • 3
  • 26
  • 29

1 Answers1

1

1. What is Shading ?

In the context of maven, shading is a process by which you can change the package name of certain dependencies your project relies on. One of the primary reasons to do this is to get around dependency version conflicts.

Think about this - Your project relies on a specific version of a dependency like com.fasterxml.jackson:jackson-databind - v2.9.10. You build a uber jar packaging the dependency in your service's artifact. Now, let's say the host on which you run your service provides a runtime environment which provides com.fasterxml.jackson:jackson-databind - v2.6.7.

We've got a problem. With two versions of the same library on the classpath, we're going to run into runtime errors and unpredictable behavior. For example, your code calls FAIL_ON_TRAILING_TOKENS field in v2.9.10 of com.fasterxml.jackson:jackson-databind, but that does not exist in com.fasterxml.jackson:jackson-databind - v2.6.7. So essentially, we've got two versions of com.fasterxml.jackson.databind.DeserializationFeature.java class. The runtime is not gonna know which one to pull, and in such a situation most likely you'll see the following error during runtime -

    java.lang.NoSuchFieldError: FAIL_ON_TRAILING_TOKENS

And thats not good. When running your code in production, you want it to be predictable and under your control.

So, how do we get around this problem? Shading to the rescue. By shading we change the "fully qualified name" of the DeserializationFeature.java class - so instead of having two versions of com.fasterxml.jackson.databing.DeserializationFeature.java class, we'll have two different classes -

  1. com.fasterxml.jackson.databind.DeserializationFeature.java
  2. shaded-base.com.fasterxml.jackson.databind.DeserializationFeature.java

where shaded-base is the prefix the maven-shade-plugin will apply to the com.fasterxml.jackson:jackson-databind:2.9.10 artifact and all the classes within it. Let's see how it's actually done.


2. Maven-Shade-Plugin

Now that we're briefly familiar what shading is, and when to use it, let's drill a little bit into the maven-shade-plugin itself.

2.a) Building an uber jar using maven-shade-plugin

Apart from shading, maven-shade-plugin can be used to build an uber jar, also known as "fat" jar. An uber jar is a jar in which you can package pretty much all the dependencies needed for your project.

Let's start with an example. Check the <dependencies> required for the lake-tahoe project. By using the maven-shade-plugin we package the dependencies in the final artifact of the lake-tahoe project. You can do it yourself. Simply clone the repository, and run the following commands -

➜  lake-tahoe (master) mvn clean install

That's going to create lake-tahoe-<version>-SNAPSHOT.jar in the target/ directory. Now, let's check if the jackson files are "packaged" in that artifact; We'll check for the DeserializationFeature.java file for the purposes of this post.

➜  lake-tahoe (master) jar tf target/lake-tahoe-1.0.2-SNAPSHOT.jar | grep DeserializationFeature
com/fasterxml/jackson/databind/DeserializationFeature.class

Yup. We see that the maven-shade-plugin packages the dependencies of the project in the uber jar.


2.b) Shading the dependencies.

We can use <relocations> element of the plugin to shade or "re-package" the dependencies. Check the truckee-river project's pom.xml configuration to see how we accomplish that. Clone the repo and run the following commands -

truckee-river (master) mvn clean install

Let's inspect the contents of the truckee-river's uber jar.

➜  truckee-river (master) ✗ jar tf target/truckee-river-1.0.2-SNAPSHOT.jar | grep DeserializationFeature
com/water/bodies/fasterxml/jackson/databind/DeserializationFeature.class

As you can see, all the classes in com.fasterxml.jackson have been "relocated" to com.water.bodies.fasterxml.jackson. So, that's shading.


2.c) Using two versions of the same library with shading

Let's take a look at the lake-pyramid project. It pulls in v2.6.7 of com.fasterxml.jackson:jackson-databind library, and it also pulls in truckee-river project. As we saw in #2.b section, truckee-river pulls in the lake-tahoe artifact, which in turn packages v2.9.10 of com.fasterxml.jackson:jackson-databind. However, truckee-river project shades the com.fasterxml.jackson and relocates it, in effect creating a new "fully qualified class".

Clone lake-pyramid and you can see for yourself that both the shaded and the v2.6.7 version are packaged in lake-pyramid's final artifact.

➜  lake-pyramid (master) mvn clean install
// creates the `lake-pyramid-1.0.0-SNAPSHOT.jar`
(base) ➜  lake-pyramid (master) jar tf target/lake-pyramid-1.0.0-SNAPSHOT.jar | grep DeserializationFeature
com/water/bodies/fasterxml/jackson/databind/DeserializationFeature.class
com/fasterxml/jackson/databind/DeserializationFeature.class

So, that's pretty much it. Try the examples yourselves locally and you'd feel more comfortable.

Trivia: The Truckee river is the sole outlet of Lake Tahoe and drains part of the high Sierra Nevada, emptying into Pyramid Lake in the Great Basin.

ARK
  • 3,734
  • 3
  • 26
  • 29
  • I appreciate the efforts in your answer. A few points: `lake-tahoe (master)` and the like is part of the prompt, not part of the command. Usual characters like `$` or `>` in between would make this clearer. The bold arrows "➜" are too much. Code blocks are formatted easily recognizable already. "✗" is not a usual prompt character. If you'd use line continuation "\" one hasn't to scroll horizontally in the code blocks that much. – Gerold Broser Nov 02 '21 at 13:26