Remove support for the deprecated layertools jar mode

Closes gh-48568
This commit is contained in:
Stéphane Nicoll
2025-12-22 08:36:52 +01:00
parent 41828b6e15
commit da56bfea6e
11 changed files with 21 additions and 257 deletions
@@ -333,9 +333,9 @@ abstract class AbstractBootArchiveIntegrationTests {
assertThat(task).isNotNull();
assertThat(task.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
Map<String, List<String>> indexedLayers;
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
assertThat(jarFile.getEntry(layerToolsJar)).isNotNull();
assertThat(jarFile.getEntry(toolsJar)).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "commons-lang3-3.9.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "spring-core-5.2.5.RELEASE.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "spring-jcl-5.2.5.RELEASE.jar")).isNotNull();
@@ -360,7 +360,7 @@ abstract class AbstractBootArchiveIntegrationTests {
expectedDependencies.add(this.libPath + "spring-boot-starter-logging-2.2.0.RELEASE.jar");
Set<String> expectedSnapshotDependencies = new TreeSet<>();
expectedSnapshotDependencies.add(this.libPath + "library-1.0-SNAPSHOT.jar");
(layerToolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(layerToolsJar);
(toolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(toolsJar);
assertThat(indexedLayers.get("dependencies")).containsExactlyElementsOf(expectedDependencies);
assertThat(indexedLayers.get("spring-boot-loader")).containsExactly("org/");
assertThat(indexedLayers.get("snapshot-dependencies")).containsExactlyElementsOf(expectedSnapshotDependencies);
@@ -390,9 +390,9 @@ abstract class AbstractBootArchiveIntegrationTests {
assertThat(task).isNotNull();
assertThat(task.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
Map<String, List<String>> indexedLayers;
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
assertThat(jarFile.getEntry(layerToolsJar)).isNotNull();
assertThat(jarFile.getEntry(toolsJar)).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "alpha-1.2.3.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "bravo-1.2.3.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "charlie-1.2.3.jar")).isNotNull();
@@ -413,7 +413,7 @@ abstract class AbstractBootArchiveIntegrationTests {
expectedDependencies.add(this.libPath + "spring-jcl-5.2.5.RELEASE.jar");
Set<String> expectedSnapshotDependencies = new TreeSet<>();
expectedSnapshotDependencies.add(this.libPath + "library-1.0-SNAPSHOT.jar");
(layerToolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(layerToolsJar);
(toolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(toolsJar);
assertThat(indexedLayers.get("dependencies")).containsExactlyElementsOf(expectedDependencies);
assertThat(indexedLayers.get("spring-boot-loader")).containsExactly("org/");
assertThat(indexedLayers.get("snapshot-dependencies")).containsExactlyElementsOf(expectedSnapshotDependencies);
@@ -442,9 +442,9 @@ abstract class AbstractBootArchiveIntegrationTests {
assertThat(task).isNotNull();
assertThat(task.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
Map<String, List<String>> indexedLayers;
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
assertThat(jarFile.getEntry(layerToolsJar)).isNotNull();
assertThat(jarFile.getEntry(toolsJar)).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "commons-lang3-3.9.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "spring-core-5.2.5.RELEASE.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "spring-jcl-5.2.5.RELEASE.jar")).isNotNull();
@@ -462,7 +462,7 @@ abstract class AbstractBootArchiveIntegrationTests {
expectedDependencies.add(this.libPath + "spring-jcl-5.2.5.RELEASE.jar");
List<String> expectedSnapshotDependencies = new ArrayList<>();
expectedSnapshotDependencies.add(this.libPath + "library-1.0-SNAPSHOT.jar");
(layerToolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(layerToolsJar);
(toolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(toolsJar);
assertThat(indexedLayers.get("dependencies")).containsExactlyElementsOf(expectedDependencies);
assertThat(indexedLayers.get("commons-dependencies")).containsExactly(this.libPath + "commons-lang3-3.9.jar");
assertThat(indexedLayers.get("snapshot-dependencies")).containsExactlyElementsOf(expectedSnapshotDependencies);
@@ -495,9 +495,9 @@ abstract class AbstractBootArchiveIntegrationTests {
assertThat(task).isNotNull();
assertThat(task.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
Map<String, List<String>> indexedLayers;
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
assertThat(jarFile.getEntry(layerToolsJar)).isNotNull();
assertThat(jarFile.getEntry(toolsJar)).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "alpha-1.2.3.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "bravo-1.2.3.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "charlie-1.2.3.jar")).isNotNull();
@@ -522,7 +522,7 @@ abstract class AbstractBootArchiveIntegrationTests {
expectedDependencies.add(this.libPath + "spring-jcl-5.2.5.RELEASE.jar");
List<String> expectedSnapshotDependencies = new ArrayList<>();
expectedSnapshotDependencies.add(this.libPath + "library-1.0-SNAPSHOT.jar");
(layerToolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(layerToolsJar);
(toolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(toolsJar);
assertThat(indexedLayers.get("subproject-dependencies"))
.containsExactlyElementsOf(expectedSubprojectDependencies);
assertThat(indexedLayers.get("dependencies")).containsExactlyElementsOf(expectedDependencies);
@@ -488,22 +488,22 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
List<String> index = entryLines(jarFile, this.indexPath + "layers.idx");
assertThat(getLayerNames(index)).containsExactly("dependencies", "spring-boot-loader",
"snapshot-dependencies", "application");
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
List<String> expected = new ArrayList<>();
expected.add("- \"dependencies\":");
expected.add(" - \"" + this.libPath + "first-library.jar\"");
expected.add(" - \"" + this.libPath + "first-project-library.jar\"");
expected.add(" - \"" + this.libPath + "fourth-library.jar\"");
expected.add(" - \"" + this.libPath + "second-library.jar\"");
if (!layerToolsJar.contains("SNAPSHOT")) {
expected.add(" - \"" + layerToolsJar + "\"");
if (!toolsJar.contains("SNAPSHOT")) {
expected.add(" - \"" + toolsJar + "\"");
}
expected.add("- \"spring-boot-loader\":");
expected.add(" - \"org/\"");
expected.add("- \"snapshot-dependencies\":");
expected.add(" - \"" + this.libPath + "second-project-library-SNAPSHOT.jar\"");
if (layerToolsJar.contains("SNAPSHOT")) {
expected.add(" - \"" + layerToolsJar + "\"");
if (toolsJar.contains("SNAPSHOT")) {
expected.add(" - \"" + toolsJar + "\"");
}
expected.add(" - \"" + this.libPath + "third-library-SNAPSHOT.jar\"");
expected.add("- \"application\":");
@@ -542,10 +542,10 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
List<String> index = entryLines(jarFile, this.indexPath + "layers.idx");
assertThat(getLayerNames(index)).containsExactly("my-deps", "my-internal-deps", "my-snapshot-deps",
"resources", "application");
String layerToolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
String toolsJar = this.libPath + JarModeLibrary.TOOLS.getName();
List<String> expected = new ArrayList<>();
expected.add("- \"my-deps\":");
expected.add(" - \"" + layerToolsJar + "\"");
expected.add(" - \"" + toolsJar + "\"");
expected.add("- \"my-internal-deps\":");
expected.add(" - \"" + this.libPath + "first-library.jar\"");
expected.add(" - \"" + this.libPath + "first-project-library.jar\"");
@@ -570,7 +570,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
}
@Test
void whenArchiveIsLayeredThenLayerToolsAreAddedToTheJar() throws IOException {
void whenArchiveIsLayeredThenToolsJarIsIncluded() throws IOException {
List<String> entryNames = getEntryNames(createLayeredJar());
assertThat(entryNames).contains(this.libPath + JarModeLibrary.TOOLS.getName());
}
@@ -1,56 +0,0 @@
/*
* Copyright 2012-present 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.springframework.boot.jarmode.tools;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.loader.jarmode.JarMode;
/**
* {@link JarMode} providing {@code "layertools"} support.
*
* @author Phillip Webb
* @author Scott Frederick
* @since 2.3.0
*/
public class LayerToolsJarMode implements JarMode {
static @Nullable Context contextOverride;
@Override
public boolean accepts(String mode) {
return "layertools".equalsIgnoreCase(mode);
}
@Override
public void run(String mode, String[] args) {
try {
Context context = (contextOverride != null) ? contextOverride : new Context();
new Runner(System.out, context, getCommands(context)).run(args);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
static List<Command> getCommands(Context context) {
return List.of(new ListCommand(context), new ExtractLayersCommand(context));
}
}
@@ -15,7 +15,7 @@
*/
/**
* JarMode support for layertools and tools.
* JarMode support for application tools.
*/
@NullMarked
package org.springframework.boot.jarmode.tools;
@@ -1,4 +1,3 @@
# Jar Modes
org.springframework.boot.loader.jarmode.JarMode=\
org.springframework.boot.jarmode.tools.LayerToolsJarMode,\
org.springframework.boot.jarmode.tools.ToolsJarMode
@@ -1,134 +0,0 @@
/*
* Copyright 2012-present 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.springframework.boot.jarmode.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.jar.JarEntry;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link LayerToolsJarMode}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class LayerToolsJarModeTests {
private static final String[] NO_ARGS = {};
private TestPrintStream out;
private PrintStream systemOut;
@TempDir
@SuppressWarnings("NullAway.Init")
File temp;
@BeforeEach
void setup() throws Exception {
Context context = mock(Context.class);
given(context.getArchiveFile()).willReturn(createJarFile("test.jar"));
this.out = new TestPrintStream(this);
this.systemOut = System.out;
System.setOut(this.out);
LayerToolsJarMode.contextOverride = context;
System.setProperty("jarmode", "layertools");
}
@AfterEach
void restore() {
System.setOut(this.systemOut);
LayerToolsJarMode.contextOverride = null;
System.clearProperty("jarmode");
}
@Test
void mainWithNoParametersShowsHelp() {
new LayerToolsJarMode().run("layertools", NO_ARGS);
assertThat(this.out).hasSameContentAsResource("layertools-help-output.txt");
}
@Test
void mainWithArgRunsCommand() {
new LayerToolsJarMode().run("layertools", new String[] { "list" });
assertThat(this.out).hasSameContentAsResource("layertools-list-output.txt");
}
@Test
void mainWithUnknownCommandShowsErrorAndHelp() {
new LayerToolsJarMode().run("layertools", new String[] { "invalid" });
assertThat(this.out).hasSameContentAsResource("layertools-error-command-unknown-output.txt");
}
@Test
void mainWithUnknownOptionShowsErrorAndCommandHelp() {
new LayerToolsJarMode().run("layertools", new String[] { "extract", "--invalid" });
assertThat(this.out).hasSameContentAsResource("layertools-error-option-unknown-output.txt");
}
@Test
void mainWithOptionMissingRequiredValueShowsErrorAndCommandHelp() {
new LayerToolsJarMode().run("layertools", new String[] { "extract", "--destination" });
assertThat(this.out).hasSameContentAsResource("layertools-error-option-missing-value-output.txt");
}
private File createJarFile(String name) throws Exception {
File file = new File(this.temp, name);
try (ZipOutputStream jarOutputStream = new ZipOutputStream(new FileOutputStream(file))) {
jarOutputStream.putNextEntry(new JarEntry("META-INF/MANIFEST.MF"));
jarOutputStream.write(getFile("test-manifest.MF").getBytes());
jarOutputStream.closeEntry();
JarEntry indexEntry = new JarEntry("BOOT-INF/layers.idx");
jarOutputStream.putNextEntry(indexEntry);
Writer writer = new OutputStreamWriter(jarOutputStream, StandardCharsets.UTF_8);
writer.write("- \"0001\":\n");
writer.write(" - \"BOOT-INF/lib/a.jar\"\n");
writer.write(" - \"BOOT-INF/lib/b.jar\"\n");
writer.write("- \"0002\":\n");
writer.write(" - \"0002 BOOT-INF/lib/c.jar\"\n");
writer.write("- \"0003\":\n");
writer.write(" - \"BOOT-INF/lib/d.jar\"\n");
writer.flush();
}
return file;
}
private String getFile(String fileName) throws Exception {
ClassPathResource resource = new ClassPathResource(fileName, getClass());
InputStreamReader reader = new InputStreamReader(resource.getInputStream());
return FileCopyUtils.copyToString(reader);
}
}
@@ -1,10 +0,0 @@
Error: Unknown command "invalid"
Usage:
java -Djarmode=layertools -jar test.jar
Available commands:
help Help about any command
Deprecated commands:
list List layers from the jar that can be extracted
extract Extracts layers from the jar for image creation
@@ -1,11 +0,0 @@
Warning: This command is deprecated. Use '-Djarmode=tools extract --layers --launcher' instead.
Error: Option "--destination" for the extract command requires a value
Extracts layers from the jar for image creation
Usage:
java -Djarmode=layertools -jar test.jar extract [options] [<layer>...]
Options:
--destination string The destination to extract files to
@@ -1,11 +0,0 @@
Warning: This command is deprecated. Use '-Djarmode=tools extract --layers --launcher' instead.
Error: Unknown option "--invalid" for the extract command
Extracts layers from the jar for image creation
Usage:
java -Djarmode=layertools -jar test.jar extract [options] [<layer>...]
Options:
--destination string The destination to extract files to
@@ -1,8 +0,0 @@
Usage:
java -Djarmode=layertools -jar test.jar
Available commands:
help Help about any command
Deprecated commands:
list List layers from the jar that can be extracted
extract Extracts layers from the jar for image creation
@@ -1,5 +0,0 @@
Warning: This command is deprecated. Use '-Djarmode=tools list-layers' instead.
0001
0002
0003