Issue
In this Q&A post we'll try to answer the following questions -
- What is shading in maven?
- What is
maven-shade-plugin
, and what is used for ? - How to configure the
maven-shade-plugin
to achieve shading ?
Solution
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 -
com.fasterxml.jackson.databind.DeserializationFeature.java
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.
Answered By - ARK
Answer Checked By - Candace Johnson (JavaFixing Volunteer)