<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://brokenco.de//feed/by_tag/gradlegoodness.xml" rel="self" type="application/atom+xml" /><link href="https://brokenco.de//" rel="alternate" type="text/html" /><updated>2026-05-03T00:12:50+00:00</updated><id>https://brokenco.de//feed/by_tag/gradlegoodness.xml</id><title type="html">rtyler</title><subtitle>a moderately technical blog</subtitle><author><name>R. Tyler Croy</name></author><entry><title type="html">Gradle Goodness: Excluding Shadow jar dependencies</title><link href="https://brokenco.de//2015/07/15/gradle-goodness-excluding-depends-from-shadow.html" rel="alternate" type="text/html" title="Gradle Goodness: Excluding Shadow jar dependencies" /><published>2015-07-15T00:00:00+00:00</published><updated>2015-07-15T00:00:00+00:00</updated><id>https://brokenco.de//2015/07/15/gradle-goodness-excluding-depends-from-shadow</id><content type="html" xml:base="https://brokenco.de//2015/07/15/gradle-goodness-excluding-depends-from-shadow.html"><![CDATA[<p>Over the past year, I’ve spent a lot of time <a href="https://github.com/jruby-gradle">hacking in the Gradle
ecosysgtem</a> which, for better or worse, has
earned me a reputation of knowing Gradle-y things within
<a href="http://hackers.lookout.com">Lookout</a>. Recently, my colleague
<a href="https://github.com/rkuris">Ron</a> approached me with a Gradle problem: using the
<a href="https://github.com/johnrengelman/shadow/">shadow plugin</a> (a great plugin for
building fat jars), he was having trouble excluding some dependencies from the
produced jar artifact. I figured I would emulate Mr. Haki’s <a href="http://mrhaki.blogspot.com/search/label/Gradle%3AGoodness">Gradle
Goodness</a> series and
post one of my own.</p>

<h3 id="the-problem">The Problem</h3>

<p>The Shadow plugin will, by default, include runtime dependencies inside of the
produced artifact. The way that it handles this is by unzipping (effectively)
the <code class="language-plaintext highlighter-rouge">.jar</code> dependencies into the shadow jar’s file tree.</p>

<p>In Ron’s case, he’s building an <a href="http://storm.apache.org">Apache Storm</a>
topology which must compile against the “storm-core” dependency, but it
must not include that dependency in the resulting artifact. Otherwise the
deployment of the topology explodes with all kinds of classpath conflicts, as
Storm already has the “storm-core” code loaded at runtime.</p>

<p>His <code class="language-plaintext highlighter-rouge">build.gradle</code> originally looked something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plugins {
    id 'java'
    id 'com.github.johnrengelman.shadow' version '1.2.1'
}

shadowJar {
    manifest {
        attributes 'Implementation-Title': 'Storm Health Check',
                'Implementation-Version': project.version,
                'Main-Class': 'com.github.lookout.storm.healthcheck.HealthCheckTopology'
    }
}

dependencies {
    compile group: 'org.apache.storm', name: 'storm-core', version: '0.9.4'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}
</code></pre></div></div>

<h3 id="attempt-1">Attempt #1</h3>

<p>Overly confident, as per usual, I say “<a href="http://www.gagbay.com/images/2012/03/well_theres_your_problem-56537.jpg">well there’s your
problem</a>”
and change the <code class="language-plaintext highlighter-rouge">dependencies { }</code> closure to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dependencies {
    runtime group: 'org.apache.storm', name: 'storm-core', version: '0.9.4'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}
</code></pre></div></div>

<p>This, of course, will fail to compile the Java code included in the project
since it needs the transitive dependencies of “storm-core” to compile properly.</p>

<p>Duh.</p>

<h3 id="attempt-2">Attempt #2</h3>

<p>Reading through the docs again for the Shadow plugin, I noticed <a href="https://github.com/johnrengelman/shadow#filtering-shadow-jar-contents-by-mavenproject-dependency">this bit about
excluding
dependencies</a> and figured  I would give that a try:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shadowJar {
    manifest {
        attributes 'Implementation-Title': 'Storm Health Check',
                'Implementation-Version': project.version,
                'Main-Class': 'com.github.lookout.storm.healthcheck.HealthCheckTopology'
    }

    dependencies {
        exclude(dependency('org.apache.storm:storm-core:0.9.4'))
    }
}
</code></pre></div></div>

