Add classpath index support for exploded war archives

Update the Maven and Gradle packaging for war files so that a
`classpath.idx` file is written into the archive that provides the
original order of the classpath, as was previously done for jar files.
The `WarLauncher` class will use this file when running as an exploded
archive to ensure that the classpath order is the same as when running
from the far war.

Fixes gh-19875
This commit is contained in:
Scott Frederick
2021-12-09 16:25:30 -06:00
parent 8b5600fca8
commit 8f57f0babb
14 changed files with 168 additions and 76 deletions

View File

@@ -75,17 +75,20 @@ abstract class AbstractArchiveIntegrationTests {
return Collections.emptyMap();
}
Map<String, List<String>> index = new LinkedHashMap<>();
String layerPrefix = "- ";
String entryPrefix = " - ";
ZipEntry indexEntry = jarFile.getEntry(getLayersIndexLocation());
try (BufferedReader reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(indexEntry)))) {
String line = reader.readLine();
String layer = null;
while (line != null) {
if (line.startsWith("- ")) {
layer = line.substring(3, line.length() - 2);
if (line.startsWith(layerPrefix)) {
layer = line.substring(layerPrefix.length() + 1, line.length() - 2);
index.put(layer, new ArrayList<>());
}
else if (line.startsWith(" - ")) {
index.computeIfAbsent(layer, (key) -> new ArrayList<>()).add(line.substring(5, line.length() - 1));
else if (line.startsWith(entryPrefix)) {
index.computeIfAbsent(layer, (key) -> new ArrayList<>())
.add(line.substring(entryPrefix.length() + 1, line.length() - 1));
}
line = reader.readLine();
}
@@ -97,6 +100,22 @@ abstract class AbstractArchiveIntegrationTests {
return null;
}
protected List<String> readClasspathIndex(JarFile jarFile, String location) throws IOException {
List<String> index = new ArrayList<>();
String entryPrefix = "- ";
ZipEntry indexEntry = jarFile.getEntry(location);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(indexEntry)))) {
String line = reader.readLine();
while (line != null) {
if (line.startsWith(entryPrefix)) {
index.add(line.substring(entryPrefix.length() + 1, line.length() - 1));
}
line = reader.readLine();
}
}
return index;
}
static final class JarAssert extends AbstractAssert<JarAssert, File> {
private JarAssert(File actual) {

View File

@@ -372,6 +372,10 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
assertThat(jar(repackaged)).manifest(
(manifest) -> manifest.hasAttribute("Spring-Boot-Classpath-Index", "BOOT-INF/classpath.idx"));
assertThat(jar(repackaged)).hasEntryWithName("BOOT-INF/classpath.idx");
try (JarFile jarFile = new JarFile(repackaged)) {
List<String> index = readClasspathIndex(jarFile, "BOOT-INF/classpath.idx");
assertThat(index).allMatch((entry) -> entry.startsWith("BOOT-INF/lib/"));
}
});
}

View File

@@ -211,12 +211,17 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
}
@TestTemplate
void repackagedWarDoesNotContainClasspathIndex(MavenBuild mavenBuild) {
void repackagedWarContainsClasspathIndex(MavenBuild mavenBuild) {
mavenBuild.project("war").execute((project) -> {
File repackaged = new File(project, "target/war-0.0.1.BUILD-SNAPSHOT.war");
assertThat(jar(repackaged))
.manifest((manifest) -> manifest.doesNotHaveAttribute("Spring-Boot-Classpath-Index"));
assertThat(jar(repackaged)).doesNotHaveEntryWithName("BOOT-INF/classpath.idx");
assertThat(jar(repackaged)).manifest(
(manifest) -> manifest.hasAttribute("Spring-Boot-Classpath-Index", "WEB-INF/classpath.idx"));
assertThat(jar(repackaged)).hasEntryWithName("WEB-INF/classpath.idx");
try (JarFile jarFile = new JarFile(repackaged)) {
List<String> index = readClasspathIndex(jarFile, "WEB-INF/classpath.idx");
assertThat(index).allMatch(
(entry) -> entry.startsWith("WEB-INF/lib/") || entry.startsWith("WEB-INF/lib-provided/"));
}
});
}