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 4bb64855725..5d41434b51a 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 deaf44cc43b..ae2c7f76090 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,10 +18,12 @@ 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; import java.net.URLConnection; +import java.net.URLDecoder; import java.security.CodeSource; import java.util.ArrayList; import java.util.Arrays; @@ -96,56 +98,71 @@ public abstract class AbstractEmbeddedServletContainerFactory List staticResourceUrls = new ArrayList(); if (classLoader instanceof URLClassLoader) { for (URL url : ((URLClassLoader) classLoader).getURLs()) { - try { - if ("file".equals(url.getProtocol())) { - File file = new File(url.getFile()); - if (file.isDirectory() - && new File(file, "META-INF/resources").isDirectory()) { - staticResourceUrls.add(url); - } - else if (isResourcesJar(file)) { - staticResourceUrls.add(url); - } - } - else { - URLConnection connection = url.openConnection(); - if (connection instanceof JarURLConnection) { - if (isResourcesJar((JarURLConnection) connection)) { - staticResourceUrls.add(url); - } - } - } - } - catch (IOException ex) { - throw new IllegalStateException(ex); + if (isStaticResourceJar(url)) { + staticResourceUrls.add(url); } } } return staticResourceUrls; } + private boolean isStaticResourceJar(URL url) { + try { + if ("file".equals(url.getProtocol())) { + 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 + && isResourcesJar((JarURLConnection) connection)) { + return true; + } + } + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + 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) { + this.logger.warn("Unable to open jar from connection '" + connection + + "' to determine if it contains static resources", ex); return false; } } private boolean isResourcesJar(File file) { try { - return isResourcesJar(new JarFile(file)); + return file.getName().endsWith(".jar") && isResourcesJar(new JarFile(file)); } catch (IOException ex) { + this.logger.warn("Unable to open jar '" + file + + "' to determine if it contains static resources", ex); return false; } } private boolean isResourcesJar(JarFile jar) throws IOException { try { - return jar.getName().endsWith(".jar") - && (jar.getJarEntry("META-INF/resources") != null); + return jar.getJarEntry("META-INF/resources") != null; } finally { jar.close(); 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 6ec3274a2a8..dac1fc034cb 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 716ff9f03cf..e9150258f61 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 942bc01727d..a290a3216c0 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 72f04946ebf..59a93177335 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.