Skip to content

Commit f69e83d

Browse files
committed
Allow defined and automatic modules to co-exist
In a maven project, a maven module creates two artifacts: - a main artifact with a module-info.class file that defines a JPMS module "foo.bar". The jar is called foo-bar-1.0.jar - a test artifact which is not a JPMS module. It is called foo-bar-1.0-tests.jar Another module declares dependencies on both modules: <dependencies> <dependency> <groupId>xxx</groupId> <artifactId>foo-bar</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>xxx</groupId> <artifactId>foo-bar</artifactId> <version>1.0</version> <classifier>tests</classifier> <scope>test</scope> </dependency> </dependencies> This is a common use case in large projects. The LocationManager now creates two JavaModuleDescriptors for the "foo.bar" JPMS module. One from the main artifact and its module-info.class (automatic == false) and one from the test artifact (automatic == true). The current code considers these duplicates and drops one. As a result, the test code no longer compiles because a dependency is missing. The patch separates out modules with a module descriptor and automatic modules. Then it adds the modules with module descriptors to the module path. Any duplicate is dropped for modules with module descriptors. Finally, it adds the automatic modules; if a module with the same module id already exists on the module path, it adds it to the class path. In the case above, this will allow the compile to successfully compile test code (the tests dependency drops to the class path, while the main artifact is on the module path).
1 parent 6d7f315 commit f69e83d

File tree

4 files changed

+92
-13
lines changed

4 files changed

+92
-13
lines changed

plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java

+38-12
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Map;
3535
import java.util.Map.Entry;
3636
import java.util.Set;
37+
import java.util.function.Consumer;
3738

3839
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides;
3940

@@ -249,22 +250,47 @@ public String extract(Path path) throws IOException {
249250
request.isIncludeStatic());
250251
}
251252

252-
// in case of identical module names, first one wins
253+
Map<T, JavaModuleDescriptor> definedModules = new HashMap<>();
254+
Map<T, JavaModuleDescriptor> automaticModules = new HashMap<>();
255+
256+
pathElements.forEach((k, v) -> {
257+
if (v != null && !v.isAutomatic()) {
258+
definedModules.put(k, v);
259+
} else {
260+
automaticModules.put(k, v);
261+
}
262+
});
263+
264+
// in case of identical module names, first one wins, others drop onto classpath
265+
// if they are automatic modules.
253266
Set<String> collectedModules = new HashSet<>(requiredNamedModules.size());
254267

