From 56966123b3874f4bc6262b59e29ec23bdb6e84d8 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Sat, 26 Sep 2026 00:25:27 +0200 Subject: [PATCH 1/2] Read the module descriptor of a multi-release output directory --- .../jpms/AbstractBinaryModuleInfoParser.java | 29 +++++++++++++++++- .../languages/java/jpms/LocationManager.java | 6 +++- .../java/jpms/BinaryModuleInfoParserTest.java | 10 ++++++ .../java/jpms/LocationManagerTest.java | 21 +++++++++++++ .../out/META-INF/versions/9/module-info.class | Bin 0 -> 231 bytes 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 plexus-java/src/test/test-data/dir.mr.descriptor/out/META-INF/versions/9/module-info.class diff --git a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java index 0abd9251..b39bc2d0 100644 --- a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java +++ b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java @@ -39,7 +39,14 @@ public JavaModuleDescriptor getModuleDescriptor(Path modulePath) throws IOExcept public JavaModuleDescriptor getModuleDescriptor(Path modulePath, JavaVersion jdkVersion) throws IOException { JavaModuleDescriptor descriptor; if (Files.isDirectory(modulePath)) { - try (InputStream in = Files.newInputStream(modulePath.resolve("module-info.class"))) { + Path moduleInfo = modulePath.resolve("module-info.class"); + if (!Files.exists(moduleInfo)) { + Path versionedModuleInfo = findVersionedModuleInfo(modulePath, jdkVersion); + if (versionedModuleInfo != null) { + moduleInfo = versionedModuleInfo; + } + } + try (InputStream in = Files.newInputStream(moduleInfo)) { descriptor = parse(in); } } else { @@ -82,5 +89,25 @@ public JavaModuleDescriptor getModuleDescriptor(Path modulePath, JavaVersion jdk return descriptor; } + /** + * Finds the module descriptor of a multi-release output directory, which has no {@code module-info.class} at its + * root but under {@code META-INF/versions/}, the way a multi-release jar does. + * + * @param directory the output directory + * @param jdkVersion the highest version to consider + * @return the path of the highest applicable versioned descriptor, or {@code null} if there is none + */ + static Path findVersionedModuleInfo(Path directory, JavaVersion jdkVersion) { + int javaVersion = Integer.parseInt(jdkVersion.asMajor().getValue(1)); + + for (int version = javaVersion; version >= 9; version--) { + Path moduleInfo = directory.resolve("META-INF/versions/" + version + "/module-info.class"); + if (Files.exists(moduleInfo)) { + return moduleInfo; + } + } + return null; + } + abstract JavaModuleDescriptor parse(InputStream in) throws IOException; } diff --git a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java index 2c866cf7..632a9e3e 100644 --- a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java +++ b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java @@ -36,6 +36,7 @@ import java.util.Set; import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides; +import org.codehaus.plexus.languages.java.version.JavaVersion; /** * Maps artifacts to modules and analyzes the type of required modules @@ -323,7 +324,10 @@ private ResolvePathResult resolvePath( "'" + path + "' not allowed on the path, only outputDirectories and jars are accepted"); } - if (Files.isRegularFile(path) || Files.exists(path.resolve("module-info.class"))) { + if (Files.isRegularFile(path) + || Files.exists(path.resolve("module-info.class")) + || AbstractBinaryModuleInfoParser.findVersionedModuleInfo(path, JavaVersion.JAVA_SPECIFICATION_VERSION) + != null) { moduleDescriptor = binaryParser.getModuleDescriptor(path); } diff --git a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java index bba54b7f..eca787af 100644 --- a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java +++ b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java @@ -93,6 +93,16 @@ void classicJar() throws Exception { assertNull(descriptor); } + @Test + void multiReleaseOutputDirectoryDescriptor() throws Exception { + JavaModuleDescriptor descriptor = parser.getModuleDescriptor( + Paths.get("src/test/test-data/dir.mr.descriptor/out"), JavaVersion.parse("17")); + + assertNotNull(descriptor); + assertEquals("org.codehaus.plexus.languages.java.demo", descriptor.name()); + assertFalse(descriptor.isAutomatic()); + } + @Test void outputDirectoryDescriptor() throws Exception { JavaModuleDescriptor descriptor = diff --git a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java index 0fe8f292..a99f214f 100644 --- a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java +++ b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java @@ -106,6 +106,27 @@ void manifestWithReflectRequires() throws Exception { assertThat(result.getPathExceptions()).isEmpty(); } + @Test + void multiReleaseDirDescriptor() throws Exception { + Path abc = Paths.get("src/test/test-data/dir.mr.descriptor/out"); + JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule("base") + .requires("dir.mr.descriptor") + .build(); + when(sourceParser.fromSourcePath(any(Path.class))).thenReturn(descriptor); + ResolvePathsRequest request = + ResolvePathsRequest.ofPaths(Collections.singletonList(abc)).setMainModuleDescriptor(mockModuleInfoJava); + + when(asmParser.getModuleDescriptor(abc)) + .thenReturn(JavaModuleDescriptor.newModule("dir.mr.descriptor").build()); + + ResolvePathsResult result = locationManager.resolvePaths(request); + + assertThat(result.getModulepathElements()).hasSize(1); + assertThat(result.getModulepathElements().get(abc)).isEqualTo(ModuleNameSource.MODULEDESCRIPTOR); + assertThat(result.getClasspathElements()).isEmpty(); + assertThat(result.getPathExceptions()).isEmpty(); + } + @Test void dirDescriptorWithReflectRequires() throws Exception { Path abc = Paths.get("src/test/test-data/dir.descriptor/out"); diff --git a/plexus-java/src/test/test-data/dir.mr.descriptor/out/META-INF/versions/9/module-info.class b/plexus-java/src/test/test-data/dir.mr.descriptor/out/META-INF/versions/9/module-info.class new file mode 100644 index 0000000000000000000000000000000000000000..653dc58889796ac3fb3df8b695280a31408454cd GIT binary patch literal 231 zcmYk0I}XAy5JYDaNCM%j+<=A(cNBC~bX1(cB*p~USwv(+$+;*v0Ea>t)3C+NXta8} zulM5#V2N>xKB2#3cPZ7THi|H@Tsfl_b#q{Gl2=JcwxKGJ7Z?y|RbW_PM9BYk33I+^ zQF5h@(zW7j)XkfbjdoJ2Hn2pcEE9TPuGq`g-^)V2SwonXY=vglMtP34+}r{3kNze? Y Date: Sat, 26 Sep 2026 00:34:09 +0200 Subject: [PATCH 2/2] Skip the multi-release directory resolution test on Java 8 --- .../plexus/languages/java/jpms/LocationManagerTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java index a99f214f..69df2f1d 100644 --- a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java +++ b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java @@ -29,6 +29,8 @@ import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires.JavaModifier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -107,6 +109,7 @@ void manifestWithReflectRequires() throws Exception { } @Test + @DisabledOnJre(value = JRE.JAVA_8, disabledReason = "Versioned descriptors are only read on Java 9+") void multiReleaseDirDescriptor() throws Exception { Path abc = Paths.get("src/test/test-data/dir.mr.descriptor/out"); JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule("base")