@@ -4,12 +4,16 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats
4
4
import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext
5
5
import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
6
6
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp
7
- import java.io.File
8
7
import java.net.URL
8
+ import java.nio.file.Path
9
9
import java.util.Collections
10
10
import java.util.Enumeration
11
+ import kotlin.io.path.createTempFile
12
+ import kotlin.io.path.deleteIfExists
13
+ import kotlin.io.path.outputStream
11
14
import org.apache.commons.io.output.CloseShieldOutputStream
12
15
import org.apache.logging.log4j.core.config.plugins.processor.PluginCache
16
+ import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry
13
17
import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor
14
18
import org.apache.tools.zip.ZipEntry
15
19
import org.apache.tools.zip.ZipOutputStream
@@ -25,64 +29,101 @@ import org.gradle.api.file.FileTreeElement
25
29
*/
26
30
@CacheableTransformer
27
31
public open class Log4j2PluginsCacheFileTransformer : Transformer {
28
- private val temporaryFiles = mutableListOf<File >()
29
- private val relocators = mutableListOf<Relocator >()
32
+ /* *
33
+ * Log4j config files to share across the transformation stages.
34
+ */
35
+ private val tempFiles = mutableListOf<Path >()
36
+
37
+ /* *
38
+ * [Relocator] instances to share across the transformation stages.
39
+ */
40
+ private val tempRelocators = mutableListOf<Relocator >()
30
41
private var stats: ShadowStats ? = null
31
42
32
43
override fun canTransformResource (element : FileTreeElement ): Boolean {
33
44
return PluginProcessor .PLUGIN_CACHE_FILE == element.name
34
45
}
35
46
36
47
override fun transform (context : TransformerContext ) {
37
- val temporaryFile = File .createTempFile(" Log4j2Plugins" , " .dat" )
38
- temporaryFile.deleteOnExit()
39
- temporaryFiles.add(temporaryFile)
48
+ val temporaryFile = createTempFile(" Log4j2Plugins" , " .dat" )
49
+ tempFiles.add(temporaryFile)
40
50
val fos = temporaryFile.outputStream()
41
51
context.inputStream.use {
42
52
it.copyTo(fos)
43
53
}
44
54
45
- relocators .addAll(context.relocators)
55
+ tempRelocators .addAll(context.relocators)
46
56
47
57
if (stats == null ) {
48
58
stats = context.stats
49
59
}
50
60
}
51
61
62
+ /* *
63
+ * @return true if any dat file collected
64
+ */
52
65
override fun hasTransformedResource (): Boolean {
53
- // This functionality matches the original plugin, however, I'm not clear what
54
- // the exact logic is. From what I can tell temporaryFiles should be never be empty
55
- // if anything has been performed.
56
- return temporaryFiles.isNotEmpty() || relocators.isNotEmpty()
66
+ return tempFiles.isNotEmpty()
57
67
}
58
68
59
69
override fun modifyOutputStream (os : ZipOutputStream , preserveFileTimestamps : Boolean ) {
60
- val pluginCache = PluginCache ()
61
- pluginCache.loadCacheFiles(urlEnumeration)
62
- relocatePlugins(pluginCache)
63
- val entry = ZipEntry (PluginProcessor .PLUGIN_CACHE_FILE )
64
- entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time)
65
- os.putNextEntry(entry)
66
- pluginCache.writeCache(CloseShieldOutputStream .wrap(os))
67
- temporaryFiles.clear()
70
+ try {
71
+ val aggregator = PluginCache ()
72
+ aggregator.loadCacheFiles(urlEnumeration)
73
+ relocatePlugin(tempRelocators, aggregator.allCategories)
74
+ val entry = ZipEntry (PluginProcessor .PLUGIN_CACHE_FILE )
75
+ entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time)
76
+ os.putNextEntry(entry)
77
+ aggregator.writeCache(CloseShieldOutputStream .wrap(os))
78
+ } finally {
79
+ deleteTempFiles()
80
+ }
68
81
}
69
82
70
- private fun relocatePlugins (pluginCache : PluginCache ) {
71
- pluginCache.allCategories.values.forEach { currentMap ->
72
- currentMap.values.forEach { currentPluginEntry ->
73
- val className = currentPluginEntry.className
74
- val relocateClassContext = RelocateClassContext (className, requireNotNull(stats))
75
- relocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator ->
76
- // Then we perform that relocation and update the plugin entry to reflect the new value.
77
- currentPluginEntry.className = relocator.relocateClass(relocateClassContext)
83
+ /* *
84
+ * Applies the given `relocators` to the `aggregator`.
85
+ *
86
+ * @param relocators relocators.
87
+ * @param aggregatorCategories all categories of the aggregator
88
+ */
89
+ private fun relocatePlugin (
90
+ relocators : List <Relocator >,
91
+ aggregatorCategories : Map <String , Map <String , PluginEntry >>,
92
+ ) {
93
+ for (categoryEntry in aggregatorCategories.entries) {
94
+ for (pluginMapEntry in categoryEntry.value.entries) {
95
+ val pluginEntry = pluginMapEntry.value
96
+ val originalClassName = pluginEntry.className
97
+ val relocateClassContext = RelocateClassContext (originalClassName, requireNotNull(stats))
98
+
99
+ findFirstMatchingRelocator(originalClassName, relocators)?.let {
100
+ pluginEntry.className = it.relocateClass(relocateClassContext)
78
101
}
79
102
}
80
103
}
81
104
}
82
105
106
+ private fun findFirstMatchingRelocator (originalClassName : String , relocators : List <Relocator >): Relocator ? {
107
+ for (relocator in relocators) {
108
+ if (relocator.canRelocateClass(originalClassName)) {
109
+ return relocator
110
+ }
111
+ }
112
+ return null
113
+ }
114
+
115
+ private fun deleteTempFiles () {
116
+ val pathIterator = tempFiles.listIterator()
117
+ while (pathIterator.hasNext()) {
118
+ val path = pathIterator.next()
119
+ path.deleteIfExists()
120
+ pathIterator.remove()
121
+ }
122
+ }
123
+
83
124
private val urlEnumeration: Enumeration <URL >
84
125
get() {
85
- val urls = temporaryFiles .map { it.toURI ().toURL() }
126
+ val urls = tempFiles .map { it.toUri ().toURL() }
86
127
return Collections .enumeration(urls)
87
128
}
88
129
}
0 commit comments