255-
for (Entry<T, JavaModuleDescriptor> entry : pathElements.entrySet()) {
256-
if (entry.getValue() != null
257-
&& requiredNamedModules.contains(entry.getValue().name())) {
258-
if (collectedModules.add(entry.getValue().name())) {
259-
result.getModulepathElements()
260-
.put(
261-
entry.getKey(),
262-
moduleNameSources.get(entry.getValue().name()));
268+
Consumer<Map<T, JavaModuleDescriptor>> moduleAcceptor = moduleSet -> {
269+
for (Entry<T, JavaModuleDescriptor> entry : moduleSet.entrySet()) {
270+
if (entry.getValue() != null
271+
&& requiredNamedModules.contains(entry.getValue().name())) {
272+
if (collectedModules.add(entry.getValue().name())) {
273+
result.getModulepathElements()
274+
.put(
275+
entry.getKey(),
276+
moduleNameSources.get(entry.getValue().name()));
277+
// if the module is an automatic module, add it to the classpath
278+
} else if (entry.getValue().isAutomatic()) {
279+
result.getClasspathElements().add(entry.getKey());
280+
}
281+
} else {
282+
result.getClasspathElements().add(entry.getKey());
263283
}
264-
} else {
265-
result.getClasspathElements().add(entry.getKey());
266284
}
267-
}
285+
};
286+
287+
// process defined modules first. This fixes a corner case where a project creates
288+
// a main artifact that is a JPMS module and a tests artifact that is not. If the
289+
// main artifact and the test artifact happen to have the same module id (one from
290+
// the module-info, one from the automatic module naming), the main artifact will
291+
// be on the module path and the test artifact will be on the class path.
292+
moduleAcceptor.accept(definedModules);
293+
moduleAcceptor.accept(automaticModules);
268294

269295
return result;
270296
}

plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java

+54-1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,32 @@ public void testIdenticalModuleNames() throws Exception {
166166
ResolvePathsRequest<Path> request =
167167
ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava);
168168

169+
when(asmParser.getModuleDescriptor(pj1))
170+
.thenReturn(JavaModuleDescriptor.newModule("plexus.java").build());
171+
when(asmParser.getModuleDescriptor(pj2))
172+
.thenReturn(JavaModuleDescriptor.newModule("plexus.java").build());
173+
174+
ResolvePathsResult<Path> result = locationManager.resolvePaths(request);
175+
176+
assertThat(result.getMainModuleDescriptor(), is(descriptor));
177+
assertThat(result.getPathElements().size(), is(2));
178+
assertThat(result.getModulepathElements().size(), is(1));
179+
assertThat(result.getModulepathElements().containsKey(pj1), is(true));
180+
assertThat(result.getModulepathElements().containsKey(pj2), is(false));
181+
assertThat(result.getClasspathElements().size(), is(0));
182+
assertThat(result.getPathExceptions().size(), is(0));
183+
}
184+
185+
@Test
186+
public void testIdenticalAutomaticModuleNames() throws Exception {
187+
Path pj1 = Paths.get("src/test/resources/jar.empty/plexus-java-1.0.0-SNAPSHOT.jar");
188+
Path pj2 = Paths.get("src/test/resources/jar.empty.2/plexus-java-2.0.0-SNAPSHOT.jar");
189+
JavaModuleDescriptor descriptor =
190+
JavaModuleDescriptor.newModule("base").requires("plexus.java").build();
191+
when(qdoxParser.fromSourcePath(any(Path.class))).thenReturn(descriptor);
192+
ResolvePathsRequest<Path> request =
193+
ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava);
194+
169195
when(asmParser.getModuleDescriptor(pj1))
170196
.thenReturn(
171197
JavaModuleDescriptor.newAutomaticModule("plexus.java").build());
@@ -180,7 +206,34 @@ public void testIdenticalModuleNames() throws Exception {
180206
assertThat(result.getModulepathElements().size(), is(1));
181207
assertThat(result.getModulepathElements().containsKey(pj1), is(true));
182208
assertThat(result.getModulepathElements().containsKey(pj2), is(false));
183-
assertThat(result.getClasspathElements().size(), is(0));
209+
assertThat(result.getClasspathElements().size(), is(1));
210+
assertThat(result.getPathExceptions().size(), is(0));
211+
}
212+
213+
@Test
214+
public void testMainJarModuleAndTestJarAutomatic() throws Exception {
215+
Path pj1 = Paths.get("src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT.jar");
216+
Path pj2 = Paths.get("src/test/resources/jar.tests/plexus-java-1.0.0-SNAPSHOT-tests.jar");
217+
JavaModuleDescriptor descriptor =
218+
JavaModuleDescriptor.newModule("base").requires("plexus.java").build();
219+
when(qdoxParser.fromSourcePath(any(Path.class))).thenReturn(descriptor);
220+
ResolvePathsRequest<Path> request =
221+
ResolvePathsRequest.ofPaths(Arrays.asList(pj1, pj2)).setMainModuleDescriptor(mockModuleInfoJava);
222+
223+
when(asmParser.getModuleDescriptor(pj1))
224+
.thenReturn(JavaModuleDescriptor.newModule("plexus.java").build());
225+
when(asmParser.getModuleDescriptor(pj2)).thenReturn(null);
226+
227+
ResolvePathsResult<Path> result = locationManager.resolvePaths(request);
228+
229+
assertThat(result.getMainModuleDescriptor(), is(descriptor));
230+
assertThat(result.getPathElements().size(), is(2));
231+
assertThat(result.getModulepathElements().size(), is(1));
232+
assertThat(result.getModulepathElements().containsKey(pj1), is(true));
233+
assertThat(result.getModulepathElements().containsKey(pj2), is(false));
234+
assertThat(result.getClasspathElements().size(), is(1));
235+
assertThat(result.getClasspathElements().contains(pj2), is(true));
236+
assertThat(result.getClasspathElements().contains(pj1), is(false));
184237
assertThat(result.getPathExceptions().size(), is(0));
185238
}
186239

Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)