-
Notifications
You must be signed in to change notification settings - Fork 396
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Convert ServiceFileTransformer * Convert ComponentsXmlResourceTransformer * Simplify ComponentsXmlResourceTransformer
- Loading branch information
Showing
4 changed files
with
250 additions
and
419 deletions.
There are no files selected for viewing
134 changes: 134 additions & 0 deletions
134
...m/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package com.github.jengelman.gradle.plugins.shadow.transformers | ||
|
||
import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext | ||
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp | ||
import java.io.BufferedInputStream | ||
import java.io.ByteArrayOutputStream | ||
import java.io.IOException | ||
import org.apache.tools.zip.ZipEntry | ||
import org.apache.tools.zip.ZipOutputStream | ||
import org.codehaus.plexus.util.xml.XmlStreamReader | ||
import org.codehaus.plexus.util.xml.XmlStreamWriter | ||
import org.codehaus.plexus.util.xml.Xpp3Dom | ||
import org.codehaus.plexus.util.xml.Xpp3DomBuilder | ||
import org.codehaus.plexus.util.xml.Xpp3DomWriter | ||
import org.gradle.api.file.FileTreeElement | ||
|
||
/** | ||
* A resource processor that aggregates plexus `components.xml` files. | ||
* | ||
* Modified from `org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java` | ||
* | ||
* @author John Engelman | ||
*/ | ||
open class ComponentsXmlResourceTransformer : Transformer { | ||
private val components = mutableMapOf<String, Xpp3Dom>() | ||
|
||
override fun canTransformResource(element: FileTreeElement): Boolean { | ||
return COMPONENTS_XML_PATH == element.relativePath.pathString | ||
} | ||
|
||
override fun transform(context: TransformerContext) { | ||
val newDom = try { | ||
val bis = object : BufferedInputStream(context.inputStream) { | ||
@Throws(IOException::class) | ||
override fun close() { | ||
// leave ZIP open | ||
} | ||
} | ||
Xpp3DomBuilder.build(XmlStreamReader(bis)) | ||
} catch (e: Exception) { | ||
throw IOException("Error parsing components.xml in ${context.inputStream}", e) | ||
} | ||
|
||
// Only try to merge in components if there are some elements in the component-set | ||
if (newDom.getChild("components") == null) return | ||
|
||
val children = newDom.getChild("components").getChildren("component") | ||
for (component in children) { | ||
var role: String? = getValue(component, "role") | ||
role = getRelocatedClass(role, context) | ||
setValue(component, "role", role) | ||
|
||
val roleHint = getValue(component, "role-hint") | ||
|
||
var impl: String? = getValue(component, "implementation") | ||
impl = getRelocatedClass(impl, context) | ||
setValue(component, "implementation", impl) | ||
|
||
val key = "$role:$roleHint" | ||
// TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing | ||
// configuration carry over | ||
components[key]?.getChild("configuration")?.let { | ||
component.addChild(it) | ||
} | ||
|
||
val requirements = component.getChild("requirements") | ||
if (requirements != null && requirements.childCount > 0) { | ||
for (r in requirements.childCount - 1 downTo 0) { | ||
val requirement = requirements.getChild(r) | ||
var requiredRole: String? = getValue(requirement, "role") | ||
requiredRole = getRelocatedClass(requiredRole, context) | ||
setValue(requirement, "role", requiredRole) | ||
} | ||
} | ||
components[key] = component | ||
} | ||
} | ||
|
||
override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { | ||
val entry = ZipEntry(COMPONENTS_XML_PATH) | ||
entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) | ||
os.putNextEntry(entry) | ||
|
||
transformedResource.inputStream().use { | ||
it.copyTo(os) | ||
} | ||
components.clear() | ||
} | ||
|
||
override fun hasTransformedResource(): Boolean = components.isNotEmpty() | ||
|
||
@get:Throws(IOException::class) | ||
private val transformedResource: ByteArray | ||
get() { | ||
val os = ByteArrayOutputStream(1024 * 4) | ||
XmlStreamWriter(os).use { writer -> | ||
val dom = Xpp3Dom("component-set") | ||
val componentDom = Xpp3Dom("components") | ||
dom.addChild(componentDom) | ||
for (component in components.values) { | ||
componentDom.addChild(component) | ||
} | ||
Xpp3DomWriter.write(writer, dom) | ||
} | ||
return os.toByteArray() | ||
} | ||
|
||
companion object { | ||
const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" | ||
|
||
private fun getRelocatedClass(className: String?, context: TransformerContext): String? { | ||
val stats = context.stats | ||
if (!className.isNullOrEmpty()) { | ||
for (relocator in context.relocators) { | ||
if (relocator.canRelocateClass(className)) { | ||
val relocateClassContext = RelocateClassContext(className, stats) | ||
return relocator.relocateClass(relocateClassContext) | ||
} | ||
} | ||
} | ||
return className | ||
} | ||
|
||
private fun getValue(dom: Xpp3Dom, element: String): String { | ||
return dom.getChild(element).value.orEmpty() | ||
} | ||
|
||
private fun setValue(dom: Xpp3Dom, element: String, value: String?) { | ||
val child = dom.getChild(element) | ||
if (child == null || value.isNullOrEmpty()) return | ||
child.value = value | ||
} | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
.../kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package com.github.jengelman.gradle.plugins.shadow.transformers | ||
|
||
import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext | ||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction | ||
import java.io.ByteArrayInputStream | ||
import java.io.ByteArrayOutputStream | ||
import java.io.File | ||
import java.io.IOException | ||
import java.io.InputStream | ||
import org.apache.tools.zip.ZipEntry | ||
import org.apache.tools.zip.ZipOutputStream | ||
import org.gradle.api.file.FileTreeElement | ||
import org.gradle.api.tasks.Input | ||
import org.gradle.api.tasks.util.PatternFilterable | ||
import org.gradle.api.tasks.util.PatternSet | ||
|
||
/** | ||
* Modified from `org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java` | ||
* | ||
* Resources transformer that appends entries in `META-INF/services` resources into | ||
* a single resource. For example, if there are several `META-INF/services/org.apache.maven.project.ProjectBuilder` | ||
* resources spread across many JARs the individual entries will all be concatenated into a single | ||
* `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant JAR produced | ||
* by the shading process. | ||
* | ||
* @author jvanzyl | ||
* @author Charlie Knudsen | ||
* @author John Engelman | ||
*/ | ||
@CacheableTransformer | ||
open class ServiceFileTransformer( | ||
private val patternSet: PatternSet = PatternSet() | ||
.include(SERVICES_PATTERN) | ||
.exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), | ||
) : Transformer, | ||
PatternFilterable by patternSet { | ||
private val serviceEntries = mutableMapOf<String, ServiceStream>() | ||
|
||
override fun canTransformResource(element: FileTreeElement): Boolean { | ||
val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element | ||
return patternSet.asSpec.isSatisfiedBy(target) | ||
} | ||
|
||
override fun transform(context: TransformerContext) { | ||
val lines = context.inputStream.bufferedReader().readLines().toMutableList() | ||
var targetPath = context.path | ||
context.relocators.forEach { rel -> | ||
if (rel.canRelocateClass(File(targetPath).name)) { | ||
val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build() | ||
targetPath = rel.relocateClass(classContext) | ||
} | ||
lines.forEachIndexed { i, line -> | ||
if (rel.canRelocateClass(line)) { | ||
val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() | ||
lines[i] = rel.relocateClass(lineContext) | ||
} | ||
} | ||
} | ||
lines.forEach { line -> | ||
serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply { | ||
append(ByteArrayInputStream(line.toByteArray())) | ||
} | ||
} | ||
} | ||
|
||
override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() | ||
|
||
override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { | ||
serviceEntries.forEach { (path, stream) -> | ||
val entry = ZipEntry(path) | ||
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) | ||
os.putNextEntry(entry) | ||
stream.toInputStream().use { | ||
it.copyTo(os) | ||
} | ||
os.closeEntry() | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Input | ||
override fun getIncludes(): Set<String> = patternSet.includes | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Input | ||
override fun getExcludes(): Set<String> = patternSet.excludes | ||
|
||
open fun setPath(path: String): PatternFilterable = apply { | ||
patternSet.setIncludes(listOf("$path/**")) | ||
} | ||
|
||
open class ServiceStream : ByteArrayOutputStream(1024) { | ||
@Throws(IOException::class) | ||
open fun append(inputStream: InputStream) { | ||
if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { | ||
val newline = "\n".toByteArray() | ||
write(newline, 0, newline.size) | ||
} | ||
inputStream.use { | ||
it.copyTo(this) | ||
} | ||
} | ||
|
||
open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) | ||
} | ||
|
||
private companion object { | ||
private const val SERVICES_PATTERN = "META-INF/services/**" | ||
private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = | ||
"META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" | ||
} | ||
} |
Oops, something went wrong.