DISTRIBUTION PACKAGING W/ SHADOW

About Me

Presentation

What is Shadow?

Simple Answer

Fat (uber) JAR Creator

Detailed Answer

Downloads

Shadow pre 1.0
Shadow post 1.0

> 1,000,000 !!!

Adding Shadow

application/build.gradle
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

application/build.gradle
shadowJar {
  classifier = 'shadow' (1)
}
1 Change the default of all

Integration with Application

application/build.gradle
mainClassName = 'shadow.App'
$ gradle runShadow
$ gradle distShadowTar
$ gradle distShadowZip

Filtering Files

Use the standard methods from Jar task

application-resolved-versions/build.gradle
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

application-filter-dependency/build.gradle
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

Shadow Configuration

project.configurations.shadow holds runtime dependencies that are not merged

application-shadow-conf/build.gradle
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
application-shadow-conf/build.gradle
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

application-transformer/build.gradle
shadowJar {
  mergeServiceFiles()
}
$ gradle application-transformer:showServices
1.0.0
1.0.0-additional

Other transforms

application-transformer/build.gradle
shadowJar {
  mergeGroovyExtensionModules()
  append 'META-INF/LICENSE'
  transform(XmlAppendingTransformer) {
    resource = 'properties.xml'
  }
}

Package Relocation

Conflicting dependencies

dot example
application-resolved-versions/build.gradle
dependencies {
  compile 'shadow:client:1.0.0'
  compile 'shadow:client-adapter:1.0.0'
}
application-resolved-versions/src/main/java/shadow/App.java
  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

client-adapter/build.gradle
shadowJar {
  relocate 'shadow.client', 'adapter.shadow.client'
}

Update Dependency

application-adapter-all/build.gradle
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

client-adapter-maven-publish/build.gradle
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

client-adapter-relocate/build.gradle
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

client-adapter-relocate/build.gradle
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

client-adapter/build.gradle
shadowJar {
  baseName = 'client-adapter-all'
  classifier = ''
}

Multi-Projects

Use the Map notation for a project dependency

multi-app/build.gradle
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

http://imperceptiblethoughts.com/shadow

REMEMBER

$ gradle knows

NO, THE SHADOW KNOWS…​

Questions?