Skip to content

Commit 760376c

Browse files
committed
Restore check for jar root existence (now via getEntryName/getJarEntry)
Closes gh-34607
1 parent 5b6abe4 commit 760376c

File tree

3 files changed

+30
-20
lines changed

3 files changed

+30
-20
lines changed

spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public boolean exists() {
5656
// Try a URL connection content-length header
5757
URLConnection con = url.openConnection();
5858
customizeConnection(con);
59+
5960
HttpURLConnection httpCon = (con instanceof HttpURLConnection huc ? huc : null);
6061
if (httpCon != null) {
6162
httpCon.setRequestMethod("HEAD");
@@ -81,12 +82,16 @@ else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
8182
}
8283
}
8384
}
84-
// Check content-length entry but not for JarURLConnection where
85-
// this would open the jar file but effectively never close it ->
86-
// for jar entries, always fall back to stream existence instead.
87-
if (!(con instanceof JarURLConnection) && con.getContentLengthLong() > 0) {
85+
86+
if (con instanceof JarURLConnection jarCon) {
87+
// For JarURLConnection, do not check content-length but rather the
88+
// existence of the entry (or the jar root in case of no entryName).
89+
return (jarCon.getEntryName() == null || jarCon.getJarEntry() != null);
90+
}
91+
else if (con.getContentLengthLong() > 0) {
8892
return true;
8993
}
94+
9095
if (httpCon != null) {
9196
// No HTTP OK status, and no content-length header: give up
9297
httpCon.disconnect();
@@ -346,8 +351,8 @@ public long lastModified() throws IOException {
346351
*/
347352
protected void customizeConnection(URLConnection con) throws IOException {
348353
ResourceUtils.useCachesIfNecessary(con);
349-
if (con instanceof HttpURLConnection httpConn) {
350-
customizeConnection(httpConn);
354+
if (con instanceof HttpURLConnection httpCon) {
355+
customizeConnection(httpCon);
351356
}
352357
}
353358

spring-core/src/main/java/org/springframework/core/io/UrlResource.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -234,8 +234,8 @@ public InputStream getInputStream() throws IOException {
234234
}
235235
catch (IOException ex) {
236236
// Close the HTTP connection (if applicable).
237-
if (con instanceof HttpURLConnection httpConn) {
238-
httpConn.disconnect();
237+
if (con instanceof HttpURLConnection httpCon) {
238+
httpCon.disconnect();
239239
}
240240
throw ex;
241241
}

spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java

+16-11
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@
5151
import org.springframework.core.io.DefaultResourceLoader;
5252
import org.springframework.core.io.FileSystemResource;
5353
import org.springframework.core.io.Resource;
54+
import org.springframework.core.io.UrlResource;
5455
import org.springframework.util.ClassUtils;
5556
import org.springframework.util.FileSystemUtils;
57+
import org.springframework.util.ResourceUtils;
5658
import org.springframework.util.StreamUtils;
5759
import org.springframework.util.StringUtils;
5860

@@ -133,6 +135,7 @@ void encodedHashtagInPath() throws IOException {
133135
assertExactFilenames("classpath*:scanned/*.txt", "resource#test1.txt", "resource#test2.txt");
134136
}
135137

138+
136139
@Nested
137140
class WithHashtagsInTheirFilenames {
138141

@@ -299,6 +302,7 @@ void rootPatternRetrievalInJarFiles() throws IOException {
299302
}
300303
}
301304

305+
302306
@Nested
303307
class ClassPathManifestEntries {
304308

@@ -313,8 +317,8 @@ void javaDashJarFindsClassPathManifestEntries() throws Exception {
313317
writeApplicationJar(this.temp.resolve("app.jar"));
314318
String java = ProcessHandle.current().info().command().get();
315319
Process process = new ProcessBuilder(java, "-jar", "app.jar")
316-
.directory(this.temp.toFile())
317-
.start();
320+
.directory(this.temp.toFile())
321+
.start();
318322
assertThat(process.waitFor()).isZero();
319323
String result = StreamUtils.copyToString(process.getInputStream(), StandardCharsets.UTF_8);
320324
assertThat(result.replace("\\", "/")).contains("!!!!").contains("/lib/asset.jar!/assets/file.txt");
@@ -328,6 +332,8 @@ private void writeAssetJar(Path path) throws Exception {
328332
StreamUtils.copy("test", StandardCharsets.UTF_8, jar);
329333
jar.closeEntry();
330334
}
335+
assertThat(new FileSystemResource(path).exists()).isTrue();
336+
assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue();
331337
}
332338

333339
private void writeApplicationJar(Path path) throws Exception {
@@ -338,8 +344,7 @@ private void writeApplicationJar(Path path) throws Exception {
338344
mainAttributes.put(Name.MANIFEST_VERSION, "1.0");
339345
try (JarOutputStream jar = new JarOutputStream(new FileOutputStream(path.toFile()), manifest)) {
340346
String appClassResource = ClassUtils.convertClassNameToResourcePath(
341-
ClassPathManifestEntriesTestApplication.class.getName())
342-
+ ClassUtils.CLASS_FILE_SUFFIX;
347+
ClassPathManifestEntriesTestApplication.class.getName()) + ClassUtils.CLASS_FILE_SUFFIX;
343348
String folder = "";
344349
for (String name : appClassResource.split("/")) {
345350
if (!name.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) {
@@ -356,18 +361,19 @@ private void writeApplicationJar(Path path) throws Exception {
356361
}
357362
}
358363
}
364+
assertThat(new FileSystemResource(path).exists()).isTrue();
365+
assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue();
359366
}
360367

361368
private String buildSpringClassPath() throws Exception {
362-
return copyClasses(PathMatchingResourcePatternResolver.class, "spring-core")
363-
+ copyClasses(LogFactory.class, "commons-logging");
369+
return copyClasses(PathMatchingResourcePatternResolver.class, "spring-core") +
370+
copyClasses(LogFactory.class, "commons-logging");
364371
}
365372

366-
private String copyClasses(Class<?> sourceClass, String destinationName)
367-
throws URISyntaxException, IOException {
373+
private String copyClasses(Class<?> sourceClass, String destinationName) throws URISyntaxException, IOException {
368374
Path destination = this.temp.resolve(destinationName);
369-
String resourcePath = ClassUtils.convertClassNameToResourcePath(sourceClass.getName())
370-
+ ClassUtils.CLASS_FILE_SUFFIX;
375+
String resourcePath = ClassUtils.convertClassNameToResourcePath(
376+
sourceClass.getName()) + ClassUtils.CLASS_FILE_SUFFIX;
371377
URL resource = getClass().getClassLoader().getResource(resourcePath);
372378
URL url = new URL(resource.toString().replace(resourcePath, ""));
373379
URLConnection connection = url.openConnection();
@@ -393,7 +399,6 @@ private String copyClasses(Class<?> sourceClass, String destinationName)
393399
}
394400
return destinationName + "/ ";
395401
}
396-
397402
}
398403

399404

0 commit comments

Comments
 (0)