Skip to content

Add extract support for executable war files #51367

Description

@ttreuthardt

Summary

Running the tools jarmode extract command against an executable Spring Boot WAR fails with IllegalStateException: Invalid library location WEB-INF/lib-provided/.... The extractor assumes every entry listed in WEB-INF/classpath.idx lives under the single Spring-Boot-Lib directory (WEB-INF/lib/), but an executable WAR legitimately also places the provided / embedded-container dependencies under WEB-INF/lib-provided/ and records them in the classpath index. The first lib-provided entry encountered aborts the command.

Environment

  • Spring Boot: 4.1.0
  • Packaging: WAR (executable, WarLauncher)
  • Java (runtime): 25
  • Build: Maven WAR Plugin 3.5.1

Steps to reproduce

A minimal project generated from start.spring.io reproduces the problem.
The key factors are WAR packaging plus the web dependency, which places the embedded Tomcat and spring-boot-web-server artifacts in WEB-INF/lib-provided/.

  1. Generate a project (Maven, Java, WAR packaging, Java 25, Spring Boot 4.1.0,
    web dependency):

    https://start.spring.io/#!type=maven-project&language=java&platformVersion=4.1.0&packaging=war&configurationFileFormat=properties&jvmVersion=25&groupId=com.example&artifactId=demo&packageName=com.example.demo&dependencies=web

  2. Build the WAR: ./mvnw package

  3. Run the tools jarmode extract command: java -Djarmode=tools -jar target/demo-0.0.1-SNAPSHOT.war extract

The command fails with the stack trace shown below.

Actual behavior

java.lang.IllegalStateException: java.lang.IllegalStateException: Invalid library location WEB-INF/lib-provided/spring-boot-web-server-4.1.0.jar
        at org.springframework.boot.jarmode.tools.ToolsJarMode.run(ToolsJarMode.java:58)
        at org.springframework.boot.loader.launch.JarModeRunner.runJarMode(JarModeRunner.java:64)
        at org.springframework.boot.loader.launch.JarModeRunner.main(JarModeRunner.java:44)
        ...
Caused by: java.lang.IllegalStateException: Invalid library location WEB-INF/lib-provided/spring-boot-web-server-4.1.0.jar
        at org.springframework.util.Assert.state(Assert.java:102)
        at org.springframework.boot.jarmode.tools.IndexedJarStructure.toStructureDependency(IndexedJarStructure.java:133)
        at org.springframework.boot.jarmode.tools.IndexedJarStructure.resolve(IndexedJarStructure.java:102)
        at org.springframework.boot.jarmode.tools.JarStructure.resolve(JarStructure.java:40)
        at org.springframework.boot.jarmode.tools.ExtractCommand.lambda$extractLibraries$0(ExtractCommand.java:162)
        ...

Expected behavior

extract completes and lays out the archive contents, treating WEB-INF/lib-provided/ entries as libraries (or otherwise handling them) instead of asserting on the Spring-Boot-Lib prefix.

Analysis

The WAR is internally consistent. Manifest, WEB-INF/layers.idx, and WEB-INF/classpath.idx all agree that WEB-INF/lib-provided/ exists and is on the runtime classpath. Relevant manifest attributes:

Spring-Boot-Version: 4.1.0
Spring-Boot-Lib: WEB-INF/lib/
Spring-Boot-Classpath-Index: WEB-INF/classpath.idx
Spring-Boot-Layers-Index: WEB-INF/layers.idx
Main-Class: org.springframework.boot.loader.launch.WarLauncher

During extraction, IndexedJarStructure.resolve(String) classifies every archive entry.
Any entry present in classpathEntries (loaded verbatim from classpath.idx) is treated as a library and passed to toStructureDependency, which asserts on the Spring-Boot-Lib prefix:

private String toStructureDependency(String location) {
    Assert.state(location.startsWith(this.libLocation),   // libLocation = "WEB-INF/lib/"
            () -> "Invalid library location " + location);
    return location.substring(this.libLocation.length());
}

Our context

We are transitioning from traditional WAR deployments to running Spring Boot applications with the embedded Tomcat. During this transition we need to keep producing WAR files, while still using the tools jarmode to build layered container images. Hence, we would welcome a fix that allows tools extract to work with executable WARs as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions