diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java index 835434e06594..96cce9218d45 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java @@ -447,21 +447,40 @@ default Resource newJarFileResource(URI uri) } /** - * Split a string of references, that may be split with '{@code ,}', or '{@code ;}', or '{@code |}' into URIs. + * Split a string of references, that may be split with '{@code ,}', or '{@code ;}', or '{@code |}' into a List of {@link Resource}. *

- * Each part of the input string could be path references (unix or windows style), or string URI references. + * Each part of the input string could be path references (unix or windows style), string URI references, or even glob references (eg: {@code /path/to/libs/*}). *

*

* If the result of processing the input segment is a java archive, then its resulting URI will be a mountable URI as {@code jar:file:...!/} *

* * @param str the input string of references + * @return list of resources */ default List split(String str) + { + return split(str, ",;|"); + } + + /** + * Split a string of references by provided delims into a List of {@link Resource}. + *

+ * Each part of the input string could be path references (unix or windows style), string URI references, or even glob references (eg: {@code /path/to/libs/*}). + * Note: that if you use the {@code :} character in your delims, then URI references will be impossible. + *

+ *

+ * If the result of processing the input segment is a java archive, then its resulting URI will be a mountable URI as {@code jar:file:...!/} + *

+ * + * @param str the input string of references + * @return list of resources + */ + default List split(String str, String delims) { List list = new ArrayList<>(); - StringTokenizer tokenizer = new StringTokenizer(str, ",;|"); + StringTokenizer tokenizer = new StringTokenizer(str, delims); while (tokenizer.hasMoreTokens()) { String reference = tokenizer.nextToken(); @@ -475,7 +494,6 @@ default List split(String str) { List expanded = dir.list(); expanded.sort(ResourceCollators.byName(true)); - // TODO it is unclear why non archive files are not expanded into the list expanded.stream().filter(r -> FileID.isLibArchive(r.getName())).forEach(list::add); } } @@ -487,11 +505,12 @@ default List split(String str) } catch (Exception e) { - LOG.warn("Invalid Resource Reference: " + reference); + LOG.warn("Invalid Resource Reference: {}", reference); throw e; } } + // Perform Archive file mounting (if needed) for (ListIterator i = list.listIterator(); i.hasNext(); ) { Resource resource = i.next(); diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceFactoryTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceFactoryTest.java index 75febbcc8836..1b9e9e7cb1a7 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceFactoryTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceFactoryTest.java @@ -405,7 +405,7 @@ public void testSplitOnSemicolon() } @Test - public void testSplitOnPipeWithGlob() throws IOException + public void testSplitOnPathSeparatorWithGlob() throws IOException { try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { @@ -418,9 +418,54 @@ public void testSplitOnPipeWithGlob() throws IOException FS.ensureDirExists(bar); Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-foo.jar")); Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-zed.zip")); + Path exampleJar = base.resolve("example.jar"); + Files.copy(MavenPaths.findTestResourceFile("example.jar"), exampleJar); + + // This represents a classpath with a glob + String config = String.join(File.pathSeparator, List.of( + dir.toString(), foo.toString(), bar + File.separator + "*", exampleJar.toString() + )); + + // Split using commas + List uris = resourceFactory.split(config, File.pathSeparator).stream().map(Resource::getURI).toList(); + + URI[] expected = new URI[]{ + dir.toUri(), + foo.toUri(), + // Should see the two archives as `jar:file:` URI entries + URIUtil.toJarFileUri(bar.resolve("lib-foo.jar").toUri()), + URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri()), + URIUtil.toJarFileUri(exampleJar.toUri()) + }; + + assertThat(uris, contains(expected)); + } + } + + @ParameterizedTest + @ValueSource(strings = {";", "|", ","}) + public void testSplitOnDelimWithGlob(String delimChar) throws IOException + { + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + // TIP: don't allow raw delim to show up in base dir, otherwise the string split later will be wrong. + Path base = MavenPaths.targetTestDir("testSplitOnPipeWithGlob_%02x".formatted((byte)delimChar.charAt(0))); + FS.ensureEmpty(base); + Path dir = base.resolve("dir"); + FS.ensureDirExists(dir); + Path foo = dir.resolve("foo"); + FS.ensureDirExists(foo); + Path bar = dir.resolve("bar"); + FS.ensureDirExists(bar); + Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-foo.jar")); + Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-zed.zip")); + Path exampleJar = base.resolve("example.jar"); + Files.copy(MavenPaths.findTestResourceFile("example.jar"), exampleJar); // This represents the user-space raw configuration with a glob - String config = String.format("%s;%s;%s%s*", dir, foo, bar, File.separator); + String config = String.join(delimChar, List.of( + dir.toString(), foo.toString(), bar + File.separator + "*", exampleJar.toString() + )); // Split using commas List uris = resourceFactory.split(config).stream().map(Resource::getURI).toList(); @@ -430,7 +475,8 @@ public void testSplitOnPipeWithGlob() throws IOException foo.toUri(), // Should see the two archives as `jar:file:` URI entries URIUtil.toJarFileUri(bar.resolve("lib-foo.jar").toUri()), - URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri()) + URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri()), + URIUtil.toJarFileUri(exampleJar.toUri()) }; assertThat(uris, contains(expected)); diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java index d91878512451..c81d61f6f742 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java @@ -166,10 +166,10 @@ public void findAndFilterContainerPaths(final WebAppContext context) throws Exce String classPath = System.getProperty("java.class.path"); if (classPath != null) { - Stream.of(classPath.split(File.pathSeparator)) - .map(resourceFactory::newResource) + resourceFactory.split(classPath, File.pathSeparator) + .stream() .filter(Objects::nonNull) - .filter(r -> uriPatternPredicate.test(r.getURI())) + .filter(r -> uriPatternPredicate.test(URIUtil.unwrapContainer(r.getURI()))) .forEach(addContainerResource); } diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java index 3d54cf02593d..df9d2b5aa506 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java @@ -557,6 +557,7 @@ public void testGetContainerPathsWithModuleSystem() throws Exception .stream() .sorted(ResourceCollators.byName(true)) .map(Resource::getURI) + .map(URIUtil::unwrapContainer) .map(URI::toASCIIString) .toList(); // we "correct" the bad file URLs that come from the ClassLoader diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java index 8cea3d13a8d3..3c4799de97fe 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java @@ -169,9 +169,10 @@ public void findAndFilterContainerPaths(final WebAppContext context) throws Exce String classPath = System.getProperty("java.class.path"); if (classPath != null) { - Stream.of(classPath.split(File.pathSeparator)) - .map(resourceFactory::newResource) - .filter(r -> uriPatternPredicate.test(r.getURI())) + resourceFactory.split(classPath, File.pathSeparator) + .stream() + .filter(Objects::nonNull) + .filter(r -> uriPatternPredicate.test(URIUtil.unwrapContainer(r.getURI()))) .forEach(addContainerResource); } diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/MetaInfConfigurationTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/MetaInfConfigurationTest.java index df1f8ca66bec..34a51ff1883b 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/MetaInfConfigurationTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/MetaInfConfigurationTest.java @@ -20,14 +20,11 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -import org.eclipse.jetty.util.resource.FileSystemPool; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -153,13 +150,14 @@ public void testFindAndFilterContainerPathsJDK9() throws Exception assertEquals(2, containerResources.size()); for (Resource r : containerResources) { - String s = r.toString(); + String s = URIUtil.unwrapContainer(r.getURI()).toASCIIString(); assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("servlet-api")); } } finally { config.postConfigure(context); + LifeCycle.stop(context.getResourceFactory()); } } }