From 32d0abac303356c1005046f8c36554fc66512625 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 19 May 2026 09:22:13 +0200 Subject: [PATCH 1/2] Fix loadGlob using OR instead of AND for glob matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loadGlob method used || (OR) instead of && (AND) when matching files against prefix and suffix patterns. This caused glob patterns like "maven-*.jar" to match all files ending with .jar (since every jar matches the suffix), regardless of the prefix. This bug was introduced in 4f4cfe64d1 ("Some automatic code cleanups") where an automated refactoring incorrectly simplified the filter lambda, breaking De Morgan's law: the original negated early-returns (!prefix → false, !suffix → false, else true) are equivalent to (prefix AND suffix), not (prefix OR suffix). On Linux ext4, File.listFiles() returns non-deterministic ordering. Combined with this bug, classloading order becomes unpredictable, causing class collisions when multiple jars contain the same FQCN (e.g., PlexusXmlBeanConverter in both maven-embedder and sisu-plexus). Co-Authored-By: Claude Opus 4.6 --- .../launcher/ConfigurationParser.java | 2 +- .../launcher/ConfigurationParserTest.java | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java b/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java index 4861c09..5627e78 100644 --- a/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java +++ b/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java @@ -334,7 +334,7 @@ protected void loadGlob(String line, boolean optionally) final String suffix = localName.substring(starLoc + 1); - File[] matches = dir.listFiles((dir1, name) -> name.startsWith(prefix) || name.endsWith(suffix)); + File[] matches = dir.listFiles((dir1, name) -> name.startsWith(prefix) && name.endsWith(suffix)); if (matches != null) { for (File match : matches) { diff --git a/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java b/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java index 56e2638..c608478 100644 --- a/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java +++ b/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java @@ -1,7 +1,16 @@ package org.codehaus.plexus.classworlds.launcher; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -80,4 +89,47 @@ void filterInMiddle() throws Exception { assertEquals("cheesetest prop valuetoast", result); } + + @Test + void loadGlobMatchesBothPrefixAndSuffix(@TempDir Path tempDir) throws Exception { + Files.createFile(tempDir.resolve("maven-core.jar")); + Files.createFile(tempDir.resolve("maven-model.jar")); + Files.createFile(tempDir.resolve("sisu-plexus.jar")); + Files.createFile(tempDir.resolve("guice.jar")); + + List loaded = new ArrayList<>(); + ConfigurationHandler handler = new NoopConfigurationHandler() { + @Override + public void addLoadFile(File file) { + loaded.add(file); + } + }; + ConfigurationParser parser = new ConfigurationParser(handler, System.getProperties()); + + parser.loadGlob(tempDir.resolve("maven-*.jar").toString(), false); + + assertEquals( + 2, + loaded.size(), + "glob 'maven-*.jar' should match only files starting with 'maven-' AND ending with '.jar'"); + List names = loaded.stream().map(File::getName).sorted().collect(Collectors.toList()); + assertEquals(Arrays.asList("maven-core.jar", "maven-model.jar"), names); + } + + private abstract static class NoopConfigurationHandler implements ConfigurationHandler { + @Override + public void setAppMain(String mainClassName, String mainRealmName) {} + + @Override + public void addRealm(String realmName) {} + + @Override + public void addImportFrom(String realmName, String importSpec) {} + + @Override + public void addLoadFile(File file) {} + + @Override + public void addLoadURL(java.net.URL url) {} + } } From 9c43886c294f7111c2c51b6e10df46ddbbaa89e4 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 19 May 2026 09:48:19 +0200 Subject: [PATCH 2/2] Fix test to use File API for glob pattern (Windows compat) Path.resolve rejects '*' on Windows with InvalidPathException. Use File constructor instead, which accepts glob characters. Co-Authored-By: Claude Opus 4.6 --- .../plexus/classworlds/launcher/ConfigurationParserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java b/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java index c608478..f7d6f8a 100644 --- a/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java +++ b/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java @@ -106,7 +106,7 @@ public void addLoadFile(File file) { }; ConfigurationParser parser = new ConfigurationParser(handler, System.getProperties()); - parser.loadGlob(tempDir.resolve("maven-*.jar").toString(), false); + parser.loadGlob(new File(tempDir.toFile(), "maven-*.jar").toString(), false); assertEquals( 2,