Commit df58b9ba authored by Phillip Webb's avatar Phillip Webb

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
parent 65672a11
......@@ -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<String> lines = this.writtenLibraries.stream().map((line) -> "- \"" + line + "\"")
.collect(Collectors.toList());
writeEntry(classPathIndex, ZipEntryWriter.fromLines(BootZipCopyAction.this.encoding, lines), true);
}
}
......
......@@ -185,8 +185,8 @@ class BootJarTests extends AbstractBootArchiveTests<TestBootJar> {
@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<TestBootJar> {
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\"");
}
}
......
......@@ -505,7 +505,8 @@ public abstract class Packager {
}
private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException {
List<String> names = this.libraries.keySet().stream().map(this::getJarName).collect(Collectors.toList());
List<String> names = this.libraries.keySet().stream().map(this::getJarName)
.map((name) -> "- \"" + name + "\"").collect(Collectors.toList());
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
}
......
......@@ -212,7 +212,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
}
@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<P extends Packager> {
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<String> 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<P extends Packager> {
});
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<String> 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<String> expectedLayers = new ArrayList<>();
......@@ -293,7 +295,8 @@ abstract class AbstractPackagerTests<P extends Packager> {
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<String> expectedLayers = new ArrayList<>();
......
......@@ -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<String> 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() {
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment