diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc index ba7842db8b..9443566b90 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc @@ -275,19 +275,6 @@ By default, the `bootJar` task builds an archive that contains the application's For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these directories further so that they can be written into distinct layers. Layered jars use the same layout as regular boot packaged jars, but include an additional meta-data file that describes each layer. -To use this feature, the layering feature must be enabled: - -[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] -.Groovy ----- -include::../gradle/packaging/boot-jar-layered.gradle[tags=layered] ----- - -[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"] -.Kotlin ----- -include::../gradle/packaging/boot-jar-layered.gradle.kts[tags=layered] ----- By default, the following layers are defined: @@ -300,7 +287,21 @@ The layers order is important as it determines how likely previous layers can be The default order is `dependencies`, `spring-boot-loader`, `snapshot-dependencies`, `application`. Content that is least likely to change should be added first, followed by layers that are more likely to change. -When you create a layered jar, the `spring-boot-jarmode-layertools` jar will be added as a dependency to your jar. +To disable this feature, you can do so in the following manner: + +[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] +.Groovy +---- +include::../gradle/packaging/boot-jar-layered-disabled.gradle[tags=layered] +---- + +[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"] +.Kotlin +---- +include::../gradle/packaging/boot-jar-layered-disabled.gradle.kts[tags=layered] +---- + +When a layered jar is created, 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. If you wish to exclude this dependency, you can do so in the following manner: diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle similarity index 86% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle rename to spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle index 2391a6610f..476309dede 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle @@ -9,6 +9,8 @@ bootJar { // tag::layered[] bootJar { - layered() + layered { + enabled = false + } } // end::layered[] diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle.kts b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle.kts similarity index 90% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle.kts rename to spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle.kts index f37aa1db7a..889d4a2dad 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered.gradle.kts +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-disabled.gradle.kts @@ -11,6 +11,8 @@ tasks.getByName("bootJar") { // tag::layered[] tasks.getByName("bootJar") { - layered() + layered { + isEnabled = false + } } // end::layered[] 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 c5e24b132b..6ad01f6d58 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 @@ -64,7 +64,7 @@ public class BootJar extends Jar implements BootArchive { private FileCollection classpath; - private LayeredSpec layered; + private LayeredSpec layered = new LayeredSpec(); /** * Creates a new {@code BootJar} task. @@ -98,13 +98,17 @@ public class BootJar extends Jar implements BootArchive { @Override public void copy() { this.support.configureManifest(getManifest(), getMainClassName(), CLASSES_DIRECTORY, LIB_DIRECTORY, - CLASSPATH_INDEX, (this.layered != null) ? LAYERS_INDEX : null); + CLASSPATH_INDEX, (isLayeredDisabled()) ? null : LAYERS_INDEX); super.copy(); } + private boolean isLayeredDisabled() { + return this.layered != null && !this.layered.isEnabled(); + } + @Override protected CopyAction createCopyAction() { - if (this.layered != null) { + if (!isLayeredDisabled()) { JavaPluginConvention javaPluginConvention = getProject().getConvention() .findPlugin(JavaPluginConvention.class); Iterable sourceSets = (javaPluginConvention != null) ? javaPluginConvention.getSourceSets() diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayeredSpec.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayeredSpec.java index dcec9252d8..1aa9d1753c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayeredSpec.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayeredSpec.java @@ -52,6 +52,8 @@ public class LayeredSpec { private boolean includeLayerTools = true; + private boolean enabled = true; + private ApplicationSpec application = new ApplicationSpec(); private DependenciesSpec dependencies = new DependenciesSpec(); @@ -80,6 +82,24 @@ public class LayeredSpec { this.includeLayerTools = includeLayerTools; } + /** + * Returns whether the layers.idx should be included in the jar. + * @return whether the layers.idx should be included + */ + @Input + public boolean isEnabled() { + return this.enabled; + } + + /** + * Sets whether the layers.idx should be included in the jar. + * @param enabled {@code true} layers.idx should be included in the jar, otherwise + * {@code false} + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + /** * Returns the {@link ApplicationSpec} that controls the layers to which application * classes and resources belong. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java index 6be656843a..07c46b578f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java @@ -180,16 +180,14 @@ class PackagingDocumentationTests { } @TestTemplate - void bootJarLayered() throws IOException { - this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered").build("bootJar"); + void bootJarLayeredDisabled() throws IOException { + this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered-disabled").build("bootJar"); File file = new File(this.gradleBuild.getProjectDir(), "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); assertThat(file).isFile(); try (JarFile jar = new JarFile(file)) { JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx"); - assertThat(entry).isNotNull(); - assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName) - .filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isNotEmpty(); + assertThat(entry).isNull(); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java index ada979dd2f..6c3a8c273c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java @@ -63,26 +63,34 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { @TestTemplate void upToDateWhenBuiltTwiceWithLayers() throws InvalidRunnerConfigurationException, UnexpectedBuildFailure { - assertThat(this.gradleBuild.build("-Playered=true", "bootJar").task(":bootJar").getOutcome()) + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "bootJar").task(":bootJar").getOutcome()) .isEqualTo(TaskOutcome.SUCCESS); - assertThat(this.gradleBuild.build("-Playered=true", "bootJar").task(":bootJar").getOutcome()) + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "bootJar").task(":bootJar").getOutcome()) .isEqualTo(TaskOutcome.UP_TO_DATE); } @TestTemplate - void notUpToDateWhenBuiltWithoutLayersAndThenWithLayers() + void upToDateWhenBuiltWithDefaultLayeredAndThenWithExplicitLayered() throws InvalidRunnerConfigurationException, UnexpectedBuildFailure { assertThat(this.gradleBuild.build("bootJar").task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); - assertThat(this.gradleBuild.build("-Playered=true", "bootJar").task(":bootJar").getOutcome()) - .isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "bootJar").task(":bootJar").getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); } @TestTemplate - void notUpToDateWhenBuiltWithLayersAndToolsAndThenWithLayersAndWithoutTools() + void notUpToDateWhenBuiltWithoutLayersAndThenWithLayers() throws InvalidRunnerConfigurationException, UnexpectedBuildFailure { - assertThat(this.gradleBuild.build("-Playered=true", "bootJar").task(":bootJar").getOutcome()) + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "-PdisableLayers=true", "bootJar").task(":bootJar") + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "bootJar").task(":bootJar").getOutcome()) .isEqualTo(TaskOutcome.SUCCESS); - assertThat(this.gradleBuild.build("-Playered=true", "-PexcludeTools=true", "bootJar").task(":bootJar") + } + + @TestTemplate + void notUpToDateWhenBuiltWithLayerToolsAndThenWithoutLayerTools() + throws InvalidRunnerConfigurationException, UnexpectedBuildFailure { + assertThat(this.gradleBuild.build("bootJar").task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PcustomizeLayered=true", "-PexcludeTools=true", "bootJar").task(":bootJar") .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); } 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 8ff1107d08..6f8a288f88 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 @@ -83,6 +83,30 @@ class BootJarTests extends AbstractBootArchiveTests { } } + @Test + void jarShouldBeLayeredByDefault() throws IOException { + addContent(); + executeTask(); + BootJar bootJar = getTask(); + try (JarFile jarFile = new JarFile(bootJar.getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classes")) + .isEqualTo("BOOT-INF/classes/"); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Lib")) + .isEqualTo("BOOT-INF/lib/"); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classpath-Index")) + .isEqualTo("BOOT-INF/classpath.idx"); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Layers-Index")) + .isEqualTo("BOOT-INF/layers.idx"); + assertThat(getEntryNames(jarFile)).contains("BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName()); + } + } + + @Test + void jarWhenLayersDisabledShouldNotContainLayersIndex() throws IOException { + List entryNames = getEntryNames(createLayeredJar((configuration) -> configuration.setEnabled(false))); + assertThat(entryNames).doesNotContain("BOOT-INF/layers.idx"); + } + @Test void whenJarIsLayeredThenManifestContainsEntryForLayersIndexInPlaceOfClassesAndLib() throws IOException { try (JarFile jarFile = new JarFile(createLayeredJar())) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchive.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchive.gradle index e4365c4ccf..d7d658a366 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchive.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchive.gradle @@ -15,3 +15,9 @@ dependencies { developmentOnly("org.apache.commons:commons-lang3:3.9") implementation("commons-io:commons-io:2.6") } + +bootJar { + layered { + enabled = false + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchiveByDefault.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchiveByDefault.gradle index 003360efa5..04b50c51f4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchiveByDefault.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesAreNotIncludedInTheArchiveByDefault.gradle @@ -16,3 +16,9 @@ dependencies { developmentOnly("commons-io:commons-io:2.6") implementation("commons-io:commons-io:2.6") } + +bootJar { + layered { + enabled = false + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesCanBeIncludedInTheArchive.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesCanBeIncludedInTheArchive.gradle index 9c84b35c14..0c4794a7dd 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesCanBeIncludedInTheArchive.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-developmentOnlyDependenciesCanBeIncludedInTheArchive.gradle @@ -19,3 +19,9 @@ dependencies { bootJar { classpath configurations.developmentOnly } + +bootJar { + layered { + enabled = false + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle index fb56a68e55..5b6aa89104 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle @@ -10,9 +10,10 @@ bootJar { properties 'prop' : project.hasProperty('launchScriptProperty') ? launchScriptProperty : 'default' } } - if (project.hasProperty('layered') && project.getProperty('layered')) { + if (project.hasProperty('customizeLayered') && project.getProperty('customizeLayered')) { layered { includeLayerTools = project.hasProperty('excludeTools') && project.getProperty('excludeTools') ? false : true + enabled = project.hasProperty('disableLayers') && project.getProperty('disableLayers') ? false : true } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java index adc0d47a8c..16d5a17a4e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java @@ -168,18 +168,25 @@ public abstract class Packager { protected final void write(JarFile sourceJar, Libraries libraries, AbstractJarWriter writer) throws IOException { Assert.notNull(libraries, "Libraries must not be null"); WritableLibraries writeableLibraries = new WritableLibraries(libraries); - if (this.layers != null) { + if (isLayersEnabled()) { writer = new LayerTrackingEntryWriter(writer); } writer.writeManifest(buildManifest(sourceJar)); writeLoaderClasses(writer); writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries); writeableLibraries.write(writer); - if (this.layers != null) { + if (isLayersEnabled()) { writeLayerIndex(writer); } } + private boolean isLayersEnabled() { + if (!(getLayout() instanceof Layouts.Jar)) { + return false; + } + return this.layers != null; + } + private void writeLoaderClasses(AbstractJarWriter writer) throws IOException { Layout layout = getLayout(); if (layout instanceof CustomLoaderLayout) { @@ -332,7 +339,7 @@ public abstract class Packager { attributes.putValue(BOOT_CLASSES_ATTRIBUTE, layout.getRepackagedClassesLocation()); putIfHasLength(attributes, BOOT_LIB_ATTRIBUTE, getLayout().getLibraryLocation("", LibraryScope.COMPILE)); putIfHasLength(attributes, BOOT_CLASSPATH_INDEX_ATTRIBUTE, layout.getClasspathIndexFileLocation()); - if (this.layers != null) { + if (isLayersEnabled()) { putIfHasLength(attributes, BOOT_LAYERS_INDEX_ATTRIBUTE, layout.getLayersIndexFileLocation()); } } @@ -465,7 +472,7 @@ public abstract class Packager { addLibrary(library); } }); - if (Packager.this.layers != null && Packager.this.includeRelevantJarModeJars) { + if (isLayersEnabled() && Packager.this.includeRelevantJarModeJars) { addLibrary(JarModeLibrary.LAYER_TOOLS); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging.adoc b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging.adoc index 1cf342fbcb..67d1bacc02 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging.adoc @@ -80,7 +80,20 @@ A repackaged jar contains the application's classes and dependencies in `BOOT-IN For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these directories further so that they can be written into distinct layers. Layered jars use the same layout as regular repackaged jars, but include an additional meta-data file that describes each layer. -To use this feature, the layering feature must be enabled: + +By default, the following layers are defined: + +* `dependencies` for any dependency whose version does not contain `SNAPSHOT`. +* `spring-boot-loader` for the jar loader classes. +* `snapshot-dependencies` for any dependency whose version contains `SNAPSHOT`. +* `application` for application classes and resources. + +The layers order is important as it determines how likely previous layers can be cached when part of the application changes. +The default order is `dependencies`, `spring-boot-loader`, `snapshot-dependencies`, `application`. +Content that is least likely to change should be added first, followed by layers that are more likely to change. + +The repackaged jar includes the `layers.idx` file by default. +To disable this feature, you can do so in the following manner: [source,xml,indent=0,subs="verbatim,attributes"] ---- @@ -93,7 +106,7 @@ To use this feature, the layering feature must be enabled: {gradle-project-version} - true + false @@ -102,17 +115,6 @@ To use this feature, the layering feature must be enabled: ---- -By default, the following layers are defined: - -* `dependencies` for any dependency whose version does not contain `SNAPSHOT`. -* `spring-boot-loader` for the jar loader classes. -* `snapshot-dependencies` for any dependency whose version contains `SNAPSHOT`. -* `application` for application classes and resources. - -The layers order is important as it determines how likely previous layers can be cached when part of the application changes. -The default order is `dependencies`, `spring-boot-loader`, `snapshot-dependencies`, `application`. -Content that is least likely to change should be added first, followed by layers that are more likely to change. - [[repackage-layers-configuration]] @@ -531,7 +533,7 @@ This example excludes any artifact belonging to the `com.foo` group: [[repackage-layered-jars-tools]] ==== Layered Jar Tools -When you create a layered jar, the `spring-boot-jarmode-layertools` jar will be added as a dependency to your jar. +When a layered jar is created, 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. If you wish to exclude this dependency, you can do so in the following manner: @@ -546,7 +548,6 @@ If you wish to exclude this dependency, you can do so in the following manner: {gradle-project-version} - true false diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java index 7e8be36c80..09e75be907 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java @@ -297,7 +297,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests { } @TestTemplate - void whenJarIsRepackagedWithLayersEnabledTheJarContainsTheLayersIndex(MavenBuild mavenBuild) { + void repackagedJarContainsTheLayersIndexByDefault(MavenBuild mavenBuild) { mavenBuild.project("jar-layered").execute((project) -> { File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar"); assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/") @@ -316,6 +316,18 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests { }); } + @TestTemplate + void whenJarIsRepackagedWithTheLayersDisabledDoesNotContainLayersIndex(MavenBuild mavenBuild) { + mavenBuild.project("jar-layered-disabled").execute((project) -> { + File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar"); + assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/") + .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release") + .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot") + .doesNotHaveEntryWithName("BOOT-INF/layers.idx") + .doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName()); + }); + } + @TestTemplate void whenJarIsRepackagedWithTheLayersEnabledAndLayerToolsExcluded(MavenBuild mavenBuild) { mavenBuild.project("jar-layered-no-layer-tools").execute((project) -> { @@ -323,6 +335,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests { assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot") + .hasEntryWithNameStartingWith("BOOT-INF/layers.idx") .doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName()); }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-release/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-release/pom.xml new file mode 100644 index 0000000000..a06fe545f1 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-release/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + org.springframework.boot.maven.it + jar-release + 0.0.1.RELEASE + jar + jar + Release Jar dependency + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-snapshot/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-snapshot/pom.xml new file mode 100644 index 0000000000..ab31e719ba --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar-snapshot/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + org.springframework.boot.maven.it + jar-snapshot + 0.0.1.BUILD-SNAPSHOT + jar + jar + Snapshot Jar dependency + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/pom.xml new file mode 100644 index 0000000000..3bc2d63626 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + org.springframework.boot.maven.it + jar-layered + 0.0.1.BUILD-SNAPSHOT + + UTF-8 + @java.version@ + @java.version@ + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + repackage + + + + false + + + + + + + + + + org.springframework.boot.maven.it + jar-snapshot + 0.0.1.BUILD-SNAPSHOT + + + org.springframework.boot.maven.it + jar-release + 0.0.1.RELEASE + + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/src/main/java/org/test/SampleApplication.java new file mode 100644 index 0000000000..ca2b9a2f0e --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/jar/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-2020 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 org.test; + +public class SampleApplication { + + public static void main(String[] args) { + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/pom.xml new file mode 100644 index 0000000000..fdd9895381 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-disabled/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + org.springframework.boot.maven.it + aggregator + 0.0.1.BUILD-SNAPSHOT + pom + + UTF-8 + @java.version@ + @java.version@ + + + jar-snapshot + jar-release + jar + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-no-layer-tools/jar/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-no-layer-tools/jar/pom.xml index 96d431fee8..b3db8941df 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-no-layer-tools/jar/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered-no-layer-tools/jar/pom.xml @@ -23,7 +23,6 @@ - true false diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered/jar/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered/jar/pom.xml index d8f190dcbb..53d7a40c14 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered/jar/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-layered/jar/pom.xml @@ -21,11 +21,6 @@ repackage - - - true - - diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractPackagerMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractPackagerMojo.java index 94b45034a3..3a2824b61e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractPackagerMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractPackagerMojo.java @@ -135,7 +135,10 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo getLog().info("Layout: " + this.layout); packager.setLayout(this.layout.layout()); } - if (this.layers != null && this.layers.isEnabled()) { + if (this.layers == null) { + packager.setLayers(IMPLICIT_LAYERS); + } + else if (this.layers.isEnabled()) { packager.setLayers((this.layers.getConfiguration() != null) ? getCustomLayers(this.layers.getConfiguration()) : IMPLICIT_LAYERS); packager.setIncludeRelevantJarModeJars(this.layers.isIncludeLayerTools()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Layers.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Layers.java index a3d253863b..bb1f912289 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Layers.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Layers.java @@ -26,7 +26,7 @@ import java.io.File; */ public class Layers { - private boolean enabled; + private boolean enabled = true; private boolean includeLayerTools = true;