# 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 theServiceFileTransformer
(opens new window). This is due to these files having a different syntax than standard service descriptor files. Use themergeGroovyExtensionModules()
(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'
}
}