Skip to content

Commit b932dd8

Browse files
committed
Add 'exportAllPackages' option for module() patching
See #38
1 parent eb45615 commit b932dd8

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,13 @@
4747
import java.nio.file.Files;
4848
import java.util.ArrayList;
4949
import java.util.Collection;
50+
import java.util.Collections;
51+
import java.util.TreeSet;
5052
import java.util.LinkedHashMap;
5153
import java.util.List;
5254
import java.util.Map;
5355
import java.util.Optional;
56+
import java.util.Set;
5457
import java.util.jar.JarEntry;
5558
import java.util.jar.JarInputStream;
5659
import java.util.jar.JarOutputStream;
@@ -197,8 +200,9 @@ private void addAutomaticModuleName(File originalJar, File moduleJar, AutomaticM
197200
manifest.getMainAttributes().putValue("Automatic-Module-Name", automaticModule.getModuleName());
198201
try (JarOutputStream outputStream = new JarOutputStream(Files.newOutputStream(moduleJar.toPath()), manifest)) {
199202
Map<String, List<String>> providers = new LinkedHashMap<>();
200-
copyAndExtractProviders(inputStream, outputStream, !automaticModule.getMergedJars().isEmpty(), providers);
201-
mergeJars(automaticModule, outputStream, providers);
203+
Set<String> packages = new TreeSet<>();
204+
copyAndExtractProviders(inputStream, outputStream, !automaticModule.getMergedJars().isEmpty(), providers, packages);
205+
mergeJars(automaticModule, outputStream, providers, packages);
202206
}
203207
} catch (IOException e) {
204208
throw new RuntimeException(e);
@@ -209,10 +213,12 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
209213
try (JarInputStream inputStream = new JarInputStream(Files.newInputStream(originalJar.toPath()))) {
210214
try (JarOutputStream outputStream = newJarOutputStream(Files.newOutputStream(moduleJar.toPath()), inputStream.getManifest())) {
211215
Map<String, List<String>> providers = new LinkedHashMap<>();
212-
copyAndExtractProviders(inputStream, outputStream, !moduleInfo.getMergedJars().isEmpty(), providers);
213-
mergeJars(moduleInfo, outputStream, providers);
216+
Set<String> packages = new TreeSet<>();
217+
copyAndExtractProviders(inputStream, outputStream, !moduleInfo.getMergedJars().isEmpty(), providers, packages);
218+
mergeJars(moduleInfo, outputStream, providers, packages);
214219
outputStream.putNextEntry(new JarEntry("module-info.class"));
215-
outputStream.write(addModuleInfo(moduleInfo, providers, versionFromFilePath(originalJar.toPath())));
220+
outputStream.write(addModuleInfo(moduleInfo, providers, versionFromFilePath(originalJar.toPath()),
221+
moduleInfo.getExportAllPackages() ? packages : Collections.emptySet()));
216222
outputStream.closeEntry();
217223
}
218224
} catch (IOException e) {
@@ -224,7 +230,7 @@ private JarOutputStream newJarOutputStream(OutputStream out, @Nullable Manifest
224230
return manifest == null ? new JarOutputStream(out) : new JarOutputStream(out, manifest);
225231
}
226232

227-
private void copyAndExtractProviders(JarInputStream inputStream, JarOutputStream outputStream, boolean willMergeJars, Map<String, List<String>> providers) throws IOException {
233+
private void copyAndExtractProviders(JarInputStream inputStream, JarOutputStream outputStream, boolean willMergeJars, Map<String, List<String>> providers, Set<String> packages) throws IOException {
228234
JarEntry jarEntry = inputStream.getNextJarEntry();
229235
while (jarEntry != null) {
230236
byte[] content = readAllBytes(inputStream);
@@ -238,7 +244,7 @@ private void copyAndExtractProviders(JarInputStream inputStream, JarOutputStream
238244
providers.get(key).addAll(extractImplementations(content));
239245
}
240246

241-
if (!JAR_SIGNATURE_PATH.matcher(jarEntry.getName()).matches() && !"META-INF/MANIFEST.MF".equals(jarEntry.getName())) {
247+
if (!JAR_SIGNATURE_PATH.matcher(entryName).matches() && !"META-INF/MANIFEST.MF".equals(jarEntry.getName())) {
242248
if (!willMergeJars || !isServiceProviderFile) { // service provider files will be merged later
243249
jarEntry.setCompressedSize(-1);
244250
try {
@@ -250,6 +256,12 @@ private void copyAndExtractProviders(JarInputStream inputStream, JarOutputStream
250256
throw new RuntimeException(e);
251257
}
252258
}
259+
if (entryName.endsWith(".class")) {
260+
int i = entryName.lastIndexOf("/");
261+
if (i > 0) {
262+
packages.add(entryName.substring(0, i));
263+
}
264+
}
253265
}
254266
}
255267
jarEntry = inputStream.getNextJarEntry();
@@ -266,11 +278,14 @@ private List<String> extractImplementations(byte[] content) {
266278
.collect(Collectors.toList());
267279
}
268280

269-
private byte[] addModuleInfo(ModuleInfo moduleInfo, Map<String, List<String>> providers, @Nullable String version) {
281+
private byte[] addModuleInfo(ModuleInfo moduleInfo, Map<String, List<String>> providers, @Nullable String version, Set<String> autoExportedPackages) {
270282
ClassWriter classWriter = new ClassWriter(0);
271283
classWriter.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null);
272284
String moduleVersion = moduleInfo.getModuleVersion() == null ? version : moduleInfo.getModuleVersion();
273285
ModuleVisitor moduleVisitor = classWriter.visitModule(moduleInfo.getModuleName(), Opcodes.ACC_OPEN, moduleVersion);
286+
for (String packageName : autoExportedPackages) {
287+
moduleVisitor.visitExport(packageName, 0);
288+
}
274289
for (String packageName : moduleInfo.exports) {
275290
moduleVisitor.visitExport(packageName.replace('.', '/'), 0);
276291
}
@@ -297,7 +312,7 @@ private byte[] addModuleInfo(ModuleInfo moduleInfo, Map<String, List<String>> pr
297312
return classWriter.toByteArray();
298313
}
299314

300-
private void mergeJars(ModuleSpec moduleSpec, JarOutputStream outputStream, Map<String, List<String>> providers) throws IOException {
315+
private void mergeJars(ModuleSpec moduleSpec, JarOutputStream outputStream, Map<String, List<String>> providers, Set<String> packages) throws IOException {
301316
if (moduleSpec.getMergedJars().isEmpty()) {
302317
return;
303318
}
@@ -321,7 +336,7 @@ private void mergeJars(ModuleSpec moduleSpec, JarOutputStream outputStream, Map<
321336

322337
if (mergeJarFile != null) {
323338
try (JarInputStream toMergeInputStream = new JarInputStream(Files.newInputStream(mergeJarFile.getAsFile().toPath()))) {
324-
copyAndExtractProviders(toMergeInputStream, outputStream, true, providers);
339+
copyAndExtractProviders(toMergeInputStream, outputStream, true, providers, packages);
325340
}
326341
} else {
327342
throw new RuntimeException("Jar not found: " + identifier);

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ abstract public class ModuleSpec implements Serializable {
2929
private final String identifier;
3030
private final String moduleName;
3131
private final List<String> mergedJars = new ArrayList<>();
32+
private boolean exportAllPackages;
3233

3334
ModuleSpec(String identifier, String moduleName) {
3435
this.identifier = identifier;
@@ -62,4 +63,12 @@ public void mergeJar(String identifier) {
6263
public List<String> getMergedJars() {
6364
return mergedJars;
6465
}
66+
67+
public void exportAllPackages(boolean exportAllPackages) {
68+
this.exportAllPackages = exportAllPackages;
69+
}
70+
71+
public boolean getExportAllPackages() {
72+
return exportAllPackages;
73+
}
6574
}

src/test/groovy/org/gradlex/javamodule/moduleinfo/test/AbstractFunctionalTest.groovy

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,4 +530,50 @@ abstract class AbstractFunctionalTest extends Specification {
530530
org.apache.qpid.server.management.plugin.ConfiguredObjectRegistrationImpl
531531
'''.stripIndent())
532532
}
533+
534+
def "can automatically export all packages of a legacy library"() {
535+
given:
536+
file("src/main/java/org/gradle/sample/app/Main.java") << """
537+
package org.gradle.sample.app;
538+
539+
public class Main {
540+
public static void main(String[] args) throws Exception {
541+
org.apache.commons.collections.Bag a;
542+
org.apache.commons.collections.bag.HashBag b;
543+
org.apache.commons.collections.bidimap.DualHashBidiMap c;
544+
org.apache.commons.collections.buffer.BlockingBuffer e;
545+
org.apache.commons.collections.collection.CompositeCollection f;
546+
org.apache.commons.collections.comparators.BooleanComparator g;
547+
org.apache.commons.collections.functors.AllPredicate h;
548+
org.apache.commons.collections.iterators.ArrayIterator i;
549+
org.apache.commons.collections.keyvalue.MultiKey j;
550+
org.apache.commons.collections.list.LazyList k;
551+
org.apache.commons.collections.map.FixedSizeMap l;
552+
org.apache.commons.collections.set.TypedSet m;
553+
}
554+
}
555+
"""
556+
file("src/main/java/module-info.java") << """
557+
module org.gradle.sample.app {
558+
exports org.gradle.sample.app;
559+
560+
requires org.apache.commons.collections;
561+
}
562+
"""
563+
buildFile << """
564+
dependencies {
565+
implementation("commons-collections:commons-collections:3.2.2")
566+
}
567+
568+
extraJavaModuleInfo {
569+
module("${libs.commonsCollections}", "org.apache.commons.collections") {
570+
exportAllPackages(true)
571+
}
572+
}
573+
"""
574+
575+
expect:
576+
run().task(':run').outcome == TaskOutcome.SUCCESS
577+
}
578+
533579
}

0 commit comments

Comments
 (0)