Skip to content

Commit 2142a79

Browse files
authored
fix: wrong existence check in getStaticResource (CP: 25.0) (#24285)
This PR cherry-picks changes from the original PR #24283 to branch 25.0. A conflict in `flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java` was resolved manually: the new `getStaticResource_jarUrlOnJetty12_returnsUrlInsteadOfThrowing` test was rewritten in JUnit 4 idioms (`@Rule TemporaryFolder`, `Assert.*`) since 25.0 has not adopted the JUnit 5 migration that landed on `main`.
1 parent 755c836 commit 2142a79

2 files changed

Lines changed: 61 additions & 10 deletions

File tree

flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
import jakarta.servlet.ServletContext;
2020
import jakarta.servlet.http.HttpServletRequest;
2121

22+
import java.io.IOException;
2223
import java.io.InputStream;
2324
import java.net.MalformedURLException;
24-
import java.net.URISyntaxException;
2525
import java.net.URL;
26-
import java.nio.file.Files;
27-
import java.nio.file.Path;
2826
import java.util.List;
2927
import java.util.Objects;
3028
import java.util.Optional;
@@ -337,13 +335,15 @@ static URL getStaticResource(ServletContext servletContext, String path)
337335
URL url = servletContext.getResource(path);
338336
if (url != null && Optional.ofNullable(servletContext.getServerInfo())
339337
.orElse("").contains("jetty/12.")) {
340-
// Making sure that resource exists before returning it. Jetty
341-
// 12 may return URL for non-existing resource.
342-
try {
343-
if (!Files.exists(Path.of(url.toURI()))) {
344-
url = null;
345-
}
346-
} catch (URISyntaxException e) {
338+
// Jetty 12 may return URLs for non-existing resources. Probe the
339+
// URL by opening a stream: this works uniformly for file: URLs
340+
// and for jar:file:...!/entry URLs without requiring a NIO
341+
// FileSystem to be mounted for the JAR (which Path.of(jarUri)
342+
// would need, breaking on Jetty 12.1.9 where classpath JARs are
343+
// no longer kept mounted in the JVM-wide cache).
344+
try (InputStream probe = url.openStream()) {
345+
// resource exists
346+
} catch (IOException e) {
347347
url = null;
348348
}
349349
}

flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,26 @@
2121

2222
import java.io.IOException;
2323
import java.net.MalformedURLException;
24+
import java.net.URI;
2425
import java.net.URL;
26+
import java.nio.charset.StandardCharsets;
27+
import java.nio.file.Files;
28+
import java.nio.file.Path;
2529
import java.util.Collections;
2630
import java.util.HashMap;
2731
import java.util.List;
2832
import java.util.Map;
33+
import java.util.UUID;
2934
import java.util.concurrent.locks.ReentrantLock;
35+
import java.util.jar.JarEntry;
36+
import java.util.jar.JarOutputStream;
3037

3138
import org.junit.After;
3239
import org.junit.Assert;
3340
import org.junit.Before;
41+
import org.junit.Rule;
3442
import org.junit.Test;
43+
import org.junit.rules.TemporaryFolder;
3544
import org.mockito.Mockito;
3645

3746
import com.vaadin.flow.di.Instantiator;
@@ -63,6 +72,9 @@ public String getThemeUrl() {
6372
private TestVaadinServletService service;
6473
private VaadinServlet servlet;
6574

75+
@Rule
76+
public TemporaryFolder tempFolder = new TemporaryFolder();
77+
6678
@Before
6779
public void setup() throws Exception {
6880
mocks = new MockServletServiceSessionSetup();
@@ -328,6 +340,45 @@ protected List<VaadinRequestInterceptor> createVaadinRequestInterceptors()
328340
request.getAttribute("ended"));
329341
}
330342

343+
@Test
344+
public void getStaticResource_jarUrlOnJetty12_returnsUrlInsteadOfThrowing()
345+
throws Exception {
346+
// Reproduces the regression caused by Jetty 12.1.9 where
347+
// ServletContext.getResource() returns a jar:file:... URL for
348+
// resources packaged inside a JAR (e.g. vaadinPush.js in flow-push).
349+
// Path.of(jar:URI) throws FileSystemNotFoundException because no
350+
// FileSystem has been opened for the JAR, and the existence check in
351+
// VaadinServletService.getStaticResource catches only
352+
// URISyntaxException, so the unchecked exception escapes and the
353+
// request fails with HTTP 500.
354+
String resourcePath = "META-INF/resources/VAADIN/static/push/vaadinPush.js";
355+
Path jarFile = tempFolder
356+
.newFile("flow-push-" + UUID.randomUUID() + ".jar").toPath();
357+
try (JarOutputStream jar = new JarOutputStream(
358+
Files.newOutputStream(jarFile))) {
359+
jar.putNextEntry(new JarEntry(resourcePath));
360+
jar.write("// push".getBytes(StandardCharsets.UTF_8));
361+
jar.closeEntry();
362+
}
363+
364+
URL jarResourceUrl = URI
365+
.create("jar:" + jarFile.toUri() + "!/" + resourcePath).toURL();
366+
367+
ServletContext servletContext = Mockito.mock(ServletContext.class);
368+
when(servletContext.getServerInfo()).thenReturn("jetty/12.1.9");
369+
when(servletContext.getResource("/VAADIN/static/push/vaadinPush.js"))
370+
.thenReturn(jarResourceUrl);
371+
372+
URL resolved = VaadinServletService.getStaticResource(servletContext,
373+
"/VAADIN/static/push/vaadinPush.js");
374+
375+
Assert.assertNotNull(
376+
"An existing resource served from a JAR by Jetty 12 must be returned, "
377+
+ "not lost to FileSystemNotFoundException",
378+
resolved);
379+
Assert.assertEquals(jarResourceUrl, resolved);
380+
}
381+
331382
static class ExceptionThrowingRequestHandler implements RequestHandler {
332383

333384
@Override

0 commit comments

Comments
 (0)