Skip to content

Commit

Permalink
Fix classpath index so entries match those expected by the launcher
Browse files Browse the repository at this point in the history
This reverts commit ad16426 and adds
some additional tests.

Fixes gh-24192
  • Loading branch information
wilkinsona committed Jan 8, 2021
1 parent 826d79b commit 5ad4d62
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 36 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -243,7 +243,7 @@ private void processFile(FileCopyDetails details) throws IOException {
details.copyTo(this.out);
this.out.closeArchiveEntry();
if (BootZipCopyAction.this.librarySpec.isSatisfiedBy(details)) {
this.writtenLibraries.add(name.substring(name.lastIndexOf('/') + 1));
this.writtenLibraries.add(name);
}
if (BootZipCopyAction.this.layerResolver != null) {
Layer layer = BootZipCopyAction.this.layerResolver.getLayer(details);
Expand Down
@@ -0,0 +1,41 @@
/*
* Copyright 2012-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.bootjar.classpath;

import java.net.URL;
import java.net.URLClassLoader;

/**
* Application used for testing classpath handling with BootJar.
*
* @author Andy Wilkinson
*/
public class BootJarClasspathApplication {

protected BootJarClasspathApplication() {

}

public static void main(String[] args) {
int i = 1;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (URL url : ((URLClassLoader) classLoader).getURLs()) {
System.out.println(i++ + ". " + url.getFile());
}
}

}
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.example.classpath;
package com.example.bootrun.classpath;

import java.io.File;
import java.lang.management.ManagementFactory;
Expand Down
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.example.jvmargs;
package com.example.bootrun.jvmargs;

import java.lang.management.ManagementFactory;

Expand Down
Expand Up @@ -44,6 +44,7 @@
import org.junit.jupiter.api.TestTemplate;

import org.springframework.boot.loader.tools.JarModeLibrary;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -240,6 +241,28 @@ void multiModuleCustomLayers() throws IOException {
assertExtractedLayers(layerNames, indexedLayers);
}

@TestTemplate
void packagedApplicationClasspath() throws IOException {
copyClasspathApplication();
BuildResult result = this.gradleBuild.build("launch");
String output = result.getOutput();
assertThat(output).containsPattern("1\\. .*classes");
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
assertThat(output).doesNotContain("4. ");
}

@TestTemplate
void explodedApplicationClasspath() throws IOException {
copyClasspathApplication();
BuildResult result = this.gradleBuild.build("launch");
String output = result.getOutput();
assertThat(output).containsPattern("1\\. .*classes");
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
assertThat(output).doesNotContain("4. ");
}

private void assertExtractedLayers(List<String> layerNames, Map<String, List<String>> indexedLayers)
throws IOException {
Map<String, List<String>> extractedLayers = readExtractedLayers(this.gradleBuild.getProjectDir(), layerNames);
Expand Down Expand Up @@ -339,4 +362,14 @@ private Map<String, List<String>> readExtractedLayers(File root, List<String> la
return extractedLayers;
}

private void copyClasspathApplication() throws IOException {
copyApplication("classpath");
}

private void copyApplication(String name) throws IOException {
File output = new File(this.gradleBuild.getProjectDir(), "src/main/java/com/example/bootjar/" + name);
output.mkdirs();
FileSystemUtils.copyRecursively(new File("src/test/java/com/example/bootjar/" + name), output);
}

}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -200,9 +200,10 @@ void jarsInLibAreStored() throws IOException {
@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\"", "- \"first-project-library.jar\"",
"- \"second-project-library-SNAPSHOT.jar\"");
assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly(
"- \"BOOT-INF/lib/first-library.jar\"", "- \"BOOT-INF/lib/second-library.jar\"",
"- \"BOOT-INF/lib/third-library-SNAPSHOT.jar\"", "- \"BOOT-INF/lib/first-project-library.jar\"",
"- \"BOOT-INF/lib/second-project-library-SNAPSHOT.jar\"");
}
}

Expand All @@ -224,9 +225,10 @@ void classpathIndexPointsToBootInfLibs() throws IOException {
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\"", "- \"first-project-library.jar\"",
"- \"second-project-library-SNAPSHOT.jar\"");
assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly(
"- \"BOOT-INF/lib/first-library.jar\"", "- \"BOOT-INF/lib/second-library.jar\"",
"- \"BOOT-INF/lib/third-library-SNAPSHOT.jar\"", "- \"BOOT-INF/lib/first-project-library.jar\"",
"- \"BOOT-INF/lib/second-project-library-SNAPSHOT.jar\"");
}
}

