Reducing JAR Footprint with Maven Shade Plugin 3.2.2

Complex Java applications often consist of lots of JAR files. Particularly when it comes to open source projects, there is a deep “swamp” of transitive libraries your (possibly rather small) own code depends on. It might be that you do not care initially, but when it comes to deployment, chances are good that you will. At least your customers running that application in elastic clouds environments might not like the idea to have hundres of possibly huge JARs loaded into RAM again and again by more and more replicas of a kubernetes pod. That’s roughly the point when you start to think about squeezing excess material out of your box.

The Maven Shade Plugin

A possible solution is using the Maven Shade Plugin, in particular the plugin’s minimizeJar goal. It is a standard tool which, roughly spoken, takes all content from all JARs (yes, including transitive dependencies) and puts it all into one common target JAR. But not enough, while doing that it filters out anything not needed. This means, yes, it really understands your Java binary code, and, thanks to some fixes I contributed in the past months, the recently published version 3.2.2 even understands how to deal with loosely couples services (aka ServiceLoader). So this new release is able to even scan complex frameworks like Jersey.

Test Drive

To proof the usability of this new release, I set up a small hello world REST application using JAX-RS and Jersey, and added the following shading configuration:

<plugin>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.2.2</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <minimizeJar>true</minimizeJar>
        <filters>
          <filter>
            <excludeDefaults>false</excludeDefaults>
            <artifact>*:*</artifact>
            <includes>
              <include>org/glassfish/json/JsonProviderImpl</include>
              <include>com/sun/xml/bind/v2/model/nav/ReflectionNavigator</include>
            </includes>
            <excludes>
              <exclude>**/*.md</exclude>
              <exclude>**/*.markdown</exclude>
              <exclude>**/*.header</exclude>
              <exclude>**/*.xml</exclude>
              <exclude>**/pom.properties</exclude>
              <exclude>**/io.netty.versions.properties</exclude>
              <exclude>**/*.args</exclude>
              <exclude>**/*.so</exclude>
            </excludes>
          </filter>
        </filters>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>HelloWorldBootstrap</mainClass>
          </transformer>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
        </transformers>
      </configuration>
    </execution>
  </executions>
 </plugin>

It is essentials to really use version 3.2.2 of this plugin, so we explicitly provide that particular version here. Next we declared that we want the shading happing in the package phase of the project lifecycle. Then you’ll find the minimizeJar option being enabled, which is the essence of what this blog post talks about: Tree shaking with JARs. In my particular case, in addition to the tree shaking, I wanted to remove more stuff which the shade plugin doesn’t touch normally: Resource files. Also I wanted to keep stuff which the plugin would other discard: Classes which are actually not referenced otherwise. How could that happen? This is because Jersey uses their string names instead of their class constants to load them. The plugin cannot know what a string is good for, so I added these explicit includes. For services this is no needed, because I extended the shade plugin in 3.2.2 with the ability to discover services — which is, what makes it so easy to shade Jersey with its lots of service-loaded modules. But what we do need is to prevent our includes to override the default includes, which is what the excludeDefaults option does that I also added in 3.2.2. And we need to tell the shade plugin that is must combine all service metafiles into a single one, which is what the secondfilter does. The first simply makes the final JAR executable.

So the outcome is…

7261 [INFO] --- maven-shade-plugin:3.2.2:shade (default) @ helloworld ---
7608 [INFO] Including jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6 in the shaded jar.
7611 [INFO] Including org.glassfish.jersey.core:jersey-server:jar:2.30 in the shaded jar.
7612 [INFO] Including org.glassfish.jersey.core:jersey-common:jar:2.30 in the shaded jar.
7612 [INFO] Including org.glassfish.hk2:osgi-resource-locator:jar:1.0.3 in the shaded jar.
7613 [INFO] Including com.sun.activation:jakarta.activation:jar:1.2.1 in the shaded jar.
7613 [INFO] Including org.glassfish.jersey.core:jersey-client:jar:2.30 in the shaded jar.
7613 [INFO] Including org.glassfish.jersey.media:jersey-media-jaxb:jar:2.30 in the shaded jar.
7614 [INFO] Including jakarta.annotation:jakarta.annotation-api:jar:1.3.5 in the shaded jar.
7617 [INFO] Including org.glassfish.hk2.external:jakarta.inject:jar:2.6.1 in the shaded jar.
7617 [INFO] Including jakarta.validation:jakarta.validation-api:jar:2.0.2 in the shaded jar.
7617 [INFO] Including jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.2 in the shaded jar.
7618 [INFO] Including jakarta.activation:jakarta.activation-api:jar:1.2.1 in the shaded jar.
7618 [INFO] Including org.glassfish.jersey.inject:jersey-hk2:jar:2.30 in the shaded jar.
7622 [INFO] Including org.glassfish.hk2:hk2-locator:jar:2.6.1 in the shaded jar.
7625 [INFO] Including org.glassfish.hk2.external:aopalliance-repackaged:jar:2.6.1 in the shaded jar.
7626 [INFO] Including org.glassfish.hk2:hk2-api:jar:2.6.1 in the shaded jar.
7626 [INFO] Including org.glassfish.hk2:hk2-utils:jar:2.6.1 in the shaded jar.
7626 [INFO] Including org.javassist:javassist:jar:3.25.0-GA in the shaded jar.
7627 [INFO] Including org.glassfish.jersey.containers:jersey-container-netty-http:jar:2.30 in the shaded jar.
7627 [INFO] Including org.glassfish.jersey.connectors:jersey-netty-connector:jar:2.30 in the shaded jar.
7627 [INFO] Including io.netty:netty-all:jar:4.1.31.Final in the shaded jar.
7629 [INFO] Including org.glassfish.jersey.media:jersey-media-json-binding:jar:2.30 in the shaded jar.
7629 [INFO] Including org.glassfish:jakarta.json:jar:1.1.5 in the shaded jar.
7629 [INFO] Including org.eclipse:yasson:jar:1.0.3 in the shaded jar.
7631 [INFO] Including jakarta.json.bind:jakarta.json.bind-api:jar:1.0.2 in the shaded jar.
7633 [INFO] Including jakarta.json:jakarta.json-api:jar:1.1.5 in the shaded jar.
...
14383 [INFO] Minimized 6074 -> 4079 (67%)
...

