diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
index e41f8b8127a..4f593509c80 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
@@ -714,6 +714,42 @@ public class URIUtil
return buf.toString();
}
+ /**
+ * Given a URI, attempt to get the last segment.
+ *
+ * If this is a {@code jar:file://} style URI, then
+ * the JAR filename is returned (not the deep {@code !/path} location)
+ *
+ *
+ * @param uri the URI to look in
+ * @return the last segment.
+ */
+ public static String getUriLastPathSegment(URI uri)
+ {
+ String ssp = uri.getSchemeSpecificPart();
+ // strip off deep jar:file: reference information
+ int idx = ssp.indexOf("!/");
+ if (idx != -1)
+ {
+ ssp = ssp.substring(0, idx);
+ }
+
+ // Strip off trailing '/' if present
+ if (ssp.endsWith("/"))
+ {
+ ssp = ssp.substring(0, ssp.length() - 1);
+ }
+
+ // Only interested in last segment
+ idx = ssp.lastIndexOf('/');
+ if (idx != -1)
+ {
+ ssp = ssp.substring(idx + 1);
+ }
+
+ return ssp;
+ }
+
/**
* Return the parent Path.
* Treat a URI like a directory path and return the parent directory.
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
index d3ba74a0e40..1e4220b1fcd 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
@@ -18,13 +18,31 @@
package org.eclipse.jetty.util;
+import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.Normalizer;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Stream;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -40,8 +58,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
* URIUtil Tests.
*/
@SuppressWarnings("SpellCheckingInspection")
+@ExtendWith(WorkDirExtension.class)
public class URIUtilTest
{
+ private static final Logger LOG = Log.getLogger(URIUtilTest.class);
+ public WorkDir workDir;
+
public static Stream encodePathSource()
{
return Stream.of(
@@ -591,4 +613,97 @@ public class URIUtilTest
{
assertThat(URIUtil.decodeSpecific(raw, chars), is(expected));
}
+
+ public static Stream resourceUriLastSegmentSource()
+ {
+ return Stream.of(
+ Arguments.of("test.war", "test.war"),
+ Arguments.of("a/b/c/test.war", "test.war"),
+ Arguments.of("bar%2Fbaz/test.war", "test.war"),
+ Arguments.of("fizz buzz/test.war", "test.war"),
+ Arguments.of("another one/bites the dust/", "bites the dust"),
+ Arguments.of("another+one/bites+the+dust/", "bites+the+dust"),
+ Arguments.of("another%20one/bites%20the%20dust/", "bites%20the%20dust"),
+ Arguments.of("spanish/n\u00FAmero.war", "n\u00FAmero.war"),
+ Arguments.of("spanish/n%C3%BAmero.war", "n%C3%BAmero.war"),
+ Arguments.of("a/b!/", "b"),
+ Arguments.of("a/b!/c/", "b"),
+ Arguments.of("a/b!/c/d/", "b"),
+ Arguments.of("a/b%21/", "b%21")
+ );
+ }
+
+ /**
+ * Using FileSystem provided URIs, attempt to get last URI path segment
+ */
+ @ParameterizedTest
+ @MethodSource("resourceUriLastSegmentSource")
+ public void testFileUriGetUriLastPathSegment(String basePath, String expectedName) throws IOException
+ {
+ Path root = workDir.getPath();
+ Path base = root.resolve(basePath);
+ if (basePath.endsWith("/"))
+ {
+ FS.ensureDirExists(base);
+ }
+ else
+ {
+ FS.ensureDirExists(base.getParent());
+ FS.touch(base);
+ }
+ URI uri = base.toUri();
+ if (OS.MAC.isCurrentOs())
+ {
+ // Normalize Unicode to NFD form that OSX Path/FileSystem produces
+ expectedName = Normalizer.normalize(expectedName, Normalizer.Form.NFD);
+ }
+ assertThat(URIUtil.getUriLastPathSegment(uri), is(expectedName));
+ }
+
+ public static Stream uriLastSegmentSource() throws URISyntaxException, IOException
+ {
+ final String TEST_RESOURCE_JAR = "test-base-resource.jar";
+ Path testJar = MavenTestingUtils.getTestResourcePathFile(TEST_RESOURCE_JAR);
+ URI uri = new URI("jar", testJar.toUri().toASCIIString(), null);
+
+ Map env = new HashMap<>();
+ env.put("multi-release", "runtime");
+
+ List arguments = new ArrayList<>();
+ arguments.add(Arguments.of(uri, TEST_RESOURCE_JAR));
+ try (FileSystem zipFs = FileSystems.newFileSystem(uri, env))
+ {
+ FileVisitOption[] fileVisitOptions = new FileVisitOption[]{};
+
+ for (Path root : zipFs.getRootDirectories())
+ {
+ Stream entryStream = Files.find(root, 10, (path, attrs) -> true, fileVisitOptions);
+ entryStream.forEach((path) ->
+ {
+ if (path.toString().endsWith("!/"))
+ {
+ // skip - JAR entry type not supported by Jetty
+ // TODO: re-enable once we start to use zipfs
+ LOG.warn("Skipping Unsupported entry: " + path.toUri());
+ }
+ else
+ {
+ arguments.add(Arguments.of(path.toUri(), TEST_RESOURCE_JAR));
+ }
+ });
+ }
+ }
+
+ return arguments.stream();
+ }
+
+ /**
+ * Tests of URIs last segment, including "jar:file:" based URIs.
+ */
+ @ParameterizedTest
+ @MethodSource("uriLastSegmentSource")
+ public void testGetUriLastPathSegment(URI uri, String expectedName)
+ {
+ assertThat(URIUtil.getUriLastPathSegment(uri), is(expectedName));
+ }
}
diff --git a/jetty-webapp/src/test/resources/test-base-resource.jar b/jetty-util/src/test/resources/test-base-resource.jar
similarity index 100%
rename from jetty-webapp/src/test/resources/test-base-resource.jar
rename to jetty-util/src/test/resources/test-base-resource.jar
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
index 0d3205f5fed..79cd050c84b 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
@@ -844,33 +844,7 @@ public class WebInfConfiguration extends AbstractConfiguration
return "";
}
- return getUriLastPathSegment(uri);
- }
-
- protected static String getUriLastPathSegment(URI uri)
- {
- String ssp = uri.getSchemeSpecificPart();
- // strip off deep jar:file: reference information
- int idx = ssp.indexOf("!/");
- if (idx != -1)
- {
- ssp = ssp.substring(0, idx);
- }
-
- // Strip off trailing '/' if present
- if (ssp.endsWith("/"))
- {
- ssp = ssp.substring(0, ssp.length() - 1);
- }
-
- // Only interested in last segment
- idx = ssp.lastIndexOf('/');
- if (idx != -1)
- {
- ssp = ssp.substring(idx + 1);
- }
-
- return ssp;
+ return URIUtil.getUriLastPathSegment(uri);
}
protected List findClassDirs(WebAppContext context)
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java
index 28c1f5a0aa7..446ec9e8180 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java
@@ -19,27 +19,14 @@
package org.eclipse.jetty.webapp;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.FileVisitOption;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.text.Normalizer;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.stream.Stream;
import org.eclipse.jetty.toolchain.test.FS;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.JavaVersion;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Test;
@@ -47,7 +34,6 @@ import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.JRE;
-import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -66,9 +52,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(WorkDirExtension.class)
public class WebInfConfigurationTest
{
- private static final Logger LOG = Log.getLogger(WebInfConfigurationTest.class);
- private static final String TEST_RESOURCE_JAR = "test-base-resource.jar";
-
public WorkDir workDir;
/**
@@ -197,96 +180,4 @@ public class WebInfConfigurationTest
Resource resource = new PathResource(base);
assertThat(WebInfConfiguration.getResourceBaseName(resource), is(expectedName));
}
-
- public static Stream fileUriBaseResourceNames()
- {
- return Stream.of(
- Arguments.of("test.war", "test.war"),
- Arguments.of("a/b/c/test.war", "test.war"),
- Arguments.of("bar%2Fbaz/test.war", "test.war"),
- Arguments.of("fizz buzz/test.war", "test.war"),
- Arguments.of("another one/bites the dust/", "bites the dust"),
- Arguments.of("another+one/bites+the+dust/", "bites+the+dust"),
- Arguments.of("another%20one/bites%20the%20dust/", "bites%20the%20dust"),
- Arguments.of("spanish/n\u00FAmero.war", "n\u00FAmero.war"),
- Arguments.of("spanish/n%C3%BAmero.war", "n%C3%BAmero.war"),
- Arguments.of("a/b!/", "b"),
- Arguments.of("a/b!/c/", "b"),
- Arguments.of("a/b!/c/d/", "b"),
- Arguments.of("a/b%21/", "b%21")
- );
- }
-
- /**
- * Similar to testPathGetResourceBaseName, but with "file:" URIs
- */
- @ParameterizedTest
- @MethodSource("fileUriBaseResourceNames")
- public void testFileUriGetUriLastPathSegment(String basePath, String expectedName) throws IOException
- {
- Path root = workDir.getPath();
- Path base = root.resolve(basePath);
- if (basePath.endsWith("/"))
- {
- FS.ensureDirExists(base);
- }
- else
- {
- FS.ensureDirExists(base.getParent());
- FS.touch(base);
- }
- URI uri = base.toUri();
- if (OS.MAC.isCurrentOs())
- {
- // Normalize Unicode to NFD form that OSX Path/FileSystem produces
- expectedName = Normalizer.normalize(expectedName, Normalizer.Form.NFD);
- }
- assertThat(WebInfConfiguration.getUriLastPathSegment(uri), is(expectedName));
- }
-
- public static Stream uriLastSegmentSource() throws URISyntaxException, IOException
- {
- Path testJar = MavenTestingUtils.getTestResourcePathFile(TEST_RESOURCE_JAR);
- URI uri = new URI("jar", testJar.toUri().toASCIIString(), null);
-
- Map env = new HashMap<>();
- env.put("multi-release", "runtime");
-
- List arguments = new ArrayList<>();
- arguments.add(Arguments.of(uri, TEST_RESOURCE_JAR));
- try (FileSystem zipFs = FileSystems.newFileSystem(uri, env))
- {
- FileVisitOption[] fileVisitOptions = new FileVisitOption[]{};
-
- for (Path root : zipFs.getRootDirectories())
- {
- Stream entryStream = Files.find(root, 10, (path, attrs) -> true, fileVisitOptions);
- entryStream.forEach((path) ->
- {
- if (path.toString().endsWith("!/"))
- {
- // skip - JAR entry type not supported by Jetty
- // TODO: re-enable once we start to use zipfs
- LOG.warn("Skipping Unsupported entry: " + path.toUri());
- }
- else
- {
- arguments.add(Arguments.of(path.toUri(), TEST_RESOURCE_JAR));
- }
- });
- }
- }
-
- return arguments.stream();
- }
-
- /**
- * Tests of URIs last segment, including "jar:file:" based URIs.
- */
- @ParameterizedTest
- @MethodSource("uriLastSegmentSource")
- public void testGetUriLastPathSegment(URI uri, String expectedName) throws IOException
- {
- assertThat(WebInfConfiguration.getUriLastPathSegment(uri), is(expectedName));
- }
}