Skip to content

Fix field cache to let child class fields take precedence over parent - #12626

Open
gnodet wants to merge 1 commit into
maven-4.0.xfrom
fix/field-cache-child-precedence
Open

Fix field cache to let child class fields take precedence over parent#12626
gnodet wants to merge 1 commit into
maven-4.0.xfrom
fix/field-cache-child-precedence

Conversation

@gnodet

@gnodet gnodet commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix EnhancedCompositeBeanHelper.buildFieldCache() to use putIfAbsent() instead of put(), so child class fields take precedence over parent fields with the same name
  • Add regression tests for field-shadowing scenarios (ParentBean/ChildBean with same skip field)

Problem

When a child Mojo class shadows a parent's field (e.g. both ResourcesMojo and TestResourcesMojo declare private boolean skip), the field cache incorrectly resolves to the parent's field. This causes POM <configuration><skip>true</skip></configuration> to inject into the parent's skip field but not the child's — breaking TestResourcesMojo's skip behavior.

Root cause: DeclaredMembers iterates child-first then parent. The field cache used put() which overwrites the child entry with the parent's. The method cache already correctly uses putIfAbsent() — this was an asymmetry.

Fix

One-line change: fieldMap.put(...)fieldMap.putIfAbsent(...) in buildFieldCache().

This matches the method cache behavior and ensures the first-seen field (from the child class) wins.

Reported in: apache/maven-resources-plugin#497

Test plan

  • New test testSetPropertyOnShadowedFieldInjectsIntoChildField — verifies child field receives the injected value
  • New test testShadowedFieldCacheConsistency — verifies cache reuse preserves correct field resolution
  • All 9 existing + new EnhancedCompositeBeanHelperTest tests pass

🤖 Generated with Claude Code

…n over parent

buildFieldCache() used put() which let parent fields overwrite child fields
with the same name, because DeclaredMembers iterates child-first then parent.
This broke Mojo parameter injection for shadowed fields (e.g. the 'skip'
field in TestResourcesMojo vs ResourcesMojo).

Switch to putIfAbsent() — matching the method cache — so the child's field
takes precedence.

Reported-in: apache/maven-resources-plugin#497

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

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Clean one-line bug fix that changes put() to putIfAbsent() in buildFieldCache(), making field cache resolution consistent with the existing method cache behavior. The fix correctly ensures child class fields take precedence over shadowed parent fields when DeclaredMembers iterates child-first.

Details:

  • The fix is verified correct: Sisu's DeclaredMembers / MemberIterator starts with the given class and walks to getSuperclass(), confirming child-first iteration order. With put(), the parent's field entry overwrites the child's; with putIfAbsent(), the child's field (seen first) is correctly preserved.
  • The method cache at line 198 already uses putIfAbsent(), so this fix resolves an asymmetry between the two caches introduced in the original d3bd71bbdf commit ("perf: optimize CompositeBeanHelper with reflection caching").
  • The test bean hierarchy (ParentBean/ChildBean both declaring private boolean skip) accurately mirrors the real-world scenario in apache/maven-resources-plugin#497 where ResourcesMojo and TestResourcesMojo both declare skip.
  • Well-tested with two new tests covering the direct fix and cache consistency.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

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