[ 
https://issues.apache.org/jira/browse/WW-5540?focusedWorklogId=1031834&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1031834
 ]

ASF GitHub Bot logged work on WW-5540:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 23/Jul/26 12:02
            Start Date: 23/Jul/26 12:02
    Worklog Time Spent: 10m 
      Work Description: Copilot commented on code in PR #1808:
URL: https://github.com/apache/struts/pull/1808#discussion_r3637916733


##########
core/src/main/java/org/apache/struts2/text/StrutsLocalizedTextProvider.java:
##########
@@ -65,6 +65,11 @@ public String findText(Class<?> startClazz, String textKey, 
Locale locale, Strin
             LOG.debug("Key is null, short-circuit to default message");
             return defaultMessage;
         }
+
+        // Trigger bundle reload (and cache invalidation) once, before any 
cached hierarchy lookup,
+        // so that in reload/devMode the hierarchy caches are cleared before 
they are read.
+        reloadBundles(valueStack != null ? valueStack.getContext() : null);

Review Comment:
   The hoisted reload call uses `valueStack != null ? valueStack.getContext() : 
null`. When callers pass a null ValueStack (e.g., via 
TextProviderSupport#getText(..., ValueStack)), this makes `reloadBundles` run 
with a null context, so the per-request `RELOADED` flag can’t be set and 
bundles/caches will be cleared on every lookup in reload mode. Use the 
ActionContext context map as a fallback when the stack is null.



##########
core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java:
##########
@@ -90,6 +93,20 @@ protected ClassLoader getCurrentThreadContextClassLoader() {
         return Thread.currentThread().getContextClassLoader();
     }
 
+    private int currentLoaderHashCode() {
+        return getCurrentThreadContextClassLoader().hashCode();
+    }

Review Comment:
   `currentLoaderHashCode()` currently uses `ClassLoader.hashCode()`, which can 
be overridden and is more collision-prone than identity. Since this value is 
used to partition caches across classloaders, prefer 
`System.identityHashCode(...)` to avoid cross-classloader cache contamination 
when a custom ClassLoader overrides `hashCode()`.



##########
core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java:
##########
@@ -582,23 +628,113 @@ protected String findMessage(Class<?> clazz, String key, 
String indexedKey, Loca
         // traverse up hierarchy
         if (clazz.isInterface()) {
             interfaces = clazz.getInterfaces();
-
             for (Class<?> anInterface : interfaces) {
-                msg = findMessage(anInterface, key, indexedKey, locale, args, 
checked, valueStack);
-
+                msg = findMessageRaw(anInterface, key, indexedKey, locale, 
checked);
                 if (msg != null) {
                     return msg;
                 }
             }
         } else {
             if (!clazz.equals(Object.class) && !clazz.isPrimitive()) {
-                return findMessage(clazz.getSuperclass(), key, indexedKey, 
locale, args, checked, valueStack);
+                return findMessageRaw(clazz.getSuperclass(), key, indexedKey, 
locale, checked);
             }
         }
 
         return null;
     }
 
+    /**
+     * Cached resolution of the class/interface/superclass hierarchy for a 
key. Returns the raw pattern
+     * found, or {@link #NOT_FOUND} when the key is absent from the entire 
hierarchy. Keyed on the
+     * context classloader hash + class name + key + locale, so no {@link 
Class} reference is retained.
+     * Uses get + putIfAbsent (never computeIfAbsent) because the 
child-property path recurses into findText.
+     */
+    protected String resolveClassHierarchyRaw(Class<?> clazz, String textKey, 
String indexedKey, Locale locale) {
+        TextCacheKey cacheKey = new TextCacheKey(currentLoaderHashCode(), 
clazz.getName(), textKey, locale);
+        String cached = classHierarchyCache.get(cacheKey);

Review Comment:
   `resolveClassHierarchyRaw`’s cache key ignores the `indexedKey` parameter 
even though the resolution logic consults both `textKey` and `indexedKey`. If 
this method is called with the same `textKey` but different `indexedKey` 
values, the cache can return an incorrect raw pattern. Include the indexed key 
in the cache key (or remove the parameter and derive it internally) to make the 
cache key match the lookup inputs.
   
   This issue also appears on line 703 of the same file.





Issue Time Tracking
-------------------

    Worklog Id:     (was: 1031834)
    Time Spent: 40m  (was: 0.5h)

> Add caching to AbstractLocalizedTextProvider
> --------------------------------------------
>
>                 Key: WW-5540
>                 URL: https://issues.apache.org/jira/browse/WW-5540
>             Project: Struts 2
>          Issue Type: Improvement
>          Components: Core
>    Affects Versions: 6.7.4, 7.0.3
>            Reporter: Kusal Kithul-Godage
>            Assignee: Lukasz Lenart
>            Priority: Minor
>             Fix For: 7.3.0
>
>          Time Spent: 40m
>  Remaining Estimate: 0h
>
> Performance enhancement



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to