Skip to content

Commit f4c38c4

Browse files
authored
Issue #11092 - Allow MetaInfConfiguration parsing of java.class.path to support globs (#12287)
* Issue #11092 - Allow MetaInfConfiguration parsing of java.class.path to support globs * Work with unwrapped URIs for uriPredicate (and test assertion)
1 parent 09b8aac commit f4c38c4

File tree

6 files changed

+85
-20
lines changed

6 files changed

+85
-20
lines changed

jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java

+24-5
Original file line numberDiff line numberDiff line change
@@ -447,21 +447,40 @@ default Resource newJarFileResource(URI uri)
447447
}
448448

449449
/**
450-
* Split a string of references, that may be split with '{@code ,}', or '{@code ;}', or '{@code |}' into URIs.
450+
* Split a string of references, that may be split with '{@code ,}', or '{@code ;}', or '{@code |}' into a List of {@link Resource}.
451451
* <p>
452-
* Each part of the input string could be path references (unix or windows style), or string URI references.
452+
* 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/*}).
453453
* </p>
454454
* <p>
455455
* 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:...!/}
456456
* </p>
457457
*
458458
* @param str the input string of references
459+
* @return list of resources
459460
*/
460461
default List<Resource> split(String str)
462+
{
463+
return split(str, ",;|");
464+
}
465+
466+
/**
467+
* Split a string of references by provided delims into a List of {@link Resource}.
468+
* <p>
469+
* 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/*}).
470+
* Note: that if you use the {@code :} character in your delims, then URI references will be impossible.
471+
* </p>
472+
* <p>
473+
* 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:...!/}
474+
* </p>
475+
*
476+
* @param str the input string of references
477+
* @return list of resources
478+
*/
479+
default List<Resource> split(String str, String delims)
461480
{
462481
List<Resource> list = new ArrayList<>();
463482

464-
StringTokenizer tokenizer = new StringTokenizer(str, ",;|");
483+
StringTokenizer tokenizer = new StringTokenizer(str, delims);
465484
while (tokenizer.hasMoreTokens())
466485
{
467486
String reference = tokenizer.nextToken();
@@ -475,7 +494,6 @@ default List<Resource> split(String str)
475494
{
476495
List<Resource> expanded = dir.list();
477496
expanded.sort(ResourceCollators.byName(true));
478-
// TODO it is unclear why non archive files are not expanded into the list
479497
expanded.stream().filter(r -> FileID.isLibArchive(r.getName())).forEach(list::add);
480498
}
481499
}
@@ -487,11 +505,12 @@ default List<Resource> split(String str)
487505
}
488506
catch (Exception e)
489507
{
490-
LOG.warn("Invalid Resource Reference: " + reference);
508+
LOG.warn("Invalid Resource Reference: {}", reference);
491509
throw e;
492510
}
493511
}
494512

513+
// Perform Archive file mounting (if needed)
495514
for (ListIterator<Resource> i = list.listIterator(); i.hasNext(); )
496515
{
497516
Resource resource = i.next();

jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceFactoryTest.java

+49-3
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ public void testSplitOnSemicolon()
405405
}
406406

407407
@Test
408-
public void testSplitOnPipeWithGlob() throws IOException
408+
public void testSplitOnPathSeparatorWithGlob() throws IOException
409409
{
410410
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
411411
{
@@ -418,9 +418,54 @@ public void testSplitOnPipeWithGlob() throws IOException
418418
FS.ensureDirExists(bar);
419419
Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-foo.jar"));
420420
Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-zed.zip"));
421+
Path exampleJar = base.resolve("example.jar");
422+
Files.copy(MavenPaths.findTestResourceFile("example.jar"), exampleJar);
423+
424+
// This represents a classpath with a glob
425+
String config = String.join(File.pathSeparator, List.of(
426+
dir.toString(), foo.toString(), bar + File.separator + "*", exampleJar.toString()
427+
));
428+
429+
// Split using commas
430+
List<URI> uris = resourceFactory.split(config, File.pathSeparator).stream().map(Resource::getURI).toList();
431+
432+
URI[] expected = new URI[]{
433+
dir.toUri(),
434+
foo.toUri(),
435+
// Should see the two archives as `jar:file:` URI entries
436+
URIUtil.toJarFileUri(bar.resolve("lib-foo.jar").toUri()),
437+
URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri()),
438+
URIUtil.toJarFileUri(exampleJar.toUri())
439+
};
440+
441+
assertThat(uris, contains(expected));
442+
}
443+
}
444+
445+
@ParameterizedTest
446+
@ValueSource(strings = {";", "|", ","})
447+
public void testSplitOnDelimWithGlob(String delimChar) throws IOException
448+
{
449+
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
450+
{
451+
// TIP: don't allow raw delim to show up in base dir, otherwise the string split later will be wrong.
452+
Path base = MavenPaths.targetTestDir("testSplitOnPipeWithGlob_%02x".formatted((byte)delimChar.charAt(0)));
453+
FS.ensureEmpty(base);
454+
Path dir = base.resolve("dir");
455+
FS.ensureDirExists(dir);
456+
Path foo = dir.resolve("foo");
457+
FS.ensureDirExists(foo);
458+
Path bar = dir.resolve("bar");
459+
FS.ensureDirExists(bar);
460+
Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-foo.jar"));
461+
Files.copy(MavenPaths.findTestResourceFile("jar-file-resource.jar"), bar.resolve("lib-zed.zip"));
462+
Path exampleJar = base.resolve("example.jar");
463+
Files.copy(MavenPaths.findTestResourceFile("example.jar"), exampleJar);
421464

422465
// This represents the user-space raw configuration with a glob
423-
String config = String.format("%s;%s;%s%s*", dir, foo, bar, File.separator);
466+
String config = String.join(delimChar, List.of(
467+
dir.toString(), foo.toString(), bar + File.separator + "*", exampleJar.toString()
468+
));
424469

425470
// Split using commas
426471
List<URI> uris = resourceFactory.split(config).stream().map(Resource::getURI).toList();
@@ -430,7 +475,8 @@ public void testSplitOnPipeWithGlob() throws IOException
430475
foo.toUri(),
431476
// Should see the two archives as `jar:file:` URI entries
432477
URIUtil.toJarFileUri(bar.resolve("lib-foo.jar").toUri()),
433-
URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri())
478+
URIUtil.toJarFileUri(bar.resolve("lib-zed.zip").toUri()),
479+
URIUtil.toJarFileUri(exampleJar.toUri())
434480
};
435481

