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 9a51ae6..06f5683 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 @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Consumer; import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides; @@ -249,22 +250,47 @@ public String extract(Path path) throws IOException { request.isIncludeStatic()); } - // in case of identical module names, first one wins + Map definedModules = new LinkedHashMap<>(); + Map automaticModules = new LinkedHashMap<>(); + + pathElements.forEach((k, v) -> { + if (v != null && !v.isAutomatic()) { + definedModules.put(k, v); + } else { + automaticModules.put(k, v); + } + }); + + // in case of identical module names, first one wins, others drop onto classpath + // if they are automatic modules. Set collectedModules = new HashSet<>(requiredNamedModules.size()); - for (Entry entry : pathElements.entrySet()) { - if (entry.getValue() != null - && requiredNamedModules.contains(entry.getValue().name())) { - if (collectedModules.add(entry.getValue().name())) { - result.getModulepathElements() - .put( - entry.getKey(), - moduleNameSources.get(entry.getValue().name())); + Consumer> moduleAcceptor = moduleSet -> { + for (Entry entry : moduleSet.entrySet()) { + if (entry.getValue() != null + && requiredNamedModules.contains(entry.getValue().name())) { + if (collectedModules.add(entry.getValue().name())) { + result.getModulepathElements() + .put( + entry.getKey(), + moduleNameSources.get(entry.getValue().name())); + // if the module is an automatic module, add it to the classpath + } else if (entry.getValue().isAutomatic()) { + result.getClasspathElements().add(entry.getKey()); + } + } else { + result.getClasspathElements().add(entry.getKey()); } - } else { - result.getClasspathElements().add(entry.getKey()); } - } + }; + + // process defined modules first. This fixes a corner case where a project creates + // a main artifact that is a JPMS module and a tests artifact that is not. If the + // main artifact and the test artifact happen to have the same module id (one from + // the module-info, one from the automatic module naming), the main artifact will + // be on the module path and the test artifact will be on the class path. + moduleAcceptor.accept(definedModules); + moduleAcceptor.accept(automaticModules); return result; } 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 acd825d..e9b0c90 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 @@ -161,6 +161,32 @@ void testIdenticalModuleNames() throws Exception { ResolvePathsRequest request = ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava); + when(asmParser.getModuleDescriptor(pj1)) + .thenReturn(JavaModuleDescriptor.newModule("plexus.java").build()); + when(asmParser.getModuleDescriptor(pj2)) + .thenReturn(JavaModuleDescriptor.newModule("plexus.java").build()); + + ResolvePathsResult result = locationManager.resolvePaths(request); + + assertThat(result.getMainModuleDescriptor()).isEqualTo(descriptor); + assertThat(result.getPathElements()).hasSize(2); + assertThat(result.getModulepathElements()).hasSize(1); + assertThat(result.getModulepathElements()).containsKey(pj1); + assertThat(result.getModulepathElements()).doesNotContainKey(pj2); + assertThat(result.getClasspathElements()).isEmpty(); + assertThat(result.getPathExceptions()).isEmpty(); + } + + @Test + public void testIdenticalAutomaticModuleNames() throws Exception { + Path pj1 = Paths.get("src/test/resources/jar.empty/plexus-java-1.0.0-SNAPSHOT.jar"); + Path pj2 = Paths.get("src/test/resources/jar.empty.2/plexus-java-2.0.0-SNAPSHOT.jar"); + JavaModuleDescriptor descriptor = + JavaModuleDescriptor.newModule("base").requires("plexus.java").build(); + when(qdoxParser.fromSourcePath(any(Path.class))).thenReturn(descriptor); + ResolvePathsRequest request = + ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava); + when(asmParser.getModuleDescriptor(pj1)) .thenReturn( JavaModuleDescriptor.newAutomaticModule("plexus.java").build()); @@ -173,8 +199,35 @@ void testIdenticalModuleNames() throws Exception { assertThat(result.getPathElements()).hasSize(2); assertThat(result.getModulepathElements()).containsOnlyKeys(pj1); assertThat(result.getModulepathElements()).doesNotContainKey(pj2); - assertThat(result.getClasspathElements()).hasSize(0); - assertThat(result.getPathExceptions()).hasSize(0); + assertThat(result.getClasspathElements()).hasSize(1); + assertThat(result.getPathExceptions()).isEmpty(); + } + + @Test + public void testMainJarModuleAndTestJarAutomatic() throws Exception { + Path pj1 = Paths.get("src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT.jar"); + Path pj2 = Paths.get("src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT-tests.jar"); + JavaModuleDescriptor descriptor = + JavaModuleDescriptor.newModule("base").requires("plexus.java").build(); + when(qdoxParser.fromSourcePath(any(Path.class))).thenReturn(descriptor); + ResolvePathsRequest request = + ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava); + + when(asmParser.getModuleDescriptor(pj1)) + .thenReturn(JavaModuleDescriptor.newModule("plexus.java").build()); + when(asmParser.getModuleDescriptor(pj2)).thenReturn(null); + + ResolvePathsResult result = locationManager.resolvePaths(request); + + assertThat(result.getMainModuleDescriptor()).isEqualTo(descriptor); + assertThat(result.getPathElements()).hasSize(2); + assertThat(result.getModulepathElements()).hasSize(1); + assertThat(result.getModulepathElements()).containsKey(pj1); + assertThat(result.getModulepathElements()).doesNotContainKey(pj2); + assertThat(result.getClasspathElements()).hasSize(1); + assertThat(result.getClasspathElements()).contains(pj2); + assertThat(result.getClasspathElements()).doesNotContain(pj1); + assertThat(result.getPathExceptions()).isEmpty(); } @Test diff --git a/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT-tests.jar b/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT-tests.jar new file mode 100644 index 0000000..a6afd0f Binary files /dev/null and b/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT-tests.jar differ diff --git a/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT.jar b/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT.jar new file mode 100644 index 0000000..413ae96 Binary files /dev/null and b/plexus-java/src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT.jar differ