Sometimes you need or want to include 3rd party dependencies into your own jar file e.g. to provide your users with
an uber jar. These dependencies can additionally be repackaged to avoid any potential classpath issues. There exist
various plugins for build systems like gradle or maven to achieve this:
Both plugins also offer a simple minimization feature, which tries to shrink the 3rd party dependencies as much as
possible. For this purpose a small and nice library is being used that is
able to analyse transitive dependencies of class files.
This already leads to some good results. Lets take a simple example. In my project Units of Mesurement
I have the need to include some code from other projects, namely guava and hibernate validator.
These two projects contain some nice utility classes that I would like to use in my own library:
In case only such a limited number of classes / methods are being used, copy&pasting the relevant code to your project is a viable option.
But I wanted to explore if the minimize feature of the shadow jar plugin can help me without copying the code which is tedious
and makes it more difficult to integrate potential bugfixes (apart from the testing coverage which might drop for your own code).
So I added a configuration like that to my project to add the 2 libraries as dependency, adding a relocate config,
and enable the standard minimize feature:
The resulting jar contains the following classes from the 2 libraries:
in total 131 classes:
This would definitely be too much, and I would prefer to copy&paste the relevant classes to my own library and update
my NOTICE.txt file accordingly. I have worked for several years on a tool called ProGuard,
so I was pretty sure that we can do better than that. Due to license problems (ProGuard is licensed under GPL) I decided to
use R8 to extend the shadow jar plugin to use a full blown shrinker to improve the results
of its minimization feature. R8 is the default minimization tool of the Android SDK and licensed under Apache v2.0 license.
The result of this proof-of-concept can be found in this pull request for the
shadow jar plugin. Using R8 instead of just simply collecting transitive class dependencies results in a shaded jar that
contains only this number of shaded classes:
Most of them are inner classes of the ConcurrentReferenceHashMap which are simply needed and can not be shrunk.
This result is certainly much better and would allow me to go the route of shading off-the-shelf dependencies into my
own library rather than copy&pasting code.
If you would like to see this feature in the shadow jar plugin, star this issue.
The test that I made with my own library can be found here.
Note: I am of course perfectly aware that under normal circumstances it is not advised to shade any 3rd party dependency as
it might result in duplicated code being loaded by consumers of your library. In this specific use-case I think a technique like
this makes sense as I want to include a very small subset of a larger dependency without cutting the ties to upstream releases of
these dependencies (and potentially using more features).
No comments found for this article.
Join the discussion for this article on this ticket. Comments appear on this page instantly.