From 13f7db2d8c6d8c92097368c1625399faf8317d0b Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 22 Jan 2020 18:54:15 -0800 Subject: [PATCH] Write classpath index file from Gradle plugin Closes gh-19847 --- .../boot/gradle/tasks/bundling/BootJar.java | 31 +++++++++++++++++-- .../gradle/tasks/bundling/BootJarTests.java | 28 +++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index 93a08521f9..e7e2ac705b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -20,7 +20,9 @@ import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.concurrent.Callable; import org.gradle.api.Action; @@ -59,7 +61,9 @@ public class BootJar extends Jar implements BootArchive { private Layers layers; - private static final String BOOT_INF_LAYERS = "BOOT-INF/layers/"; + private static final String BOOT_INF_LAYERS = "BOOT-INF/layers"; + + private List dependencies = new ArrayList<>(); /** * Creates a new {@code BootJar} task. @@ -68,7 +72,10 @@ public class BootJar extends Jar implements BootArchive { this.bootInf = getProject().copySpec().into("BOOT-INF"); getMainSpec().with(this.bootInf); this.bootInf.into("classes", classpathFiles(File::isDirectory)); - this.bootInf.into("lib", classpathFiles(File::isFile)); + this.bootInf.into("lib", classpathFiles(File::isFile)) + .eachFile((details) -> BootJar.this.dependencies.add(details.getPath())); + this.bootInf.into("", + (spec) -> spec.from((Callable) () -> createClasspathIndex(BootJar.this.dependencies))); this.bootInf.filesMatching("module-info.class", (details) -> details.setRelativePath(details.getRelativeSourcePath())); getRootSpec().eachFile((details) -> { @@ -93,9 +100,29 @@ public class BootJar extends Jar implements BootArchive { attributes.remove("Spring-Boot-Lib"); attributes.putIfAbsent("Spring-Boot-Layers-Index", "BOOT-INF/layers.idx"); } + attributes.putIfAbsent("Spring-Boot-Classpath-Index", "BOOT-INF/classpath.idx"); super.copy(); } + private File createClasspathIndex(List dependencies) { + try { + StringWriter content = new StringWriter(); + BufferedWriter writer = new BufferedWriter(content); + for (String dependency : dependencies) { + writer.write(dependency); + writer.write("\n"); + } + writer.flush(); + File source = getProject().getResources().getText().fromString(content.toString()).asFile(); + File indexFile = new File(source.getParentFile(), "classpath.idx"); + source.renameTo(indexFile); + return indexFile; + } + catch (IOException ex) { + throw new RuntimeException("Failed to create classpath.idx", ex); + } + } + @Override protected CopyAction createCopyAction() { return this.support.createCopyAction(this); 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 e44a74aa59..a12648730e 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 @@ -96,9 +96,37 @@ class BootJarTests extends AbstractBootArchiveTests { assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Lib")).isEqualTo(null); assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Layers-Index")) .isEqualTo("BOOT-INF/layers.idx"); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classpath-Index")) + .isEqualTo("BOOT-INF/classpath.idx"); try (InputStream input = jarFile.getInputStream(jarFile.getEntry("BOOT-INF/layers.idx"))) { assertThat(input).hasContent("dependencies\nsnapshot-dependencies\nresources\napplication\n"); } + try (InputStream input = jarFile.getInputStream(jarFile.getEntry("BOOT-INF/classpath.idx"))) { + assertThat(input).hasContent( + "BOOT-INF/layers/dependencies/lib/first-library.jar\nBOOT-INF/layers/dependencies/lib/second-library.jar\nBOOT-INF/layers/snapshot-dependencies/lib/third-library-SNAPSHOT.jar\n"); + } + } + } + + @Test + void classpathIndex() throws IOException { + BootJar bootJar = getTask(); + bootJar.setMainClassName("com.example.Main"); + File classesJavaMain = new File(this.temp, "classes/java/main"); + File applicationClass = new File(classesJavaMain, "com/example/Application.class"); + applicationClass.getParentFile().mkdirs(); + applicationClass.createNewFile(); + bootJar.classpath(classesJavaMain, jarFile("first-library.jar"), jarFile("second-library.jar"), + jarFile("third-library-SNAPSHOT.jar")); + bootJar.requiresUnpack("second-library.jar"); + executeTask(); + try (JarFile jarFile = new JarFile(bootJar.getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classpath-Index")) + .isEqualTo("BOOT-INF/classpath.idx"); + try (InputStream input = jarFile.getInputStream(jarFile.getEntry("BOOT-INF/classpath.idx"))) { + assertThat(input).hasContent( + "BOOT-INF/lib/first-library.jar\nBOOT-INF/lib/second-library.jar\nBOOT-INF/lib/third-library-SNAPSHOT.jar\n"); + } } }