[
https://issues.apache.org/jira/browse/WW-5791?focusedWorklogId=1049650&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1049650
]
ASF GitHub Bot logged work on WW-5791:
--------------------------------------
Author: ASF GitHub Bot
Created on: 23/Oct/26 11:44
Start Date: 23/Oct/26 11:44
Worklog Time Spent: 10m
Work Description: lukaszlenart opened a new pull request, #2053:
URL: https://github.com/apache/struts/pull/2053
Fixes [WW-5791](https://issues.apache.org/jira/browse/WW-5791)
## What
Struts Boot: run a Struts application from `main()` on embedded Tomcat, with
actions registered in Java instead of `struts.xml`. Aimed at newcomers.
Standalone, no Spring dependency, no changes under `core/`.
```java
public class HelloApp implements StrutsBootApplication {
public static void main(String[] args) {
StrutsBoot.run(HelloApp.class, args);
}
@Override
public void actions(ActionRegistry actions) {
actions.action("hello", HelloAction.class).success("hello.ftl");
}
}
```
- **`boot/` (`struts2-boot-starter`)** contains:
- `StrutsBoot` / `StrutsBootServer`: embedded Tomcat that registers
`StrutsPrepareAndExecuteFilter` in code.
- The fluent `ActionRegistry` API.
- `RegistryConfigurationProvider`, which turns the registry into ordinary
`PackageConfig`s through the existing `configProviders` init param.
- `BootSettings`, which reads `struts.boot.port`, `struts.boot.address`
and `struts.boot.context-path` from `struts.properties` or `--key=value`
arguments.
- **Tomcat follows core's Jakarta line:** 10.1.59 by default, 11.0.27 under
`-Pjakartaee11`. The version is managed in the parent POM, and the BOM lists
the starter.
- **`apps/boot-hello`** is an example app shaded into a runnable jar. It is
not deployed (`maven.deploy.skip`), because shading merges the dependencies'
LICENSE/NOTICE files.
- **`THREAT_MODEL.md`** gets a Boot section. The starter's container
defaults are now in the model, and §14 has a new question, Q-boot, for the PMC.
## Behaviour worth reviewing
- **Registry config is never broader than the equivalent `struts.xml`.**
- Packages extend `struts-default` and Strict Method Invocation stays on.
- Action classes are added to the OGNL allowlist the same way the XML
provider adds them.
- Each action allows only its registered method. That is narrower than
XML, which also inherits the package's global allowed methods.
- **Configuration errors fail at startup** with a `ConfigurationException`
for:
- slashes in an action name (use namespaces instead);
- non-`.ftl` views;
- missing templates or unknown interceptor stacks;
- non-public action classes, or classes without a no-arg constructor;
- duplicate actions, within the registry or against `struts.xml`.
- **Container defaults:**
- listens on all interfaces, port 8080 (`struts.boot.address` restricts
the interface);
- error pages carry no stack trace or server version;
- `DefaultServlet` serves an empty per-run docBase;
- Tomcat's base directory is a UUID-named temp directory, deleted on
shutdown;
- `devMode` is never switched on;
- a failed start stops Tomcat and removes the temp directory.
- `jakarta.servlet.jsp-api` is a runtime dependency of the starter.
FreeMarker's `TaglibFactory` needs it, and `tomcat-embed-core` doesn't ship it.
## Not in this PR (parked in WW-5791)
Not covered here:
- WAR-to-jar migration
- JSP views
- Gradle plugin
- other containers
- a fluent API for interceptor stacks
- global results, global exception mappings and wildcards
- a Convention bridge
- dev-mode reload
The struts-site documentation and the 7.5.0 Version Notes entry follow after
merge.
## Testing
- `./mvnw clean install -DskipAssembly`: BUILD SUCCESS, with and without
`-Pjakartaee11`.
- `boot` has 49 tests covering:
- the registry, the provider and the settings;
- end to end on embedded Tomcat:
- FreeMarker rendering;
- POST-only rejects GET;
- `!method` and `method:` blocked with DMI on, with positive controls;
- XML and the registry working side by side;
- context path and redirects;
- busy port and config-error cleanup;
- error-page content;
- bind address.
- `java -jar` on the shaded `boot-hello` jar serves `/hello.action` with 200.
Design and plan:
`docs/superpowers/specs/2026-10-23-WW-5791-struts-boot-design.md`,
`docs/superpowers/plans/2026-10-23-WW-5791-struts-boot.md`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Issue Time Tracking
-------------------
Worklog Id: (was: 1049650)
Remaining Estimate: 0h
Time Spent: 10m
> Struts Boot: run a Struts application from main() with an embedded container
> and programmatic action registration
> -----------------------------------------------------------------------------------------------------------------
>
> Key: WW-5791
> URL: https://issues.apache.org/jira/browse/WW-5791
> Project: Struts 2
> Issue Type: New Feature
> Components: Core
> Reporter: Lukasz Lenart
> Assignee: Lukasz Lenart
> Priority: Major
> Fix For: 7.5.0
>
> Time Spent: 10m
> Remaining Estimate: 0h
>
> h2. Goal
> Give newcomers a working Struts application with almost no setup: add one
> dependency, write an action class, run {{main()}}, see the page. No
> {{web.xml}}, no {{struts.xml}}, no external servlet container. Standalone —
> no Spring dependency.
> h2. Minimal scope
> * New {{struts-boot-starter}} artifact pulling in core and an embedded
> servlet container (Tomcat), registering {{StrutsPrepareAndExecuteFilter}} in
> code.
> * {{StrutsBoot.run(App.class, args)}} entry point; the application class
> implements {{StrutsBootApplication}}.
> * Fluent action registration, fed into the configuration through a single
> {{ConfigurationProvider}} built on {{PackageConfig}} / {{ActionConfig}} /
> {{ResultConfig}} builders. No Convention plugin, no classpath scanning.
> {code:java}public class HelloApp implements StrutsBootApplication {
> public static void main(String[] args) {
> StrutsBoot.run(HelloApp.class, args);
> }
> @Override
> public void actions(ActionRegistry actions) {
> actions.action("hello", HelloAction.class)
> .success("hello.ftl");
> actions.namespace("/admin", admin -> {
> admin.action("users", UserAction.class)
> .method("list")
> .success("admin/users.ftl");
> admin.action("saveUser", UserAction.class)
> .method("save")
> .httpMethods(POST)
> .result("input", "admin/userForm.ftl")
> .redirectAction("success", "users");
> });
> }
> }
> {code}
> * Defaults: each namespace is a package extending {{struts-default}}; top
> level is namespace {{""}}; {{method}} defaults to {{execute}};
> {{success(path)}} is shorthand for {{result("success", path)}}; interceptors
> default to {{defaultStack}}, overridable per action or namespace by stack
> name; {{httpMethods(...)}} maps to the http-method restriction from WW-3717.
> * Views: FreeMarker only, chosen by {{.ftl}} extension, resolved from
> {{classpath:/templates/}}.
> * Configuration: the existing {{struts.properties}}; Boot-only settings
> (port, context path) under a {{struts.boot.*}} prefix. Not
> {{application.properties}}, to avoid clashing with Spring Boot.
> * A {{struts.xml}} on the classpath is still loaded alongside the registry.
> * Runnable fat jar via a Maven plugin.
> h2. Parked for later
> * Running an existing Struts WAR as a self-contained jar (migration path).
> * JSP views in the embedded container.
> * Gradle plugin.
> * Alternative containers (Jetty, Undertow).
> * Fluent definition of custom interceptor stacks.
> * Global results, global exception mappings, wildcard actions in the registry.
> * Optional bridge to Convention plugin annotations.
> * Dev-mode reload of templates and registry.
> * Project generator / quick-start archetype.
> * Health and info endpoints.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)