Skip to content

WW-5849 Design a forward result to replace action chaining - #2129

Draft
lukaszlenart wants to merge 3 commits into
mainfrom
feat/WW-5849-forward-result
Draft

lukaszlenart wants to merge 3 commits into
mainfrom
feat/WW-5849-forward-result

Conversation

@lukaszlenart

@lukaszlenart lukaszlenart commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Fixes WW-5849

What

A design document for a new forward result: a server-side transfer from one action to another within the same request. It is meant to replace action chaining (the chain result plus ChainingInterceptor). Design only; no code yet.

Why

The "Is anyone using action chaining?" thread on user@ showed that chaining is still in use, mostly for server-side branching where the next action depends on the current one's state, without an extra browser round-trip. redirectAction and moving logic into services don't cover that, so chaining can't simply be removed.

Chaining also does much more than that hand-off needs: any action can be chained to, the target name is an expression, every value-stack property is copied by name, and the source action stays in the target's value stack. The design keeps the hand-off and makes both boundaries explicit:

  • which actions can be forwarded to: a target-side allowlist (non-inherited annotation or an <action> attribute)
  • what data crosses: request parameters bound again under the target's own @StrutsParameter rules, plus an explicit hand-off named on both sides; fresh value stack, no property copying

Rollout

  • 7.6.0: forward + struts-7.6.dtd; chain unchanged
  • 7.7.0: deprecate chain, with a log of copied properties as a migration aid
  • 8.0.0: remove chain

Review focus

The calls marked in the document that weren't settled in discussion: hand-off applied by a dedicated interceptor after params, hand-off limited to top-level action setters, depth limit default 5 backed by a startup cycle check on the static forward graph, deprecation one release after forward ships, and Convention's implicit forward carrying no hand-off values.

🤖 Generated with Claude Code

lukaszlenart and others added 3 commits November 8, 2026 10:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Static forward targets make the forward graph known from configuration,
so cycles can be reported when the configuration is built instead of on a
user's request. The per-request depth limit stays as the runtime backstop.

A per-request time budget was considered instead of the depth limit and
rejected: nested forwards overflow the stack long before any useful
budget expires, a running action cannot be safely aborted, and a
load-dependent limit fails under load and cannot be pinned by a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
During the 7.7.0 deprecation phase, chain also logs each target that has
not opted in as a forward target, next to the copied properties, so users
know which actions to mark before switching.

Evolving chain in place towards the forward design (keeping the result
type name and flipping its behaviour step by step) was considered and
rejected. The per-action work for users is the same in both approaches;
in-place evolution saves only the type="chain" -> type="forward" rename,
at the cost of several mode constants and their test matrix until 8.0.0,
a breaking change to struts.chaining.requireAnnotations (shared with
AliasInterceptor), a value-stack isolation step that cannot be phased,
and a result type whose meaning changes across releases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 8, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant