Creating self-contained, executable Jars with Gradle and Shadow
Earlier this year, we started implementing a new backend REST architecture for our application based on Yammer/Coda Hale’s Dropwizard Framework. Since we don’t use Maven anywhere in our stack, we decided to use Gradle as our build tool. This gives us enormous flexibility as we built our new backend around the concept of “micro-services” – each service is isolated from the others and responsible for a specific piece of functionality.
Since these services aren’t WARs, we wanted a way to deploy a pre-packaged jar that we could simply execute on our servers. Maven has a nice tool for this – Apache Maven Shade Plugin. It repackages all of your application’s dependencies into a single JAR file and offers a variety of extension points for effecting the contents of the resulting file. Unfortunately, at the time, Gradle lacked a similar plugin (there are a variety of similar plugins but all had severe limitations for our application). Instead, we wrote a Gradle port of the Shade plugin – Gradle Shadow Plugin.
Shadow is a port of the Shade plugin including most of its extensions points. This makes it easier for converting existing Maven builds to Gradle. It is open sourced under the ALv2 and contributions are most welcome.
To Shadow enable your project, add the BinTray repository and the Shadow plugin dependency to your buildscript configuration like so:
By default, Shadow will bundle all our your compile and runtime dependencies into an additional JAR with the classifier ‘shadow’ appended to it. This JAR is directly executable by executing the JAR:
$ java -jar build/libs/shadow-blog-0.1-shadow.jar Hello World
Notice that trying to execute the output of the normal JAR tasks results in an error due to the Groovy library not being available on the classpath:
$ java -jar build/libs/shadow-blog-0.1.jar Exception in thread "main" java.lang.NoClassDefFoundError: groovy/lang/GroovyObject at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) at java.lang.ClassLoader.defineClass(ClassLoader.java:615) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) Caused by: java.lang.ClassNotFoundException: groovy.lang.GroovyObject at java.net.URLClassLoader$1.run(URLClassLoader.java:202) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) ... 12 more