diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index 222a10612f..2838516a5d 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -8235,6 +8235,10 @@ For Gradle, refer to the {spring-boot-gradle-plugin-docs}#packaging-layered-jars ==== Writing the Dockerfile When you create a jar containing the layers index file, the `spring-boot-jarmode-layertools` jar will be added as a dependency to your jar. With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers. + +CAUTION: The `layertools` mode can not be used with a <> that includes a launch script. +Disable launch script configuration when building a jar file that is intended to be used with `layertools`. + Here’s how you can launch your jar with a `layertools` jar mode: [source] diff --git a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/main/java/org/springframework/boot/jarmode/layertools/ExtractCommand.java b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/main/java/org/springframework/boot/jarmode/layertools/ExtractCommand.java index 9a3e8b016e..cf159cc29e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/main/java/org/springframework/boot/jarmode/layertools/ExtractCommand.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/main/java/org/springframework/boot/jarmode/layertools/ExtractCommand.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import org.springframework.util.Assert; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; @@ -66,6 +67,8 @@ class ExtractCommand extends Command { } try (ZipInputStream zip = new ZipInputStream(new FileInputStream(this.context.getJarFile()))) { ZipEntry entry = zip.getNextEntry(); + Assert.state(entry != null, "File '" + this.context.getJarFile().toString() + + "' is not compatible with layertools; ensure jar file is valid and launch script is not enabled"); while (entry != null) { if (!entry.isDirectory()) { String layer = this.layers.getLayer(entry); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java index 13646be6be..898a668481 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.jarmode.layertools; import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -32,6 +33,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.BDDMockito.given; /** @@ -95,6 +97,19 @@ class ExtractCommandTests { assertThat(new File(this.extract, "c/c/c.jar")).exists(); } + @Test + void runWithJarFileContainingNoEntriesFails() throws IOException { + File file = new File(this.temp, "empty.jar"); + FileWriter writer = new FileWriter(file); + writer.write("text"); + writer.flush(); + given(this.context.getJarFile()).willReturn(file); + given(this.context.getWorkingDir()).willReturn(this.extract); + assertThatIllegalStateException() + .isThrownBy(() -> this.command.run(Collections.emptyMap(), Collections.emptyList())) + .withMessageContaining("not compatible with layertools"); + } + private File createJarFile(String name) throws IOException { File file = new File(this.temp, name); try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file))) {