Skip to content

Commit 82791b4

Browse files
committed
Improve performance of Tomcat 'jar:war:file' URLs
Update jar `Handler` fallback logic to directly support Tomcat 'jar:war:file' URLs. This commit allows contents to be accessed without the JDK needing to extracted the nested jar to the temporary folder. Closes gh-24553
1 parent e0522d9 commit 82791b4

File tree

3 files changed

+53
-2
lines changed
  • spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar
  • spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests

3 files changed

+53
-2
lines changed

spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class Handler extends URLStreamHandler {
4747

4848
private static final String FILE_PROTOCOL = "file:";
4949

50+
private static final String TOMCAT_WARFILE_PROTOCOL = "war:file:";
51+
5052
private static final String SEPARATOR = "!/";
5153

5254
private static final Pattern SEPARATOR_PATTERN = Pattern.compile(SEPARATOR, Pattern.LITERAL);
@@ -102,7 +104,8 @@ private boolean isUrlInJarFile(URL url, JarFile jarFile) throws MalformedURLExce
102104

103105
private URLConnection openFallbackConnection(URL url, Exception reason) throws IOException {
104106
try {
105-
URLConnection connection = openFallbackContextConnection(url);
107+
URLConnection connection = openFallbackTomcatConnection(url);
108+
connection = (connection != null) ? connection : openFallbackContextConnection(url);
106109
return (connection != null) ? connection : openFallbackHandlerConnection(url);
107110
}
108111
catch (Exception ex) {
@@ -118,6 +121,44 @@ private URLConnection openFallbackConnection(URL url, Exception reason) throws I
118121
}
119122
}
120123

124+
/**
125+
* Attempt to open a Tomcat formatted 'jar:war:file:...' URL. This method allows us to
126+
* use our own nested JAR support to open the content rather than the logic in
127+
* {@code sun.net.www.protocol.jar.URLJarFile} which will extract the nested jar to
128+
* the temp folder to that its content can be accessed.
129+
* @param url the URL to open
130+
* @return a {@link URLConnection} or {@code null}
131+
*/
132+
private URLConnection openFallbackTomcatConnection(URL url) {
133+
String file = url.getFile();
134+
if (isTomcatWarUrl(file)) {
135+
file = file.substring(TOMCAT_WARFILE_PROTOCOL.length());
136+
file = file.replaceFirst("\\*/", "!/");
137+
try {
138+
URLConnection connection = openConnection(new URL("jar:file:" + file));
139+
connection.getInputStream().close();
140+
return connection;
141+
}
142+
catch (IOException ex) {
143+
}
144+
}
145+
return null;
146+
}
147+
148+
private boolean isTomcatWarUrl(String file) {
149+
if (file.startsWith(TOMCAT_WARFILE_PROTOCOL) || !file.contains("*/")) {
150+
try {
151+
URLConnection connection = new URL(file).openConnection();
152+
if (connection.getClass().getName().startsWith("org.apache.catalina")) {
153+
return true;
154+
}
155+
}
156+
catch (Exception ex) {
157+
}
158+
}
159+
return false;
160+
}
161+
121162
/**
122163
* Attempt to open a fallback connection by using a context URL captured before the
123164
* jar handler was replaced with our own version. Since this method doesn't use

spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-app/src/main/java/org/springframework/boot/loaderapp/LoaderTestApplication.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.loaderapp;
1818

19+
import java.io.File;
20+
import java.net.JarURLConnection;
1921
import java.net.URL;
2022
import java.util.Arrays;
2123

@@ -33,7 +35,14 @@ public class LoaderTestApplication {
3335
@Bean
3436
public CommandLineRunner commandLineRunner(ServletContext servletContext) {
3537
return (args) -> {
38+
File temp = new File(System.getProperty("java.io.tmpdir"));
3639
URL resourceUrl = servletContext.getResource("webjars/jquery/3.5.0/jquery.js");
40+
JarURLConnection connection = (JarURLConnection) resourceUrl.openConnection();
41+
String jarName = connection.getJarFile().getName();
42+
System.out.println(">>>>> jar file " + jarName);
43+
if(jarName.contains(temp.getAbsolutePath())) {
44+
System.out.println(">>>>> jar written to temp");
45+
}
3746
byte[] resourceContent = FileCopyUtils.copyToByteArray(resourceUrl.openStream());
3847
URL directUrl = new URL(resourceUrl.toExternalForm());
3948
byte[] directContent = FileCopyUtils.copyToByteArray(directUrl.openStream());

spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/java/org/springframework/boot/loader/LoaderIntegrationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ private static File findApplication() {
5959

6060
@Test
6161
void readUrlsWithoutWarning() {
62+
System.out.println(output.toUtf8String());
6263
assertThat(output.toUtf8String()).contains(">>>>> 287649 BYTES from").doesNotContain("WARNING:")
63-
.doesNotContain("illegal");
64+
.doesNotContain("illegal").doesNotContain("jar written to temp");
6465
}
6566

6667
}

0 commit comments

Comments
 (0)