436482
assertThat(uris, contains(expected));

jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,10 @@ public void findAndFilterContainerPaths(final WebAppContext context) throws Exce
166166
String classPath = System.getProperty("java.class.path");
167167
if (classPath != null)
168168
{
169-
Stream.of(classPath.split(File.pathSeparator))
170-
.map(resourceFactory::newResource)
169+
resourceFactory.split(classPath, File.pathSeparator)
170+
.stream()
171171
.filter(Objects::nonNull)
172-
.filter(r -> uriPatternPredicate.test(r.getURI()))
172+
.filter(r -> uriPatternPredicate.test(URIUtil.unwrapContainer(r.getURI())))
173173
.forEach(addContainerResource);
174174
}
175175

jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ public void testGetContainerPathsWithModuleSystem() throws Exception
557557
.stream()
558558
.sorted(ResourceCollators.byName(true))
559559
.map(Resource::getURI)
560+
.map(URIUtil::unwrapContainer)
560561
.map(URI::toASCIIString)
561562
.toList();
562563
// we "correct" the bad file URLs that come from the ClassLoader

jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,10 @@ public void findAndFilterContainerPaths(final WebAppContext context) throws Exce
169169
String classPath = System.getProperty("java.class.path");
170170
if (classPath != null)
171171
{
172-
Stream.of(classPath.split(File.pathSeparator))
173-
.map(resourceFactory::newResource)
174-
.filter(r -> uriPatternPredicate.test(r.getURI()))
172+
resourceFactory.split(classPath, File.pathSeparator)
173+
.stream()
174+
.filter(Objects::nonNull)
175+
.filter(r -> uriPatternPredicate.test(URIUtil.unwrapContainer(r.getURI())))
175176
.forEach(addContainerResource);
176177
}
177178

jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/MetaInfConfigurationTest.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,11 @@
2020

2121
import org.eclipse.jetty.server.Server;
2222
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
23-
import org.eclipse.jetty.util.resource.FileSystemPool;
23+
import org.eclipse.jetty.util.URIUtil;
24+
import org.eclipse.jetty.util.component.LifeCycle;
2425
import org.eclipse.jetty.util.resource.Resource;
25-
import org.junit.jupiter.api.AfterEach;
26-
import org.junit.jupiter.api.BeforeEach;
2726
import org.junit.jupiter.api.Test;
2827

29-
import static org.hamcrest.MatcherAssert.assertThat;
30-
import static org.hamcrest.Matchers.empty;
3128
import static org.junit.jupiter.api.Assertions.assertEquals;
3229
import static org.junit.jupiter.api.Assertions.assertNotNull;
3330
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -153,13 +150,14 @@ public void testFindAndFilterContainerPathsJDK9() throws Exception
153150
assertEquals(2, containerResources.size());
154151
for (Resource r : containerResources)
155152
{
156-
String s = r.toString();
153+
String s = URIUtil.unwrapContainer(r.getURI()).toASCIIString();
157154
assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("servlet-api"));
158155
}
159156
}
160157
finally
161158
{
162159
config.postConfigure(context);
160+
LifeCycle.stop(context.getResourceFactory());
163161
}
164162
}
165163
}

0 commit comments

Comments
 (0)