# Controlling JAR Content Merging

Shadow allows for customizing the process by which the output JAR is generated through the Transformer (opens new window) interface. This is a concept that has been carried over from the original Maven Shade implementation. A Transformer (opens new window) is invoked for each entry in the JAR before being written to the final output JAR. This allows a Transformer (opens new window) to determine if it should process a particular entry and apply any modifications before writing the stream to the output.

// Adding a Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer implements Transformer {
    boolean canTransformResource(FileTreeElement element) { true }

    void transform(TransformerContext context) {}

    boolean hasTransformedResource() { true }

    void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {}
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(MyTransformer.class)
}

Additionally, a Transformer can accept a Closure to configure the provided Transformer.

// Configuring a Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer implements Transformer {

    boolean enabled

    boolean canTransformResource(FileTreeElement element) { true }

    void transform(TransformerContext context) {}

    boolean hasTransformedResource() { true }

    void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {}
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(MyTransformer.class) {
    enabled = true
  }
}

An instantiated instance of a Transformer can also be provided.

// Adding a Transformer Instance
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer implements Transformer {

    boolean enabled

    boolean canTransformResource(FileTreeElement element) { true }

    void transform(TransformerContext context) {}

    boolean hasTransformedResource() { true }

    void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {}
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(new MyTransformer(enabled: true))
}

# Merging Service Descriptor Files

Java libraries often contain service descriptors files in the META-INF/services directory of the JAR. A service descriptor typically contains a line delimited list of classes that are supported for a particular service. At runtime, this file is read and used to configure library or application behavior.

Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. The ServiceFileTransformer (opens new window) class is used to perform this merging. By default, it will merge each copy of a file under META-INF/services into a single file in the output JAR.

// Merging Service Files
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  mergeServiceFiles()
}

The above code snippet is a convenience syntax for calling transform(ServiceFileTransformer.class) (opens new window).

Groovy Extension Module descriptor files (located at META-INF/services/org.codehaus.groovy.runtime.ExtensionModule) are ignored by the ServiceFileTransformer (opens new window). This is due to these files having a different syntax than standard service descriptor files. Use the mergeGroovyExtensionModules() (opens new window) method to merge these files if your dependencies contain them.

# Configuring the Location of Service Descriptor Files

By default the ServiceFileTransformer (opens new window) is configured to merge files in META-INF/services. This directory can be overridden to merge descriptor files in a different location.

// Merging Service Files in a Specific Directory
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  mergeServiceFiles {
    path = 'META-INF/custom'
  }
}

# Excluding/Including Specific Service Descriptor Files From Merging

The ServiceFileTransformer (opens new window) class supports specifying specific files to include or exclude from merging.

// Excluding a Service Descriptor From Merging
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  mergeServiceFiles {
    exclude 'META-INF/services/com.acme.*'
  }
}

# Merging Groovy Extension Modules

Shadow provides a specific transformer for dealing with Groovy extension module files. This is due to their special syntax and how they need to be merged together. The GroovyExtensionModuleTransformer (opens new window) will handle these files. The ShadowJar (opens new window) task also provides a short syntax method to add this transformer.

// Merging Groovy Extension Modules
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  mergeGroovyExtensionModules()
}

# Appending Text Files

Generic text files can be appended together using the AppendingTransformer (opens new window). Each file is appended using new lines to separate content. The ShadowJar (opens new window) task provides a short syntax method of append(String) (opens new window) to configure this transformer.

// Appending a Property File
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  append 'test.properties'
}

# Appending XML Files

XML files require a special transformer for merging. The XmlAppendingTransformer (opens new window) reads each XML document and merges each root element into a single document. There is no short syntax method for the XmlAppendingTransformer (opens new window). It must be added using the transform (opens new window) methods.

// Appending a XML File
import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(XmlAppendingTransformer.class) {
    resource = 'properties.xml'
  }
}