Merge branch '1.3.x'

This commit is contained in:
Andy Wilkinson 2016-04-07 17:51:17 +01:00
commit 140e86bb97
2 changed files with 92 additions and 3 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,9 @@
package org.springframework.boot.devtools.restart; package org.springframework.boot.devtools.restart;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.ArrayList;
@ -23,13 +26,18 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.springframework.boot.devtools.settings.DevToolsSettings; import org.springframework.boot.devtools.settings.DevToolsSettings;
import org.springframework.util.StringUtils;
/** /**
* A filtered collections of URLs which can be change after the application has started. * A filtered collection of URLs which can change after the application has started.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
final class ChangeableUrls implements Iterable<URL> { final class ChangeableUrls implements Iterable<URL> {
@ -74,7 +82,59 @@ final class ChangeableUrls implements Iterable<URL> {
} }
public static ChangeableUrls fromUrlClassLoader(URLClassLoader classLoader) { public static ChangeableUrls fromUrlClassLoader(URLClassLoader classLoader) {
return fromUrls(classLoader.getURLs()); List<URL> urls = new ArrayList<URL>();
for (URL url : classLoader.getURLs()) {
urls.add(url);
urls.addAll(getUrlsFromClassPathOfJarManifestIfPossible(url));
}
return fromUrls(urls);
}
private static List<URL> getUrlsFromClassPathOfJarManifestIfPossible(URL url) {
JarFile jarFile = getJarFileIfPossible(url);
if (jarFile != null) {
try {
return getUrlsFromClassPathAttribute(jarFile.getManifest());
}
catch (IOException ex) {
throw new IllegalStateException(
"Failed to read Class-Path attribute from manifest of jar "
+ url);
}
}
return Collections.<URL>emptyList();
}
private static JarFile getJarFileIfPossible(URL url) {
try {
File file = new File(url.toURI());
if (file.isFile()) {
return new JarFile(file);
}
}
catch (Exception ex) {
// Assume it's not a jar and continue
}
return null;
}
private static List<URL> getUrlsFromClassPathAttribute(Manifest manifest) {
List<URL> urls = new ArrayList<URL>();
String classPathAttribute = manifest.getMainAttributes()
.getValue(Attributes.Name.CLASS_PATH);
if (StringUtils.hasText(classPathAttribute)) {
for (String entry : StringUtils.delimitedListToStringArray(classPathAttribute,
" ")) {
try {
urls.add(new URL(entry));
}
catch (MalformedURLException ex) {
throw new IllegalStateException(
"Class-Path attribute contains malformed URL", ex);
}
}
}
return urls;
} }
public static ChangeableUrls fromUrls(Collection<URL> urls) { public static ChangeableUrls fromUrls(Collection<URL> urls) {

View File

@ -17,19 +17,27 @@
package org.springframework.boot.devtools.restart; package org.springframework.boot.devtools.restart;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link ChangeableUrls}. * Tests for {@link ChangeableUrls}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
public class ChangeableUrlsTests { public class ChangeableUrlsTests {
@ -63,6 +71,16 @@ public class ChangeableUrlsTests {
assertThat(urls.size()).isEqualTo(0); assertThat(urls.size()).isEqualTo(0);
} }
@Test
public void urlsFromJarClassPathAreConsidered() throws Exception {
URL projectCore = makeUrl("project-core");
URL projectWeb = makeUrl("project-web");
ChangeableUrls urls = ChangeableUrls.fromUrlClassLoader(new URLClassLoader(
new URL[] { makeJarFileWithUrlsInManifestClassPath(projectCore,
projectWeb) }));
assertThat(urls.toList()).containsExactly(projectCore, projectWeb);
}
private URL makeUrl(String name) throws IOException { private URL makeUrl(String name) throws IOException {
File file = this.temporaryFolder.newFolder(); File file = this.temporaryFolder.newFolder();
file = new File(file, name); file = new File(file, name);
@ -72,4 +90,15 @@ public class ChangeableUrlsTests {
return file.toURI().toURL(); return file.toURI().toURL();
} }
private URL makeJarFileWithUrlsInManifestClassPath(URL... urls) throws Exception {
File classpathJar = this.temporaryFolder.newFile("classpath.jar");
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(),
"1.0");
manifest.getMainAttributes().putValue(Attributes.Name.CLASS_PATH.toString(),
StringUtils.arrayToDelimitedString(urls, " "));
new JarOutputStream(new FileOutputStream(classpathJar), manifest).close();
return classpathJar.toURI().toURL();
}
} }