# Controlling JAR Content Merging
Shadow allows for customizing the process by which the output JAR is generated through the
Transformer
interface.
This is a concept that has been carried over from the original Maven Shade implementation.
A Transformer
is invoked for each
entry in the JAR before being written to the final output JAR.
This allows a Transformer
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) {}
}
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) {}
}
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) {}
}
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
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
shadowJar {
mergeServiceFiles()
}
The above code snippet is a convenience syntax for calling
transform(ServiceFileTransformer.class)
.
Groovy Extension Module descriptor files (located at
META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
) are ignored by theServiceFileTransformer
. This is due to these files having a different syntax than standard service descriptor files. Use themergeGroovyExtensionModules()
method to merge these files if your dependencies contain them.
# Configuring the Location of Service Descriptor Files
By default the ServiceFileTransformer
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
shadowJar {
mergeServiceFiles {
path = 'META-INF/custom'
}
}
# Excluding/Including Specific Service Descriptor Files From Merging
The ServiceFileTransformer
class supports specifying specific files to include or exclude from merging.
// Excluding a Service Descriptor From Merging
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
will handle these files.
The ShadowJar
task also provides a short syntax
method to add this transformer.
// Merging Groovy Extension Modules
shadowJar {
mergeGroovyExtensionModules()
}
# Appending Text Files
Generic text files can be appended together using the
AppendingTransformer
.
Each file is appended using new lines to separate content.
The ShadowJar
task provides a short syntax
method of
append(String)
to
configure this transformer.
// Appending a Property File
shadowJar {
append 'test.properties'
}
# Appending XML Files
XML files require a special transformer for merging.
The XmlAppendingTransformer
reads each XML document and merges each root element into a single document.
There is no short syntax method for the XmlAppendingTransformer
.
It must be added using the transform
methods.
// Appending a XML File
import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer
shadowJar {
transform(XmlAppendingTransformer.class) {
resource = 'properties.xml'
}
}