Skip to content

Commit

Permalink
mergeGroovyExtensionModules() not working with multiple extension mod…
Browse files Browse the repository at this point in the history
…ules for groovy 2.5+ #490
  • Loading branch information
paulk-asert committed Jul 3, 2022
1 parent 1ff12fc commit 30c23e7
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/docs/changes/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Change Log
## v7.1.3 (unreleased)
* The GroovyExtensionModuleTransformer now also works for Groovy 2.5+.

## v7.1.2 (2021-12-28)
* Upgrade log4j to 2.17.1 due to CVE-2021-45105 and CVE-2021-44832

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,25 @@ import org.gradle.api.file.FileTreeElement
/**
* Modified from eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy
* <p>
* Resource transformer that merges Groovy extension module descriptor files into a single file. If there are several
* META-INF/services/org.codehaus.groovy.runtime.ExtensionModule resources spread across many JARs the individual
* entries will all be merged into a single META-INF/services/org.codehaus.groovy.runtime.ExtensionModule resource
* Resource transformer that merges Groovy extension module descriptor files into a single file.
* Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule
* and live in the META-INF/services (Groovy up to 2.4) or META-INF/groovy (Groovy 2.5+) directory.
* See https://issues.apache.org/jira/browse/GROOVY-8480 for more details of the change.
*
* If there are several descriptor files spread across many JARs the individual
* entries will be merged into a single descriptor file which will be
* packaged into the resultant JAR produced by the shadowing process.
* It will live in the legacy directory (META-INF/services) if all of the processed descriptor
* files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy).
* Note that certain JDK9+ tooling will break when using the legacy location.
*/
@CacheableTransformer
class GroovyExtensionModuleTransformer implements Transformer {

private static final GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH =
private static final GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH =
"META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"
private static final GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH =
"META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule"

private static final MODULE_NAME_KEY = 'moduleName'
private static final MODULE_VERSION_KEY = 'moduleVersion'
Expand All @@ -47,10 +56,16 @@ class GroovyExtensionModuleTransformer implements Transformer {
private static final MERGED_MODULE_VERSION = '1.0.0'

private final Properties module = new Properties()
private boolean legacy = true // default to Groovy 2.4 or earlier

@Override
boolean canTransformResource(FileTreeElement element) {
return element.relativePath.pathString == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH
def path = element.relativePath.pathString
if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) {
legacy = false // Groovy 2.5+
return true
}
return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH
}

@Override
Expand Down Expand Up @@ -95,7 +110,7 @@ class GroovyExtensionModuleTransformer implements Transformer {

@Override
void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {
ZipEntry entry = new ZipEntry(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH)
ZipEntry entry = new ZipEntry(legacy ? GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH : GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH)
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time)
os.putNextEntry(entry)
IOUtil.copy(toInputStream(module), os)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,51 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write(
assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension'
}

def 'Groovy extension module transformer works for Groovy2_5+'() {
given:
def one = buildJar('one.jar')
.insertFile('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule',
'''moduleName=foo
moduleVersion=1.0.5
extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension
staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write()

def two = buildJar('two.jar')
.insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule',
'''moduleName=bar
moduleVersion=2.3.5
extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension
staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write()

buildFile << """
import ${GroovyExtensionModuleTransformer.name}
shadowJar {
from('${escapedPath(one)}')
from('${escapedPath(two)}')
}
shadowJar {
transform(GroovyExtensionModuleTransformer)
}
""".stripIndent()

when:
run('shadowJar')

then:
output.exists()

and:
def text = getJarFileContents(output, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule')
def props = new Properties()
props.load(new StringReader(text))
props.getProperty('moduleName') == 'MergedByShadowJar'
props.getProperty('moduleVersion') == '1.0.0'
props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension'
props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension'
doesNotContain(output, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule'])
}

def 'Groovy extension module transformer short syntax'() {
given:
def one = buildJar('one.jar')
Expand Down

0 comments on commit 30c23e7

Please sign in to comment.