Skip to content

Commit f5406d0

Browse files
authored
Add removePackage option (#163)
1 parent 5d26d6f commit f5406d0

File tree

5 files changed

+113
-22
lines changed

5 files changed

+113
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Version 1.10
44
* [New] [#160](https://github.com/gradlex-org/extra-java-module-info/pull/160) - Add 'preserveExisting' option to patch real modules
5+
* [New] [#140](https://github.com/gradlex-org/extra-java-module-info/pull/140) - Add 'removePackage' option to deal with duplicated packages
56

67
## Version 1.9
78
* [New] [#137](https://github.com/gradlex-org/extra-java-module-info/pull/137) - Configuration option for 'versionsProvidingConfiguration'

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,19 @@ This plugin offers the option to merge multiple Jars into one in such situations
311311

312312
Note: The merged Jar will include the *first* appearance of duplicated files (like the `MANIFEST.MF`).
313313

314+
In some cases, it may also be sufficient to remove appearances of the problematic package completely from some of the Jars.
315+
This can be the case if classes are in fact duplicated, or if classes are not used.
316+
For this, you can utilise the `removePackage` functionality:
317+
318+
```
319+
extraJavaModuleInfo {
320+
module("xerces:xercesImpl", "xerces") {
321+
removePackage("org.w3c.dom.html")
322+
// ...
323+
}
324+
}
325+
```
326+
314327
## How can I fix a library with a broken `module-info.class`?
315328

316329
To fix a library with a broken `module-info.class`, you can override the modular descriptor in the same way it is done with non-modular JARs.

src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoTransform.java

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ private void addAutomaticModuleName(File originalJar, File moduleJar, AutomaticM
276276
try (JarOutputStream outputStream = newJarOutputStream(Files.newOutputStream(moduleJar.toPath()), manifest)) {
277277
Map<String, List<String>> providers = new LinkedHashMap<>();
278278
Set<String> packages = new TreeSet<>();
279-
copyAndExtractProviders(inputStream, outputStream, !automaticModule.getMergedJars().isEmpty(), providers, packages);
279+
copyAndExtractProviders(inputStream, outputStream, automaticModule.getRemovedPackages(), !automaticModule.getMergedJars().isEmpty(), providers, packages);
280280
mergeJars(automaticModule, outputStream, providers, packages);
281281
}
282282
} catch (IOException e) {
@@ -289,7 +289,7 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
289289
try (JarOutputStream outputStream = newJarOutputStream(Files.newOutputStream(moduleJar.toPath()), inputStream.getManifest())) {
290290
Map<String, List<String>> providers = new LinkedHashMap<>();
291291
Set<String> packages = new TreeSet<>();
292-
byte[] existingModuleInfo = copyAndExtractProviders(inputStream, outputStream, !moduleInfo.getMergedJars().isEmpty(), providers, packages);
292+
byte[] existingModuleInfo = copyAndExtractProviders(inputStream, outputStream, moduleInfo.getRemovedPackages(), !moduleInfo.getMergedJars().isEmpty(), providers, packages);
293293
mergeJars(moduleInfo, outputStream, providers, packages);
294294
outputStream.putNextEntry(newReproducibleEntry("module-info.class"));
295295
outputStream.write(addModuleInfo(moduleInfo, providers, versionFromFilePath(originalJar.toPath()),
@@ -314,7 +314,7 @@ private JarOutputStream newJarOutputStream(OutputStream out, @Nullable Manifest
314314
}
315315

316316
@Nullable
317-
private byte[] copyAndExtractProviders(JarInputStream inputStream, JarOutputStream outputStream, boolean willMergeJars, Map<String, List<String>> providers, Set<String> packages) throws IOException {
317+
private byte[] copyAndExtractProviders(JarInputStream inputStream, JarOutputStream outputStream, List<String> removedPackages, boolean willMergeJars, Map<String, List<String>> providers, Set<String> packages) throws IOException {
318318
JarEntry jarEntry = inputStream.getNextJarEntry();
319319
byte[] existingModuleInfo = null;
320320
while (jarEntry != null) {
@@ -334,25 +334,26 @@ private byte[] copyAndExtractProviders(JarInputStream inputStream, JarOutputStre
334334
existingModuleInfo = content;
335335
} else if (!JAR_SIGNATURE_PATH.matcher(entryName).matches() && !"META-INF/MANIFEST.MF".equals(entryName)) {
336336
if (!willMergeJars || !isFileInServicesFolder) { // service provider files will be merged later
337-
jarEntry.setCompressedSize(-1);
338-
try {
339-
outputStream.putNextEntry(jarEntry);
340-
outputStream.write(content);
341-
outputStream.closeEntry();
342-
} catch (ZipException e) {
343-
if (!e.getMessage().startsWith("duplicate entry:")) {
344-
throw new RuntimeException(e);
337+
Matcher mrJarMatcher = MRJAR_VERSIONS_PATH.matcher(entryName);
338+
int i = entryName.lastIndexOf("/");
339+
String packagePath = i > 0 ? mrJarMatcher.matches()
340+
? mrJarMatcher.group(1)
341+
: entryName.substring(0, i)
342+
: "";
343+
344+
if (!removedPackages.contains(packagePath.replace('/', '.'))) {
345+
if (entryName.endsWith(".class") && !packagePath.isEmpty()) {
346+
packages.add(packagePath);
345347
}
346-
}
347-
if (entryName.endsWith(".class")) {
348-
int i = entryName.lastIndexOf("/");
349-
if (i > 0) {
350-
Matcher mrJarMatcher = MRJAR_VERSIONS_PATH.matcher(entryName);
351-
if (mrJarMatcher.matches()) {
352-
// Strip the 'META-INF/versions/11' part
353-
packages.add(mrJarMatcher.group(1));
354-
} else {
355-
packages.add(entryName.substring(0, i));
348+
349+
try {
350+
jarEntry.setCompressedSize(-1);
351+
outputStream.putNextEntry(jarEntry);
352+
outputStream.write(content);
353+
outputStream.closeEntry();
354+
} catch (ZipException e) {
355+
if (!e.getMessage().startsWith("duplicate entry:")) {
356+
throw new RuntimeException(e);
356357
}
357358
}
358359
}
@@ -497,7 +498,7 @@ private void mergeJars(ModuleSpec moduleSpec, JarOutputStream outputStream, Map<
497498

498499
if (mergeJarFile != null) {
499500
try (JarInputStream toMergeInputStream = new JarInputStream(Files.newInputStream(mergeJarFile.getAsFile().toPath()))) {
500-
copyAndExtractProviders(toMergeInputStream, outputStream, true, providers, packages);
501+
copyAndExtractProviders(toMergeInputStream, outputStream, moduleSpec.getRemovedPackages(), true, providers, packages);
501502
}
502503
} else {
503504
throw new RuntimeException("Jar not found: " + identifier);

src/main/java/org/gradlex/javamodule/moduleinfo/ModuleSpec.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public abstract class ModuleSpec implements Serializable {
3535
private final String identifier;
3636
private final String classifier; // optional
3737
private final String moduleName;
38+
private final List<String> removedPackages = new ArrayList<>();
3839
private final List<String> mergedJars = new ArrayList<>();
3940

4041
boolean overrideModuleName;
@@ -73,6 +74,20 @@ public String getModuleName() {
7374
return moduleName;
7475
}
7576

77+
/**
78+
* @param packageName a package to remove from the Jar because it is a duplicate
79+
*/
80+
public void removePackage(String packageName) {
81+
removedPackages.add(packageName);
82+
}
83+
84+
/**
85+
* @return packages that are removed by the transform
86+
*/
87+
public List<String> getRemovedPackages() {
88+
return removedPackages;
89+
}
90+
7691
/**
7792
* @param identifier group:name coordinates _or_ Jar file name (of the Jar file to merge)
7893
*/
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.gradlex.javamodule.moduleinfo.test
2+
3+
import org.gradlex.javamodule.moduleinfo.test.fixture.GradleBuild
4+
import org.gradlex.javamodule.moduleinfo.test.fixture.LegacyLibraries
5+
import spock.lang.Specification
6+
7+
class RemovePackageFunctionalTest extends Specification {
8+
9+
@Delegate
10+
GradleBuild build = new GradleBuild()
11+
12+
LegacyLibraries libs = new LegacyLibraries(false)
13+
14+
def setup() {
15+
settingsFile << 'rootProject.name = "test-project"'
16+
buildFile << '''
17+
plugins {
18+
id("application")
19+
id("org.gradlex.extra-java-module-info")
20+
}
21+
application {
22+
mainModule.set("org.example.app")
23+
mainClass.set("org.example.app.Main")
24+
}
25+
'''
26+
file("src/main/java/module-info.java") << """
27+
module org.example.app {
28+
requires jdk.xml.dom;
29+
requires xerces;
30+
}
31+
"""
32+
file("src/main/java/org/gradle/sample/app/Main.java") << """
33+
package org.example.app;
34+
public class Main {
35+
public static void main(String[] args) {
36+
org.apache.xerces.util.DOMUtil util;
37+
}
38+
}
39+
"""
40+
}
41+
42+
def "can remove duplicated packages"() {
43+
given:
44+
buildFile << """
45+
dependencies {
46+
implementation("xerces:xercesImpl:2.12.2") { isTransitive = false }
47+
}
48+
extraJavaModuleInfo {
49+
module("xerces:xercesImpl", "xerces") {
50+
removePackage("org.w3c.dom.html")
51+
exportAllPackages()
52+
requires("java.xml")
53+
}
54+
}
55+
"""
56+
57+
expect:
58+
run()
59+
}
60+
61+
}

0 commit comments

Comments
 (0)