diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java index 0e26e6a2ef..28abc20f10 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java @@ -17,17 +17,18 @@ package org.springframework.boot.loader.jar; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.net.URLStreamHandler; - import java.security.Permission; /** @@ -62,8 +63,6 @@ class JarURLConnection extends java.net.JarURLConnection { private static final JarEntryName EMPTY_JAR_ENTRY_NAME = new JarEntryName(""); - private static final String FILE_COLON_DOUBLE_SLASH = "file://"; - private static final String READ_ACTION = "read"; private static ThreadLocal useFastExceptions = new ThreadLocal(); @@ -82,7 +81,7 @@ class JarURLConnection extends java.net.JarURLConnection { // What we pass to super is ultimately ignored super(EMPTY_JAR_URL); this.url = url; - String spec = getNormalizedFile(url) + String spec = getNormalizedFileUrl(url) .substring(jarFile.getUrl().getFile().length()); int separator; while ((separator = spec.indexOf(SEPARATOR)) > 0) { @@ -95,11 +94,10 @@ class JarURLConnection extends java.net.JarURLConnection { READ_ACTION); } - private String getNormalizedFile(URL url) { - if (!url.getFile().startsWith(FILE_COLON_DOUBLE_SLASH)) { - return url.getFile(); - } - return "file:" + url.getFile().substring(FILE_COLON_DOUBLE_SLASH.length()); + private String getNormalizedFileUrl(URL url) { + String fileName = url.getFile(); + return new File(URI.create(fileName).getSchemeSpecificPart()).getAbsoluteFile() + .toURI().toString() + (fileName.endsWith("/") ? "/" : ""); } private JarFile getNestedJarFile(JarFile jarFile, String name) throws IOException { diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarURLConnectionTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarURLConnectionTests.java new file mode 100644 index 0000000000..cd7e8c4a29 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarURLConnectionTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2012-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.loader.jar; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.net.URL; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import org.springframework.boot.loader.TestJarCreator; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JarURLConnection}. + * + * @author Andy Wilkinson + */ +public class JarURLConnectionTests { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target")); + + private File rootJarFile; + + private JarFile jarFile; + + @Before + public void setup() throws Exception { + this.rootJarFile = this.temporaryFolder.newFile(); + TestJarCreator.createTestJar(this.rootJarFile); + this.jarFile = new JarFile(this.rootJarFile); + } + + @Test + public void connectionToRootUsingAbsoluteUrl() throws Exception { + URL absoluteUrl = new URL( + "jar:file:" + this.rootJarFile.getAbsoluteFile() + "!/"); + assertThat(new JarURLConnection(absoluteUrl, this.jarFile).getContent()) + .isSameAs(this.jarFile); + } + + @Test + public void connectionToRootUsingRelativeUrl() throws Exception { + URL relativeUrl = new URL("jar:file:" + this.rootJarFile + "!/"); + assertThat(new JarURLConnection(relativeUrl, this.jarFile).getContent()) + .isSameAs(this.jarFile); + } + + @Test + public void connectionToEntryUsingAbsoluteUrl() throws Exception { + URL absoluteUrl = new URL( + "jar:file:" + this.rootJarFile.getAbsoluteFile() + "!/1.dat"); + assertThat(new JarURLConnection(absoluteUrl, this.jarFile).getInputStream()) + .hasSameContentAs(new ByteArrayInputStream(new byte[] { 1 })); + } + + @Test + public void connectionToEntryUsingRelativeUrl() throws Exception { + URL relativeUrl = new URL("jar:file:" + this.rootJarFile + "!/1.dat"); + assertThat(new JarURLConnection(relativeUrl, this.jarFile).getInputStream()) + .hasSameContentAs(new ByteArrayInputStream(new byte[] { 1 })); + } + + @Test + public void connectionToEntryUsingAbsoluteUrlWithFileColonSlashSlashPrefix() + throws Exception { + URL absoluteUrl = new URL( + "jar:file:/" + this.rootJarFile.getAbsoluteFile() + "!/1.dat"); + assertThat(new JarURLConnection(absoluteUrl, this.jarFile).getInputStream()) + .hasSameContentAs(new ByteArrayInputStream(new byte[] { 1 })); + } + + @Test + public void connectionToEntryUsingAbsoluteUrlForNestedEntry() throws Exception { + URL absoluteUrl = new URL( + "jar:file:" + this.rootJarFile.getAbsoluteFile() + "!/nested.jar!/3.dat"); + assertThat(new JarURLConnection(absoluteUrl, this.jarFile).getInputStream()) + .hasSameContentAs(new ByteArrayInputStream(new byte[] { 3 })); + } + + @Test + public void connectionToEntryUsingRelativeUrlForNestedEntry() throws Exception { + URL relativeUrl = new URL("jar:file:" + this.rootJarFile + "!/nested.jar!/3.dat"); + assertThat(new JarURLConnection(relativeUrl, this.jarFile).getInputStream()) + .hasSameContentAs(new ByteArrayInputStream(new byte[] { 3 })); + } + +}