A reduction from 26 JARs to one single JAR holding only the actually needed stuff, and a strong reduction of size by 33% is pretty amazing. And it was done without any proprietary frameworks or magic, just by one official Maven plugin! Who knows what future will bring to proprietary frameworks – we know it for Apache Maven: It’s here to stay since decades.

At this point I want to say thank you to Robert, Karl-Heinz, Hervé and Mark from Apache who helped me to add these features to the plugin. And I want to encourage everybody to not simply adopt a proprietary framework (even when it open source), but to think first always: Do I need that? Isn’t it simpler to just use JDK + Maven?

Conclusion

While the plugin is not really perfect in sorting out all kinds of includes and excludes, the recent version 3.2.2 is already pretty good in dealing with loosesly coupled frameworks. If your target is to reduce footprint of your delivered classes, it is definitively worth a try – particularly because it is an official part of the de-facto build standard Maven.

 

Posted in Java, Programming, Open Source | Tagged ,

Lightweight JAX-RS Live Coding Video on YouTube (EclipseCon Europe 2019)

If you really missed all of my live coding sessions I gave on my “Lightweight Microservices with JAX-RS 2.2” preview tour (JavaLand, Java Forum Stuttgart, EclipseCon Europe, etc.), there now is a high quality video recording from EclipseCon Europe 2019 on YouTube. Have fun trying at home, and stay tuned for the upcoming actual final release (recently named to “Jakarta RESTful Web Serivces 3.0”)!

Aside | Posted on by | Tagged , , , ,

Microsoft® Java®: Welcome at OpenJDK!

Microsoft officially is an OpenJDK contributor now, having a cool team on board with names like Bruno Borges, Reza Raman, Ed Burns and Martijn Verburg. That’s amazing, particularly if you know the troublesome history of both! So finally the Java platform found the steward Oracle never had been? Let’s see what happens. Maybe we now have the power to make Java great again?

Posted in Allgemein, Java, Microsoft, Open Source | Tagged , ,

WT:Socal – Getting rid of F*book

Say goodbye to F*book, here is the new trend: A social network inspired by open source and powered by Wikipedia. That’s how it should have been done since the beginning! Join now!

Posted in Allgemein, Projects | Tagged

EclipseCon Europe 2018: Video on YouTube

Forgot to tell you… the video recording of my presentation in 2018 at EclipseCon Europe is available on YouTube.

Posted in Cons, Jakarta EE, Java, Open Source, Programming, Standards | Tagged ,

JAX-RS 2.2: Reworking the Spec

Last week Christian and I committed the initial contribution of the JAX-RS Spec into the official repo on Github. You can find the source code incl. Maven Build in the subfolder jaxrs-spec. At the moment we’re rather busy to bring it in the form requested by the officials. If you like to contribute, pick an issue from our list, and let us know you’re working on that by commenting that issue. If everything works well (there still are open questions to be answered by the EF) we soon have a nightly build of the JAX-RS 2.2-SNAPSHOT specification. This is really exciting, as it is the first time the open source community is working together on new features in a former Oracle-authored spec. Stay tuned! 🙂

Posted in Jakarta EE, Java, Open Source, Programming, Standards | Tagged ,

JakartaOne 2019: Slides on Speakerdeck

You can now find the slides of my video contribution to JakartaOne 2019 on Speakerdeck. 🙂

Posted in Cons, Jakarta EE, Java, Lectures, Open Source, Programming, Standards | Tagged