Files
struts/docs/superpowers
Lukasz Lenart 12015d0bf5 WW-5540 Add caching to AbstractLocalizedTextProvider (#1808)
* WW-5540 docs: add caching design spec for AbstractLocalizedTextProvider

Design for caching the class/package hierarchy traversal result in
findText, keyed on (classloader, class name, textKey, locale). Caches
the raw resolved pattern (or a NOT_FOUND marker) only; translation and
formatting stay per-call. Wires invalidation into the existing
reloadBundles/clearBundle/clearMissingBundlesCache sites.

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

* WW-5540 docs: add implementation plan and refine spec

Add the 3-task TDD implementation plan and record the
formatWithNullDetection fall-through decision in the spec.

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

* WW-5540 docs: deprecate+delegate findMessage/getMessage in plan

Resolve pre-flight duplication/dead-code finding: old traversal helpers
delegate to the raw twins and are marked @Deprecated instead of being
duplicated. Add a direct characterization test for the findMessage delegator.

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

* WW-5540 refactor(core): split raw message resolution from formatting

Add getRawMessage/formatMessage and a raw twin findMessageRaw. Re-express
getMessage via formatMessage and make findMessage delegate to
findMessageRaw + formatMessage; deprecate both as legacy extension points
superseded by the raw-resolution path. The deprecated findMessage triggers
the bundle reload on entry, preserving the reload side effect the old
getMessage-per-probe walk provided. Groundwork for the traversal caches.

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

* WW-5540 docs: refine Task 1 plan (deprecate/delegate + reload-on-entry)

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

* WW-5540 perf(core): cache class-hierarchy text resolution

Cache the class/interface/superclass traversal in findText keyed on
(classloader, class name, key, locale), storing the raw pattern or a
NOT_FOUND marker. Formatting stays per call and falls through to the
next tier when a cached pattern formats to null. Invalidated on
reloadBundles/clearBundle/clearMissingBundlesCache; reload is hoisted
to the top of findText so caches are cleared before they are read.

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

* WW-5540 docs: draft follow-up ticket for null-control-flow cleanup

Capture the deferred result-wrapper refactor (raised during WW-5540) as a
ready-to-file Jira draft; keep WW-5540 focused on caching.

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

* WW-5540 perf(core): cache package-hierarchy text resolution

Cache the *.package traversal in findText the same way as the class
hierarchy, with the same keying, fall-through, and invalidation.

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

* WW-5540 test(core): tighten localized-text cache tests

Assert single cache entry in the per-call-format tests (proves the raw
pattern is cached, not the formatted result), and mirror the package-cache
clearBundle/clearMissingBundlesCache invalidation test.

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

* WW-5540 docs: note devMode null-valueStack eager-reload edge

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

* WW-5540 docs: link follow-up doc to filed ticket WW-5655

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

* WW-5540 chore(core): add ASF license header to CacheFixture.properties

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

* WW-5540 chore(core): add since/forRemoval to @Deprecated annotations

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

* WW-5540 docs: drop follow-up draft superseded by WW-5655

The ticket is filed; the draft's content now lives in WW-5655 itself.

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

* WW-5540 fix(core): address fresh-eyes review findings

- Document that the deprecated getMessage/findMessage are no longer
  invoked by findText, and name formatMessage as the override point
- Fall back to the ActionContext-based reloadBundles() when findText is
  called without a value stack, so the RELOADED flag is tracked and the
  caches can warm on that path in reload/devMode
- Narrow resolveClassHierarchyRaw/resolvePackageHierarchyRaw to
  package-private (the cache key omits indexedKey, which is safe only
  when derived from textKey as the internal call sites do)
- Suppress java:S2129 on the NOT_FOUND identity sentinel

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

* WW-5540 docs: strip stray NUL bytes from design spec

Two literal NUL bytes in the sentinel example made git/GitHub treat the
whole markdown file as binary and unreviewable in the PR UI; align the
example with the shipped sentinel name.

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

* WW-5540 test(core): cover ModelDriven tier, per-locale keys, indexed keys

Close the review-noted coverage gaps: the ModelDriven tier resolves via
the shared class-hierarchy cache (action miss + model hit), each locale
gets its own cache entry backed by a new _de fixture bundle, and indexed
keys (name[N] -> name[*]) resolve and cache per full textKey.

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

* WW-5540 fix(core): address Copilot review comments

- Partition the caches by System.identityHashCode of the context
  classloader so a custom ClassLoader overriding hashCode() cannot
  collide or collapse the per-loader partitions
- Derive the indexed key inside the resolvers (miss-only) instead of
  accepting it as a parameter, so the cache key trivially covers every
  input that influences the resolution result

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

* WW-5540 fix(core): resolve SonarCloud quality-gate findings

- Suppress S4973 on isNotFound: the identity comparison against the
  non-interned NOT_FOUND sentinel is the design, not a bug
- Reduce findMessageRaw cognitive complexity (S3776) by extracting
  getRawMessageWithAlternate, reused by the package walk
- Add missing @Override annotations and suppress the deliberate
  deprecated-delegator call in the test helper (S1161, S5738)

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

* WW-5540 fix(core): make findMessageRaw cycle guard effective

The `checked` set tested `contains(clazz.getName())` but never added the
class, so the diamond-interface cycle guard was a no-op (a latent issue
inherited from the original findMessage). Add the class name after the
contains-check so repeated interface branches aren't re-traversed. Behavior
is unchanged (lookups are idempotent); this only avoids redundant recursion.
The deprecated findMessage delegates here, so it's fixed too.

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

* WW-5540 docs(core): reference WW-5658 in deprecated method javadoc

Point the @deprecated javadoc of getMessage and findMessage at WW-5658,
the ticket tracking their removal in the next major release.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 09:52:08 +02:00
..