Fat Jars with Excluded Dependencies in Gradle

Written on .

In this post I will outline how to build a jar in Gradle that includes all project dependencies, a so-called "fat jar". However, this implementation will allow you to selectively exclude certain dependencies from the packaged jar. The motivation for this came while using Apache Storm, where any dependencies must be bundled in the jar, but the Storm libraries themselves are provided by the runtime environment and thus must not be bundled.

apply plugin: "java"
sourceCompatibility = 1.8
version = "1.0"

repositories {
    mavenCentral()
    maven { url "http://oss.sonatype.org/content/repositories/github-releases/" }
    maven { url "http://clojars.org/repo" }
}

configurations {
    provided
    compile.extendsFrom provided
}

dependencies {
    provided "org.apache.storm:storm-core:0.9.2-incubating"
    compile "org.twitter4j:twitter4j-stream:4.0.2"
    testCompile "junit:junit:4.11"
}

jar {
    dependsOn configurations.runtime
    from {
        (configurations.runtime - configurations.provided).collect {
            it.isDirectory() ? it : zipTree(it)
        }
    } {
        exclude "META-INF/*.SF"
        exclude "META-INF/*.DSA"
        exclude "META-INF/*.RSA"
    }
}

First we define a new configuration in line 12 called provided. In line 13 we then make the standard compile configuration depend on provided. A third default configuration also exists called runtime, which is not explicitly defined here. This extra configuration makes no difference to Gradle.

Second we define our dependencies in lines 17 and 18. Storm is defined in the provided configuation while the Twitter API is defined in compile, as you normally would. Remember that compile depends on provided, so any dependencies defined there will be available when you actually compile and run your code.

Third, in line 22-33 we build the fat jar. When running the jar task this code instructs Gradle to include all the dependencies within the target jar file. The important bit is in line 25 where it says configurations.runtime - configurations.provided. This takes all the runtime dependencies minus all the provided dependencies, leaving only dependencies explicitly defined as either compile or runtime.

That's it. Now you have a fat jar with some dependencies selectively excluded.