From 47de05b52c0236a1a836e5f8ad6b3b3533d3fc03 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 4 Apr 2017 12:15:51 +0100 Subject: [PATCH] Update Devtools to ignore manifest Class-Path entries that do not exist Closes gh-8623 --- .../boot/devtools/restart/ChangeableUrls.java | 19 ++++++++++++---- .../devtools/restart/ChangeableUrlsTests.java | 22 +++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ChangeableUrls.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ChangeableUrls.java index 3b43a450ab3..c87a215b769 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ChangeableUrls.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ChangeableUrls.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -96,7 +96,7 @@ final class ChangeableUrls implements Iterable { return Collections.emptyList(); } try { - return getUrlsFromClassPathAttribute(url, jarFile.getManifest()); + return getUrlsFromManifestClassPathAttribute(jarFile); } catch (IOException ex) { throw new IllegalStateException( @@ -118,7 +118,9 @@ final class ChangeableUrls implements Iterable { return null; } - private static List getUrlsFromClassPathAttribute(URL base, Manifest manifest) { + private static List getUrlsFromManifestClassPathAttribute(JarFile jarFile) + throws IOException { + Manifest manifest = jarFile.getManifest(); if (manifest == null) { return Collections.emptyList(); } @@ -129,9 +131,18 @@ final class ChangeableUrls implements Iterable { } String[] entries = StringUtils.delimitedListToStringArray(classPath, " "); List urls = new ArrayList(entries.length); + File parent = new File(jarFile.getName()).getParentFile(); for (String entry : entries) { try { - urls.add(new URL(base, entry)); + File referenced = new File(parent, entry); + if (referenced.exists()) { + urls.add(referenced.toURI().toURL()); + } + else { + System.err.println("Ignoring Class-Path entry " + entry + " found in" + + jarFile.getName() + " as " + referenced + + " does not exist"); + } } catch (MalformedURLException ex) { throw new IllegalStateException( diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ChangeableUrlsTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ChangeableUrlsTests.java index eb2e846396d..9e5de5abe54 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ChangeableUrlsTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ChangeableUrlsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -74,15 +74,19 @@ public class ChangeableUrlsTests { @Test public void urlsFromJarClassPathAreConsidered() throws Exception { - URL projectCore = makeUrl("project-core"); - URL projectWeb = makeUrl("project-web"); File relative = this.temporaryFolder.newFolder(); + File jarWithClassPath = makeJarFileWithUrlsInManifestClassPath( + "project-core/target/classes/", "project-web/target/classes/", + "does-not-exist/target/classes", relative.getName() + "/"); + new File(jarWithClassPath.getParentFile(), "project-core/target/classes") + .mkdirs(); + new File(jarWithClassPath.getParentFile(), "project-web/target/classes").mkdirs(); ChangeableUrls urls = ChangeableUrls .fromUrlClassLoader(new URLClassLoader(new URL[] { - makeJarFileWithUrlsInManifestClassPath(projectCore, projectWeb, - relative.getName() + "/"), - makeJarFileWithNoManifest() })); - assertThat(urls.toList()).containsExactly(projectCore, projectWeb, + jarWithClassPath.toURI().toURL(), makeJarFileWithNoManifest() })); + assertThat(urls.toList()).containsExactly( + new URL(jarWithClassPath.toURI().toURL(), "project-core/target/classes/"), + new URL(jarWithClassPath.toURI().toURL(), "project-web/target/classes/"), relative.toURI().toURL()); } @@ -95,7 +99,7 @@ public class ChangeableUrlsTests { return file.toURI().toURL(); } - private URL makeJarFileWithUrlsInManifestClassPath(Object... urls) throws Exception { + private File makeJarFileWithUrlsInManifestClassPath(Object... urls) throws Exception { File classpathJar = this.temporaryFolder.newFile("classpath.jar"); Manifest manifest = new Manifest(); manifest.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), @@ -103,7 +107,7 @@ public class ChangeableUrlsTests { manifest.getMainAttributes().putValue(Attributes.Name.CLASS_PATH.toString(), StringUtils.arrayToDelimitedString(urls, " ")); new JarOutputStream(new FileOutputStream(classpathJar), manifest).close(); - return classpathJar.toURI().toURL(); + return classpathJar; } private URL makeJarFileWithNoManifest() throws Exception {