<p>Unfortnuately this functionality provided by the Shadow plugin on excludes the
top-level dependency, and doesn’t do anything different with transitive
dependencies.</p>

<p>So that doesn’t work for us, trying again!</p>

<h3 id="attempt-3">Attempt #3</h3>

<p>I’ve worked with the Shadow plugin a number of times before, and I’m aware of
the defaults that are applied to the default <code class="language-plaintext highlighter-rouge">shadowJar { }</code> task, so my next
attempt was to simply avoid that task</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>configurations {
    shadow
}

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

task stormTopology(type: ShadowJar) {
    manifest {
        attributes 'Implementation-Title': 'Storm Health Check',
                'Implementation-Version': project.version,
                'Main-Class': 'com.github.lookout.storm.healthcheck.HealthCheckTopology'
    }

    configurations = [project.configurations.shadow]
    from(project.sourceSets.main.output)
}
</code></pre></div></div>

<p>This introduces a new configuration called “shadow” which is assigned to the
new “stormTopology” task. So this technically <em>works</em>.</p>

<p>It’s got some downsides:</p>

<ul>
  <li>Introduction of a new task</li>
  <li>Introduction of a new, empty, configuration</li>
  <li>Additional code to get our <code class="language-plaintext highlighter-rouge">ShadowJar</code> task to be configured
properly</li>
</ul>

<p>This didn’t feel right to me, so I tried one more time to make it cleaner</p>

<h3 id="attempt-3-12">Attempt #3 1/2</h3>

<p>While re-reviewing the code for the default <code class="language-plaintext highlighter-rouge">shadowJar { }</code> task, I noticed
<a href="https://github.com/johnrengelman/shadow/blob/master/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy#L58">this
line</a>
which configures the task to use the “runtime” configuration by default. The
Gradle <a href="https://docs.gradle.org/current/userguide/java_plugin.html">Java
plugin</a> defines a
number of configurations, “compile” being one of them and another
notable one being “runtime.” The latter is intended for runtime dependencies
for the application being built. The Java plugin <em>also</em> makes “runtime” extend
from “compile,” turning it into a superset of dependency information.</p>

<p>This means that if you define a “compile” dependency, it will be present in the
“runtime” configuration automatically.</p>

<p>Recognizing this as part of a potential solution, I refactored the build.gradle one more time:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plugins {
    id 'com.github.johnrengelman.shadow' version '1.2.1'
}

shadowJar {
    manifest {
        attributes 'Implementation-Title': 'Storm Health Check',
                'Implementation-Version': project.version,
                'Main-Class': 'com.github.lookout.storm.healthcheck.HealthCheckTopology'
    }
}

dependencies {
    compile group: 'org.apache.storm', name: 'storm-core', version: '0.9.4'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

configurations {
    /* We don't want the storm-core dependency in our shadowJar */
    runtime.exclude module: 'storm-core'
}
</code></pre></div></div>

<p>Note the last <code class="language-plaintext highlighter-rouge">configurations { }</code> closure. This is using Gradle’s built in
dependency management semantics to purge <code class="language-plaintext highlighter-rouge">storm-core</code> and its transitive
dependencies from the “runtime” configuration.</p>

<p>This is short, functional and doesn’t require any special configuration, yay!</p>]]></content><author><name>R. Tyler Croy</name></author><category term="gradle" /><category term="gradlegoodness" /><summary type="html"><![CDATA[Over the past year, I’ve spent a lot of time hacking in the Gradle ecosysgtem which, for better or worse, has earned me a reputation of knowing Gradle-y things within Lookout. Recently, my colleague Ron approached me with a Gradle problem: using the shadow plugin (a great plugin for building fat jars), he was having trouble excluding some dependencies from the produced jar artifact. I figured I would emulate Mr. Haki’s Gradle Goodness series and post one of my own.]]></summary></entry></feed>