DISTRIBUTION PACKAGING W/ SHADOW
About Me
- Chief Technologist - Object Partners
- http://www.objectpartners.com
- http://imperceptiblethoughts.com
- @johnrengelman
- johnrengelman
Presentation
What is Shadow?
Simple Answer
Fat (uber) JAR Creator
Detailed Answer
- Dependency exploded/combiner
- File filter/transformer
- Package relocator
Downloads


> 1,000,000 !!!
- 928K from bintray since 1st version published.
- No data from Gradle Plugin portal
Adding Shadow
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '1.2.3'
}
$ gradle application:shadowJar && \
java -jar application/build/libs/application-shadow.jar
Client Version 1.0.0
Configuring the task
shadowJar {
classifier = 'shadow' (1)
}
1 | Change the default of all |
Integration with Application
mainClassName = 'shadow.App'
$ gradle runShadow
$ gradle distShadowTar
$ gradle distShadowZip
Filtering Files
Use the standard methods from Jar
task
shadowJar {
exclude 'adapter.properties'
}
$ gradle application-resolved-versions:jarContent
META-INF/
META-INF/MANIFEST.MF
shadow/
shadow/App.class
shadow/AdapterVersion.class
META-INF/services/
META-INF/services/shadow.client.Version
shadow/client/
shadow/client/Version.class
shadow/Version.class
Filtering Dependencies
Use the dependencies{}
block
shadowJar {
dependencies {
exclude(dependency('shadow:client'))
}
}
$ gradle :application-filter-dependency:jarContent
META-INF/
META-INF/MANIFEST.MF
shadow/
shadow/AdapterVersion.class
adapter.properties
META-INF/services/
META-INF/services/shadow.client.Version
- Dependency Filters are explicit per dependency
- This means, you must exclude transitive deps explicitly
Shadow Configuration
project.configurations.shadow
holds runtime dependencies that are not merged
dependencies {
compile 'shadow:client-adapter:1.0.0'
shadow 'shadow:runtime-library:1.0.0' (1)
}
1 | This library will not be merged into the JAR |
tasks.withType(AbstractCompile) {
classpath += project.configurations.shadow (1)
}
1 | configurations.shadow is not added to the compile classpath by default |
$ gradle :application-shadow-conf:jarContent \
:application-shadow-conf:runShadow
META-INF/
META-INF/MANIFEST.MF
shadow/
shadow/App.class
shadow/AdapterVersion.class
adapter.properties
META-INF/services/
META-INF/services/shadow.client.Version
shadow/client/
shadow/client/Version.class
shadow/Version.class
Adapter: Client Version 2.0.0
Executing runtime code!
JAR Transformations
Service Descriptors
shadowJar {
mergeServiceFiles()
}
$ gradle application-transformer:showServices
1.0.0
1.0.0-additional
Other transforms
shadowJar {
mergeGroovyExtensionModules()
append 'META-INF/LICENSE'
transform(XmlAppendingTransformer) {
resource = 'properties.xml'
}
}
Package Relocation
Conflicting dependencies
dependencies {
compile 'shadow:client:1.0.0'
compile 'shadow:client-adapter:1.0.0'
}
public static void main(String[] args) {
System.out.println(AdapterVersion.getVersion());
System.out.println(Version.getVersion());
}
$ gradle application-resolved-versions:runShadow
Adapter: Client Version 2.0.0
Client Version 2.0.0
Relocate
shadowJar {
relocate 'shadow.client', 'adapter.shadow.client'
}
Update Dependency
dependencies {
compile 'shadow:client:1.0.0'
compile 'shadow:client-adapter-all:1.0.0'
}
$ gradle application-adapter-all:runShadow
Adapter: Client Version 2.0.0
Client Version 1.0.0
Publishing Shadow JARs
Simply apply Maven or Maven-Publish
(ivy-publishing untested)
With Maven-Publish Plugin
publishing {
publications {
shadow(MavenPublication) {
from components.shadow
}
}
}
$ gradle :client-adapter-maven-publish:publishToMavenLocal
$ ls ~/.m2/repositories/shadow/client-adapter-maven-publish/1.0.0/
client-adapter-maven-publish-1.0.0-all.jar (1)
client-adapter-maven-publish-1.0.0.pom
1 | Notice the use of an artifact classifier |
Producing a Top Level Artifact w/ Shadow
With Maven-Publish Plugin
First, Configure ShadowJar
shadowJar {
baseName = "${project.name}-all" (1)
classifier = "" (2)
}
1 | Override the baseName to not step on JAR output |
2 | Remove the classifier as this will be a root artifact |
Then Configure Publishing
publishing {
publications {
shadow(MavenPublication) {
from components.shadow
artifactId "${project.name}-all"
}
}
}
$ gradle :client-adapter-relocate:publishToMavenLocal
$ ls ~/.m2/repositories/shadow/client-adapter-relocate-all/1.0.0/
client-adapter-relocate-all-1.0.0.jar
client-adapter-relocate-all-1.0.0.pom
With Maven Plugin
shadowJar {
baseName = 'client-adapter-all'
classifier = ''
}
Multi-Projects
Use the Map
notation for a project dependency
dependencies {
compile 'shadow:client:1.0.0'
compile project(path: ":client-adapter-relocate", configuration: 'shadow') (1)
}
1 | Depend on the shadow configuration from the project |
$ gradle :multi-app:run
Adapter: Client Version 2.0.0
Client Version 1.0.0
User Guide
REMEMBER
$ gradle knows