lukaszlenart opened a new pull request, #1937: URL: https://github.com/apache/struts/pull/1937
Fixes [WW-5740](https://issues.apache.org/jira/browse/WW-5740). Docs: apache/struts-site PR linked below. ## Problem A field reached through a `visitor` validator (`user.name`) had its `data-msg-*` text resolved through `DelegatingValidatorContext(action)` — the action's bundles only — while `VisitorFieldValidator.validateObject` validates it with a composite provider over the visited class and the action, and pushes the visited instance for `${...}`. A `<message key="name.required"/>` living in `ConstraintUser.properties` rendered as the raw key. ## What changed - **`Form`** gives each unwrapped visitor validator, at unwrap time, the context validation gives it: `DelegatingValidatorContext(parent, CompositeTextProvider([each visited level innermost first, parent]), parent)` — line for line `VisitorFieldValidator.validateObject`. Providers are class-based (runtime class when the instance exists), so a form rendered before the visited object exists still finds the bundle. The wrapper carries the whole visited chain (classes + OGNL paths, outermost first); visitor validators are cached per visited path rather than per class, since two visitors over one class must not share the instances the context is set on. - **`Form.getValidatedObject(name)`** (new, `@since 7.4.0`) returns the innermost visited instance when the whole chain exists and exactly one visited path claims the field; otherwise `null` and the caller uses the action. The lookup re-pushes the validation-time stack — the action plus, for a `ModelDriven` action whose interceptor pushed it, the model instance taken from the stack (identity-checked) — so an `<s:iterator>`/`<s:push>` frame above the action cannot shadow the object validation used. - **`UIBean`** pushes the visited chain (popped by the existing `restoreStackDepth`) and hands the provider that object, so `${...}` and `messageParams` read the visited bean as during validation. - **`HtmlConstraintProvider`**: no signature change; the third parameter's Javadoc now says what object it is. ## Review history Eight passes (security + seven `/code-review`), each landing as its own commit; the significant ones: doubly-nested visitors chained through every level; ambiguous fields (two `appendPrefix="false"` visitors, or visitor-nested next to direct) fall back to the action; visited objects read off the action rather than the top of the render stack; partial chains hand over the action; the model is the instance the interceptor pushed, identity-checked, never `getModel()` re-invoked — the HIGH found on pass six was `modelDriven` being in `defaultStack`, so "interceptor configured" alone promoted iterator elements to models. ## Known limitations, stated on purpose - The visitor's own `<message>` (which `AppendingValidatorContext` prepends to submitted errors, "User: Name is required") is not reflected in `data-msg-*`. It is error-display decoration rather than the field's message; the deprecated JS validator never applied it either. - Validators come from the form's *target* action, visited instances from the *rendering* action's stack. When the two differ (`list` renders `<s:form action="save">`), messages resolve against the rendering action's objects. Pre-existing; the previous code resolved against the rendering action too. - The deprecated JS path shares the cached validator instances, so under the html5 theme with `validate="true"` a visitor-nested field's JS message now also resolves through the visited bundle once the field has been rendered. An improvement for that case, but it is render-order dependent. ## Tests `mvn test -DskipAssembly -pl core`: 3376 tests, 0 failures. Every production change went red first (the initial symptom, the raw `name.required`, is the first RED). Fixtures: `ConstraintUser.properties`/`AdminConstraintUser.properties`, `ConstraintAddress`, `ModelDrivenConstraintAction`, and a `modelDrivenConstraintAction` config in `TestConfigurationProvider`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
