diff --git a/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/IdeApplicationLauncher.java b/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/IdeApplicationLauncher.java index 4bb6485572..5d41434b51 100644 --- a/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/IdeApplicationLauncher.java +++ b/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/IdeApplicationLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -40,7 +40,7 @@ import org.springframework.util.StringUtils; */ class IdeApplicationLauncher extends AbstractApplicationLauncher { - private final File exploded = new File("target/ide"); + private final File exploded = new File("target/ide application"); IdeApplicationLauncher(ApplicationBuilder applicationBuilder) { super(applicationBuilder); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java index 397d7710fd..ae2c7f7609 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java @@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLClassLoader; @@ -97,7 +98,7 @@ public abstract class AbstractEmbeddedServletContainerFactory List staticResourceUrls = new ArrayList(); if (classLoader instanceof URLClassLoader) { for (URL url : ((URLClassLoader) classLoader).getURLs()) { - if (isStaticResource(url)) { + if (isStaticResourceJar(url)) { staticResourceUrls.add(url); } } @@ -105,24 +106,19 @@ public abstract class AbstractEmbeddedServletContainerFactory return staticResourceUrls; } - protected boolean isStaticResource(URL url) { + private boolean isStaticResourceJar(URL url) { try { if ("file".equals(url.getProtocol())) { - File file = new File(URLDecoder.decode(url.getFile(), "UTF-8")); - if (file.isDirectory() - && new File(file, "META-INF/resources").isDirectory()) { - return true; - } - else if (isResourcesJar(file)) { - return true; - } + File file = new File(getDecodedFile(url), "UTF-8"); + return (file.isDirectory() + && new File(file, "META-INF/resources").isDirectory()) + || isResourcesJar(file); } else { URLConnection connection = url.openConnection(); - if (connection instanceof JarURLConnection) { - if (isResourcesJar((JarURLConnection) connection)) { - return true; - } + if (connection instanceof JarURLConnection + && isResourcesJar((JarURLConnection) connection)) { + return true; } } } @@ -132,12 +128,23 @@ public abstract class AbstractEmbeddedServletContainerFactory return false; } + protected final String getDecodedFile(URL url) { + try { + return URLDecoder.decode(url.getFile(), "UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException( + "Failed to decode '" + url.getFile() + "' using UTF-8"); + } + } + private boolean isResourcesJar(JarURLConnection connection) { try { return isResourcesJar(connection.getJarFile()); } catch (IOException ex) { - logger.warn("Unable to open jar to determine if it contains static resources", ex); + this.logger.warn("Unable to open jar from connection '" + connection + + "' to determine if it contains static resources", ex); return false; } } @@ -147,7 +154,8 @@ public abstract class AbstractEmbeddedServletContainerFactory return file.getName().endsWith(".jar") && isResourcesJar(new JarFile(file)); } catch (IOException ex) { - logger.warn("Unable to open jar to determine if it contains static resources", ex); + this.logger.warn("Unable to open jar '" + file + + "' to determine if it contains static resources", ex); return false; } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index 6ec3274a2a..dac1fc034c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -433,7 +433,7 @@ public class JettyEmbeddedServletContainerFactory private Resource createResource(URL url) throws IOException { if ("file".equals(url.getProtocol())) { - File file = new File(url.getFile()); + File file = new File(getDecodedFile(url)); if (file.isFile()) { return Resource.newResource("jar:" + url + "!/META-INF/resources"); } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatResources.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatResources.java index 716ff9f03c..e9150258f6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatResources.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatResources.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,9 +16,11 @@ package org.springframework.boot.context.embedded.tomcat; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLDecoder; import java.util.List; import javax.naming.directory.DirContext; @@ -47,7 +49,7 @@ abstract class TomcatResources { void addResourceJars(List resourceJarUrls) { for (URL url : resourceJarUrls) { - String file = url.getFile(); + String file = getDecodedFile(url); if (file.endsWith(".jar") || file.endsWith(".jar!/")) { String jar = url.toString(); if (!jar.startsWith("jar:")) { @@ -62,6 +64,16 @@ abstract class TomcatResources { } } + private String getDecodedFile(URL url) { + try { + return URLDecoder.decode(url.getFile(), "UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException( + "Failed to decode '" + url.getFile() + "' using UTF-8"); + } + } + protected final Context getContext() { return this.context; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index 942bc01727..a290a3216c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -494,7 +494,7 @@ public class UndertowEmbeddedServletContainerFactory resourceManagers.add(rootResourceManager); for (URL url : metaInfResourceUrls) { if ("file".equals(url.getProtocol())) { - File file = new File(url.getFile()); + File file = new File(getDecodedFile(url)); if (file.isFile()) { try { resourceJarUrls.add(new URL("jar:" + url + "!/")); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index 5db6fc0356..59a9317733 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,7 +18,6 @@ package org.springframework.boot.context.embedded; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; @@ -50,8 +49,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; import java.util.zip.GZIPInputStream; import javax.net.ssl.SSLContext; @@ -1038,54 +1035,6 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { assertThat(documentRoot).isNull(); } - @Test - public void includeJarWithStaticResources() throws Exception { - AbstractEmbeddedServletContainerFactory factory = getFactory(); - File jarFile = this.temporaryFolder.newFile("test.jar"); - JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile)); - JarEntry jarEntry = new JarEntry("META-INF/resources"); - jarOutputStream.putNextEntry(jarEntry); - jarOutputStream.closeEntry(); - jarOutputStream.close(); - String path = "file:" + jarFile.getAbsolutePath(); - - boolean isStaticResource = factory.isStaticResource(new URL(path)); - - assertThat(isStaticResource).isTrue(); - } - - @Test - public void includeJarWithStaticResourcesWithUrlEncodedSpaces() throws Exception { - AbstractEmbeddedServletContainerFactory factory = getFactory(); - this.temporaryFolder.newFolder("test parent"); - File jarFile = this.temporaryFolder.newFile("test parent/test.jar"); - JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile)); - JarEntry jarEntry = new JarEntry("META-INF/resources"); - jarOutputStream.putNextEntry(jarEntry); - jarOutputStream.closeEntry(); - jarOutputStream.close(); - String path = "file:" + jarFile.getAbsolutePath().replaceAll(" ", "%20"); - - boolean isStaticResource = factory.isStaticResource(new URL(path)); - - assertThat(isStaticResource).isTrue(); - } - - @Test - public void excludeJarWithoutStaticResources() throws Exception { - AbstractEmbeddedServletContainerFactory factory = getFactory(); - File jarFile = this.temporaryFolder.newFile("test.jar"); - JarOutputStream jarOutputStream = new JarOutputStream( - new FileOutputStream(jarFile)); - jarOutputStream.closeEntry(); - jarOutputStream.close(); - String path = "file:" + jarFile.getAbsolutePath(); - - boolean isStaticResource = factory.isStaticResource(new URL(path)); - - assertThat(isStaticResource).isFalse(); - } - protected abstract void addConnector(int port, AbstractEmbeddedServletContainerFactory factory);