Expand Down
Expand Up @@ -81,7 +81,8 @@ void applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException {
copyClasspathApplication();
BuildResult result = this.gradleBuild.build("echoMainClassName");
assertThat(result.task(":echoMainClassName").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("Main class name = com.example.classpath.BootRunClasspathApplication");
assertThat(result.getOutput())
.contains("Main class name = com.example.bootrun.classpath.BootRunClasspathApplication");
}

@TestTemplate
Expand Down Expand Up @@ -129,9 +130,9 @@ private void copyJvmArgsApplication() throws IOException {
}

private void copyApplication(String name) throws IOException {
File output = new File(this.gradleBuild.getProjectDir(), "src/main/java/com/example/" + name);
File output = new File(this.gradleBuild.getProjectDir(), "src/main/java/com/example/bootrun/" + name);
output.mkdirs();
FileSystemUtils.copyRecursively(new File("src/test/java/com/example/" + name), output);
FileSystemUtils.copyRecursively(new File("src/test/java/com/example/bootrun/" + name), output);
}

private String canonicalPathOf(String path) throws IOException {
Expand Down
@@ -0,0 +1,25 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}

repositories {
mavenCentral()
maven { url "file:repository" }
}

dependencies {
implementation("com.example:library:1.0-SNAPSHOT")
implementation("org.apache.commons:commons-lang3:3.9")
}

task explode(type: Sync) {
dependsOn(bootJar)
destinationDir = file("$buildDir/exploded")
from zipTree(files(bootJar).singleFile)
}

task launch(type: JavaExec) {
classpath = files(explode)
main = 'org.springframework.boot.loader.JarLauncher'
}
@@ -0,0 +1,19 @@

plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}

task launch(type: JavaExec) {
classpath = files(bootJar)
}

repositories {
mavenCentral()
maven { url "file:repository" }
}

dependencies {
implementation("com.example:library:1.0-SNAPSHOT")
implementation("org.apache.commons:commons-lang3:3.9")
}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -473,15 +473,11 @@ private void write(AbstractJarWriter writer) throws IOException {
}

private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException {
List<String> names = this.libraries.keySet().stream().map(this::getJarName)
.map((name) -> "- \"" + name + "\"").collect(Collectors.toList());
List<String> names = this.libraries.keySet().stream().map((path) -> "- \"" + path + "\"")
.collect(Collectors.toList());
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
}

private String getJarName(String path) {
return path.substring(path.lastIndexOf('/') + 1);
}

}

}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -234,7 +234,7 @@ void classPathIndex() throws Exception {
String index = getPackagedEntryContent("BOOT-INF/classpath.idx");
String[] libraries = index.split("\\r?\\n");
List<String> expected = Stream.of(libJarFile1, libJarFile2, libJarFile3)
.map((jar) -> "- \"" + jar.getName() + "\"").collect(Collectors.toList());
.map((jar) -> "- \"BOOT-INF/lib/" + jar.getName() + "\"").collect(Collectors.toList());
assertThat(Arrays.asList(libraries)).containsExactlyElementsOf(expected);
}

Expand Down Expand Up @@ -265,7 +265,7 @@ void layersIndex() throws Exception {
assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue();
String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx");
List<String> expectedClasspathIndex = Stream.of(libJarFile1, libJarFile2, libJarFile3)
.map((file) -> "- \"" + file.getName() + "\"").collect(Collectors.toList());
.map((file) -> "- \"BOOT-INF/lib/" + 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");
Expand Down Expand Up @@ -296,7 +296,7 @@ void layersEnabledAddJarModeJar() throws Exception {
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\"");
.containsExactly("- \"BOOT-INF/lib/spring-boot-jarmode-layertools.jar\"");
assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue();
String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx");
List<String> expectedLayers = new ArrayList<>();
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -78,11 +78,11 @@ void getUrlsReturnsUrls() throws Exception {
ClassPathIndexFile indexFile = copyAndLoadTestIndexFile();
List<URL> urls = indexFile.getUrls();
List<File> expected = new ArrayList<>();
expected.add(new File(this.temp, "a.jar"));
expected.add(new File(this.temp, "b.jar"));
expected.add(new File(this.temp, "c.jar"));
expected.add(new File(this.temp, "d.jar"));
expected.add(new File(this.temp, "e.jar"));
expected.add(new File(this.temp, "BOOT-INF/layers/one/lib/a.jar"));
expected.add(new File(this.temp, "BOOT-INF/layers/one/lib/b.jar"));
expected.add(new File(this.temp, "BOOT-INF/layers/one/lib/c.jar"));
expected.add(new File(this.temp, "BOOT-INF/layers/two/lib/d.jar"));
expected.add(new File(this.temp, "BOOT-INF/layers/two/lib/e.jar"));
assertThat(urls).containsExactly(expected.stream().map(this::toUrl).toArray(URL[]::new));
}

Expand Down
@@ -1,5 +1,5 @@
- "a.jar"
- "b.jar"
- "c.jar"
- "d.jar"
- "e.jar"
- "BOOT-INF/layers/one/lib/a.jar"
- "BOOT-INF/layers/one/lib/b.jar"
- "BOOT-INF/layers/one/lib/c.jar"
- "BOOT-INF/layers/two/lib/d.jar"
- "BOOT-INF/layers/two/lib/e.jar"

0 comments on commit 5ad4d62

Please sign in to comment.