Create new JarFile instance for URL connections

Update `JarURLConnection` to ensure that when connections are opened
a new copy of the JarFile is provided.

Prior to this commit, a single `JarFile` instance was shared which meant
that it could be accidental closed if accessed via
`JarURLConnection.getJarFile()`. If the underlying jar file is closed
then it's possible for a `NoClassDefFoundError` to be thrown if running
on JDK 11 with an active `SecurityManager`.

Closes gh-17796
This commit is contained in:
Phillip Webb
2020-04-21 16:08:54 -07:00
parent 6011470ba6
commit c85918b8b3
4 changed files with 60 additions and 19 deletions

View File

@@ -205,10 +205,10 @@ public class JarFileTests {
URL url = this.jarFile.getUrl();
assertThat(url.toString()).isEqualTo("jar:" + this.rootJarFile.toURI() + "!/");
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile()).isSameAs(this.jarFile);
assertThat(jarURLConnection.getJarFile().getParent()).isSameAs(this.jarFile);
assertThat(jarURLConnection.getJarEntry()).isNull();
assertThat(jarURLConnection.getContentLength()).isGreaterThan(1);
assertThat(jarURLConnection.getContent()).isSameAs(this.jarFile);
assertThat(((JarFile) jarURLConnection.getContent()).getParent()).isSameAs(this.jarFile);
assertThat(jarURLConnection.getContentType()).isEqualTo("x-java/jar");
assertThat(jarURLConnection.getJarFileURL().toURI()).isEqualTo(this.rootJarFile.toURI());
}
@@ -218,7 +218,7 @@ public class JarFileTests {
URL url = new URL(this.jarFile.getUrl(), "1.dat");
assertThat(url.toString()).isEqualTo("jar:" + this.rootJarFile.toURI() + "!/1.dat");
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile()).isSameAs(this.jarFile);
assertThat(jarURLConnection.getJarFile().getParent()).isSameAs(this.jarFile);
assertThat(jarURLConnection.getJarEntry()).isSameAs(this.jarFile.getJarEntry("1.dat"));
assertThat(jarURLConnection.getContentLength()).isEqualTo(1);
assertThat(jarURLConnection.getContent()).isInstanceOf(InputStream.class);
@@ -273,7 +273,7 @@ public class JarFileTests {
URL url = nestedJarFile.getUrl();
assertThat(url.toString()).isEqualTo("jar:" + this.rootJarFile.toURI() + "!/nested.jar!/");
JarURLConnection conn = (JarURLConnection) url.openConnection();
assertThat(conn.getJarFile()).isSameAs(nestedJarFile);
assertThat(conn.getJarFile().getParent()).isSameAs(nestedJarFile);
assertThat(conn.getJarFileURL().toString()).isEqualTo("jar:" + this.rootJarFile.toURI() + "!/nested.jar");
assertThat(conn.getInputStream()).isNotNull();
JarInputStream jarInputStream = new JarInputStream(conn.getInputStream());
@@ -301,7 +301,7 @@ public class JarFileTests {
URL url = nestedJarFile.getUrl();
assertThat(url.toString()).isEqualTo("jar:" + this.rootJarFile.toURI() + "!/d!/");
assertThat(((JarURLConnection) url.openConnection()).getJarFile()).isSameAs(nestedJarFile);
assertThat(((JarURLConnection) url.openConnection()).getJarFile().getParent()).isSameAs(nestedJarFile);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URL;
import java.util.zip.ZipFile;
import org.junit.Before;
import org.junit.Rule;
@@ -28,6 +29,7 @@ import org.junit.rules.TemporaryFolder;
import org.springframework.boot.loader.TestJarCreator;
import org.springframework.boot.loader.jar.JarURLConnection.JarEntryName;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -58,13 +60,15 @@ public class JarURLConnectionTests {
@Test
public void connectionToRootUsingAbsoluteUrl() throws Exception {
URL url = new URL("jar:" + this.rootJarFile.toURI().toURL() + "!/");
assertThat(JarURLConnection.get(url, this.jarFile).getContent()).isSameAs(this.jarFile);
Object content = JarURLConnection.get(url, this.jarFile).getContent();
assertThat(((JarFile) content).getParent()).isSameAs(this.jarFile);
}
@Test
public void connectionToRootUsingRelativeUrl() throws Exception {
URL url = new URL("jar:file:" + getRelativePath() + "!/");
assertThat(JarURLConnection.get(url, this.jarFile).getContent()).isSameAs(this.jarFile);
Object content = JarURLConnection.get(url, this.jarFile).getContent();
assertThat(((JarFile) content).getParent()).isSameAs(this.jarFile);
}
@Test
@@ -191,6 +195,15 @@ public class JarURLConnectionTests {
.isEqualTo("\u00e1/b/\u00c7.class");
}
@Test
public void openConnectionCanBeClosedWithoutClosingSourceJar() throws Exception {
URL url = new URL("jar:" + this.rootJarFile.toURI().toURL() + "!/");
JarURLConnection connection = JarURLConnection.get(url, this.jarFile);
JarFile connectionJarFile = connection.getJarFile();
connectionJarFile.close();
assertThat((Boolean) ReflectionTestUtils.getField(this.jarFile, ZipFile.class, "closeRequested")).isFalse();
}
private String getRelativePath() {
return this.rootJarFile.getPath().replace('\\', '/');
}