From df58b9baa297df1445d6a14e5aa774163221d354 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 6 Apr 2020 16:09:57 -0700 Subject: [PATCH] Use YAML compatible classpath.idx format Update the `classpath.idx` format to align with `layers.idx` and allow third-parties can parse it as YAML Closes gh-20861 --- .../gradle/tasks/bundling/BootZipCopyAction.java | 7 +++++-- .../boot/gradle/tasks/bundling/BootJarTests.java | 8 ++++---- .../boot/loader/tools/Packager.java | 3 ++- .../boot/loader/tools/AbstractPackagerTests.java | 15 +++++++++------ .../boot/loader/ClassPathIndexFile.java | 9 ++++++++- .../AbstractExecutableArchiveLauncherTests.java | 6 +++--- .../boot/loader/classpath-index-file.idx | 10 +++++----- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java index 10eb611628..99bd28bfbb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java @@ -26,9 +26,11 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.zip.CRC32; import org.apache.commons.compress.archivers.zip.UnixStat; @@ -345,8 +347,9 @@ class BootZipCopyAction implements CopyAction { Attributes manifestAttributes = BootZipCopyAction.this.manifest.getAttributes(); String classPathIndex = (String) manifestAttributes.get("Spring-Boot-Classpath-Index"); if (classPathIndex != null) { - writeEntry(classPathIndex, - ZipEntryWriter.fromLines(BootZipCopyAction.this.encoding, this.writtenLibraries), true); + List lines = this.writtenLibraries.stream().map((line) -> "- \"" + line + "\"") + .collect(Collectors.toList()); + writeEntry(classPathIndex, ZipEntryWriter.fromLines(BootZipCopyAction.this.encoding, lines), true); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java index 3dfb492938..60a8b841c7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java @@ -185,8 +185,8 @@ class BootJarTests extends AbstractBootArchiveTests { @Test void whenJarIsLayeredClasspathIndexPointsToLayeredLibs() throws IOException { try (JarFile jarFile = new JarFile(createLayeredJar())) { - assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("first-library.jar", - "second-library.jar", "third-library-SNAPSHOT.jar"); + assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("- \"first-library.jar\"", + "- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\""); } } @@ -208,8 +208,8 @@ class BootJarTests extends AbstractBootArchiveTests { try (JarFile jarFile = new JarFile(createPopulatedJar())) { assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classpath-Index")) .isEqualTo("BOOT-INF/classpath.idx"); - assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("first-library.jar", - "second-library.jar", "third-library-SNAPSHOT.jar"); + assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("- \"first-library.jar\"", + "- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\""); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java index 13cca9eea5..ba0e048ddb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java @@ -505,7 +505,8 @@ public abstract class Packager { } private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException { - List names = this.libraries.keySet().stream().map(this::getJarName).collect(Collectors.toList()); + List names = this.libraries.keySet().stream().map(this::getJarName) + .map((name) -> "- \"" + name + "\"").collect(Collectors.toList()); writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java index 7d3b575ae6..8051f97923 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java @@ -212,7 +212,7 @@ abstract class AbstractPackagerTests

{ } @Test - void index() throws Exception { + void classPathIndex() throws Exception { TestJarFile libJar1 = new TestJarFile(this.tempDir); libJar1.addClass("a/b/C.class", ClassWithoutMainMethod.class, JAN_1_1985); File libJarFile1 = libJar1.getFile(); @@ -233,8 +233,9 @@ abstract class AbstractPackagerTests

{ assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue(); String index = getPackagedEntryContent("BOOT-INF/classpath.idx"); String[] libraries = index.split("\\r?\\n"); - assertThat(Arrays.asList(libraries)).contains(libJarFile1.getName(), libJarFile2.getName(), - libJarFile3.getName()); + List expected = Stream.of(libJarFile1, libJarFile2, libJarFile3) + .map((jar) -> "- \"" + jar.getName() + "\"").collect(Collectors.toList()); + assertThat(Arrays.asList(libraries)).containsExactlyElementsOf(expected); } @Test @@ -263,8 +264,9 @@ abstract class AbstractPackagerTests

{ }); assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue(); String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx"); - assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly(libJarFile1.getName(), - libJarFile2.getName(), libJarFile3.getName()); + List expectedClasspathIndex = Stream.of(libJarFile1, libJarFile2, libJarFile3) + .map((file) -> "- \"" + file.getName() + "\"").collect(Collectors.toList()); + assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactlyElementsOf(expectedClasspathIndex); assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue(); String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx"); List expectedLayers = new ArrayList<>(); @@ -293,7 +295,8 @@ abstract class AbstractPackagerTests

{ execute(packager, Libraries.NONE); assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue(); String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx"); - assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly("spring-boot-jarmode-layertools.jar"); + assertThat(Arrays.asList(classpathIndex.split("\\n"))) + .containsExactly("- \"spring-boot-jarmode-layertools.jar\""); assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue(); String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx"); List expectedLayers = new ArrayList<>(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ClassPathIndexFile.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ClassPathIndexFile.java index da4db203d0..dab893b5d5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ClassPathIndexFile.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ClassPathIndexFile.java @@ -31,6 +31,8 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.springframework.util.Assert; + /** * A class path index file that provides ordering information for JARs. * @@ -45,7 +47,12 @@ final class ClassPathIndexFile { private ClassPathIndexFile(File root, List lines) { this.root = root; - this.lines = lines; + this.lines = lines.stream().map(this::extractName).collect(Collectors.toList()); + } + + private String extractName(String line) { + Assert.state(line.startsWith("- \"") && line.endsWith("\""), "Malformed classpath index line [" + line + "]"); + return line.substring(3, line.length() - 1); } int size() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java index 20069b8f91..84a05eb25c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java @@ -68,9 +68,9 @@ public abstract class AbstractExecutableArchiveLauncherTests { JarEntry indexEntry = new JarEntry(entryPrefix + "/classpath.idx"); jarOutputStream.putNextEntry(indexEntry); Writer writer = new OutputStreamWriter(jarOutputStream, StandardCharsets.UTF_8); - writer.write("BOOT-INF/lib/foo.jar\n"); - writer.write("BOOT-INF/lib/bar.jar\n"); - writer.write("BOOT-INF/lib/baz.jar\n"); + writer.write("- \"BOOT-INF/lib/foo.jar\"\n"); + writer.write("- \"BOOT-INF/lib/bar.jar\"\n"); + writer.write("- \"BOOT-INF/lib/baz.jar\"\n"); writer.flush(); } addNestedJars(entryPrefix, "/lib/foo.jar", jarOutputStream); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/resources/org/springframework/boot/loader/classpath-index-file.idx b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/resources/org/springframework/boot/loader/classpath-index-file.idx index 1483049b71..a08268d5ac 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/resources/org/springframework/boot/loader/classpath-index-file.idx +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/resources/org/springframework/boot/loader/classpath-index-file.idx @@ -1,5 +1,5 @@ -a.jar -b.jar -c.jar -d.jar -e.jar +- "a.jar" +- "b.jar" +- "c.jar" +- "d.jar" +- "e.jar"