-
Notifications
You must be signed in to change notification settings - Fork 392
Description
GitHub Issue: ComponentFlow fails with missing StringTemplate files in Spring Shell 4.0.0-RC1 (regression from 4.0.0-M1)
Description
When upgrading from Spring Shell 4.0.0-M1 to 4.0.0-RC1, ComponentFlow interactive flows fail with FileNotFoundException for StringTemplate (.stg) files. The required template files for rendering interactive components are missing from the classpath, making ComponentFlow unusable.
Environment
- Spring Shell Version: 4.0.0-RC1
- Previous Working Version: 4.0.0-M1
- Spring Boot Version: 4.0.1
- Java Version: 21
- OS: Windows/Linux/macOS (all affected)
Expected Behavior
When a command method calls ComponentFlow.run(), the interactive TUI components (single item selector, string input, etc.) should be displayed to the user, allowing them to interactively provide input. The flow should complete successfully and return the user's selections in the ComponentContext.
This behavior worked correctly in Spring Shell 4.0.0-M1.
Actual Behavior
The interactive flow fails with a FileNotFoundException:
shell:>test
Parameters received - name: '', type: ''
Parameters missing, falling back to interactive mode...
Error running interactive flow: java.io.FileNotFoundException: class path resource [org/springframework/shell/component/single-item-selector-default.stg] cannot be opened because it does not exist
java.io.UncheckedIOException: java.io.FileNotFoundException: class path resource [org/springframework/shell/component/single-item-selector-default.stg] cannot be opened because it does not exist
at org.springframework.shell.jline.tui.component.support.AbstractComponent.resourceAsString(AbstractComponent.java:346)
at org.springframework.shell.jline.tui.component.support.AbstractComponent.renderTemplateResource(AbstractComponent.java:227)
at org.springframework.shell.jline.tui.component.SingleItemSelector.access$000(SingleItemSelector.java:46)
at org.springframework.shell.jline.tui.component.SingleItemSelector$DefaultRenderer.apply(SingleItemSelector.java:184)
...
The StringTemplate files (.stg) required by ComponentFlow components are missing from the classpath, preventing any interactive component from rendering.
Steps to Reproduce
I've created a minimal reproducible example:
1. Create a Spring Boot application with Spring Shell dependency
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.1</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-shell-componentflow-bug</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
<spring-shell.version>4.0.0-RC1</spring-shell.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
<version>${spring-shell.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>2. Create a command that uses ComponentFlow
TestCommand.java:
package com.example.shellbug;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
import org.springframework.shell.jline.tui.component.context.ComponentContext;
import org.springframework.shell.jline.tui.component.flow.ComponentFlow;
import org.springframework.stereotype.Component;
@Component
public class TestCommand {
@Autowired
private ComponentFlow.Builder componentFlowBuilder;
@Command(name = "test", description = "Test command with interactive flow")
public void testCommand(
@Option(description = "Name parameter", defaultValue = "") String name,
@Option(description = "Type parameter", defaultValue = "") String type
) {
System.out.println("Parameters received - name: '" + name + "', type: '" + type + "'");
// If parameters are missing, use interactive flow
if (name.isEmpty() || type.isEmpty()) {
System.out.println("Parameters missing, falling back to interactive mode...");
try {
ComponentContext<?> result = runInteractiveFlow();
// Extract values from interactive flow
name = result.get("name", String.class);
type = result.get("type", String.class);
System.out.println("Interactive values - name: '" + name + "', type: '" + type + "'");
} catch (Exception e) {
System.err.println("Error running interactive flow: " + e.getMessage());
e.printStackTrace();
return;
}
}
System.out.println("Final result - name: " + name + ", type: " + type);
}
public ComponentContext<?> runInteractiveFlow() {
Map<String, String> typeItems = new HashMap<>();
typeItems.put("rest", "REST Client");
typeItems.put("soap", "SOAP Client");
ComponentFlow flow = componentFlowBuilder.clone().reset()
.withSingleItemSelector("type")
.name("Choose client type:")
.selectItems(typeItems)
.defaultSelect("rest")
.and()
.withStringInput("name")
.name("Enter artifact name:")
.defaultValue("my-client")
.and()
.build();
return flow.run().getContext();
}
}3. Run the application
mvn clean package
java -jar target/spring-shell-componentflow-bug-1.0-SNAPSHOT.jar4. Execute the command
In the shell prompt:
shell:>test
5. Observe the error
Expected output:
shell:>test
Parameters received - name: '', type: ''
Parameters missing, falling back to interactive mode...
[Interactive menu appears with client type selector]
[User makes selections]
Interactive values - name: 'my-client', type: 'rest'
Final result - name: my-client, type: rest
Actual output (4.0.0-RC1):
shell:>test
Parameters received - name: '', type: ''
Parameters missing, falling back to interactive mode...
Error running interactive flow: java.io.FileNotFoundException: class path resource [org/springframework/shell/component/single-item-selector-default.stg] cannot be opened because it does not exist
java.io.UncheckedIOException: java.io.FileNotFoundException: class path resource [org/springframework/shell/component/single-item-selector-default.stg] cannot be opened because it does not exist
at org.springframework.shell.jline.tui.component.support.AbstractComponent.resourceAsString(AbstractComponent.java:346)
at org.springframework.shell.jline.tui.component.support.AbstractComponent.renderTemplateResource(AbstractComponent.java:227)
at org.springframework.shell.jline.tui.component.SingleItemSelector.access$000(SingleItemSelector.java:46)
at org.springframework.shell.jline.tui.component.SingleItemSelector$DefaultRenderer.apply(SingleItemSelector.java:184)
...
Analysis
The ComponentFlow API has not been marked as deprecated, and there were no migration notes indicating changes to this functionality. The same code pattern works in 4.0.0-M1 but fails in 4.0.0-RC1, indicating a regression rather than an intentional API change.
Root cause: The StringTemplate Group files (.stg) required by ComponentFlow components are missing from the classpath in RC1. These template files are essential for rendering interactive components like SingleItemSelector, StringInput, etc.
Missing template files include (but are not limited to):
org/springframework/shell/component/single-item-selector-default.stg- Other component template files
This appears to be a packaging/build issue in the spring-shell-jline module where the .stg template resources are not being included in the JAR, or a classpath resolution issue when running as a Spring Boot executable JAR.
The minimal reproducible example includes two test commands:
test- Uses theclone().reset()pattern (fails with missing template error)test-no-clone- Direct builder usage without clone/reset (likely same error)
Possible technical causes:
- Maven resource filtering configuration changed between M1 and RC1
- Template files moved to a different module not included in spring-shell-starter
- ClassLoader issues with Spring Boot's nested JAR structure in RC1
- Build configuration excluding
.stgfiles from the final JAR
Impact
This is a blocking issue for GA release as it breaks existing applications that rely on interactive flows for user input. Many CLI tools use ComponentFlow to provide guided user experiences, and this regression makes them unusable.
Workaround
Currently, the only workaround is to stay on Spring Shell 4.0.0-M1, which is not ideal as it's a milestone release.
Additional Context
We discovered this issue when upgrading a production tool (honey-shell) that provides interactive Maven archetype generation. The tool has multiple commands that all rely on ComponentFlow for interactive mode when parameters are not provided via command line.
Related code patterns in our production codebase:
- Multiple commands with optional parameters
- Fallback to interactive ComponentFlow when parameters missing
- Validation before calling ComponentFlow
All of these patterns work in 4.0.0-M1 but fail in 4.0.0-RC1.
Request
Given that 4.0.0 GA is approaching, could this be prioritized? I'm happy to provide additional debugging information or test patches if needed.