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-pluginto achieve 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.javashaded-base.com.fasterxml.jackson.databind.DeserializationFeature.javawhere 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.
Maven-Shade-PluginNow 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.
maven-shade-pluginApart 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.
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.
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.