This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new c379e1f56 WW-5670 fix(core): parse struts.locale once and stop 
mislabelling the fallback locale (#1928)
c379e1f56 is described below

commit c379e1f566785faaf215c65cf0f37b5c636ba6c2
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Sep 13 11:55:27 2026 +0200

    WW-5670 fix(core): parse struts.locale once and stop mislabelling the 
fallback locale (#1928)
    
    When struts.locale was unparseable, Dispatcher.getLocale logged
    "defaulting to request locale [X]" even though, with
    struts.locale.validateRequestLocale enabled, resolveRequestLocale may
    have rejected the request locale and returned the JVM default. It also
    re-parsed the value on every request (twice on that path) although the
    result never changes after injection.
    
    Parse struts.locale once in setDefaultLocale, warn once there with the
    parse failure, and have both getLocale and resolveRequestLocale read the
    parsed value. The per-request WARN now says "defaulting to resolved
    locale [X]". No change to which locale is resolved.
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../org/apache/struts2/dispatcher/Dispatcher.java  | 42 ++++++++++-------
 .../apache/struts2/dispatcher/DispatcherTest.java  | 54 ++++++++++++++++++++++
 2 files changed, 79 insertions(+), 17 deletions(-)

diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java 
b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
index 5b7d90556..d249121f5 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -151,6 +151,11 @@ public class Dispatcher {
      */
     private String defaultLocale;
 
+    /**
+     * {@link #defaultLocale} parsed once at injection time; {@code null} when 
unset or unparseable.
+     */
+    private Locale parsedDefaultLocale;
+
     /**
      * Store state of {@link StrutsConstants#STRUTS_LOCALE_VALIDATE_REQUEST} 
setting.
      */
@@ -312,6 +317,15 @@ public class Dispatcher {
      */
     @Inject(value = StrutsConstants.STRUTS_LOCALE, required = false)
     public void setDefaultLocale(String val) {
+        Locale parsed = null;
+        if (val != null) {
+            try {
+                parsed = LocaleUtils.toLocale(val);
+            } catch (IllegalArgumentException e) {
+                LOG.warn(new ParameterizedMessage("Cannot convert 
'struts.locale' = [{}] to proper locale, the locale will be resolved per 
request instead", val), e);
+            }
+        }
+        parsedDefaultLocale = parsed;
         defaultLocale = val;
     }
 
@@ -938,20 +952,18 @@ public class Dispatcher {
     }
 
     protected Locale getLocale(HttpServletRequest request) {
+        if (parsedDefaultLocale != null) {
+            return parsedDefaultLocale;
+        }
         Locale locale;
         if (defaultLocale != null) {
             try {
-                locale = LocaleUtils.toLocale(defaultLocale);
-            } catch (IllegalArgumentException e) {
-                try {
-                    locale = resolveRequestLocale(request);
-                    LOG.warn(new ParameterizedMessage("Cannot convert 
'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
-                                    defaultLocale, locale), e);
-                } catch (RuntimeException rex) {
-                    LOG.warn(new ParameterizedMessage("Cannot convert 
'struts.locale' = [{}] to proper locale, and cannot get locale from HTTP 
Request, falling back to system default locale",
-                                    defaultLocale), rex);
-                    locale = Locale.getDefault();
-                }
+                locale = resolveRequestLocale(request);
+                LOG.warn("Cannot convert 'struts.locale' = [{}] to proper 
locale, defaulting to resolved locale [{}]", defaultLocale, locale);
+            } catch (RuntimeException rex) {
+                LOG.warn(new ParameterizedMessage("Cannot convert 
'struts.locale' = [{}] to proper locale, and cannot get locale from HTTP 
Request, falling back to system default locale",
+                                defaultLocale), rex);
+                locale = Locale.getDefault();
             }
         } else {
             try {
@@ -975,12 +987,8 @@ public class Dispatcher {
         if (!validateRequestLocale || LocaleUtils.isAvailableLocale(locale)) {
             return locale;
         }
-        if (defaultLocale != null) {
-            try {
-                return LocaleUtils.toLocale(defaultLocale);
-            } catch (IllegalArgumentException e) {
-                LOG.debug("Configured 'struts.locale' = [{}] is not parseable; 
falling back to system default", defaultLocale);
-            }
+        if (parsedDefaultLocale != null) {
+            return parsedDefaultLocale;
         }
         LOG.debug("Request locale [{}] is not available; falling back to 
system default locale", locale);
         return Locale.getDefault();
diff --git 
a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java 
b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
index ab1191cbe..970863769 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
@@ -18,6 +18,12 @@
  */
 package org.apache.struts2.dispatcher;
 
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
 import org.apache.struts2.ActionContext;
 import org.apache.struts2.text.LocalizedTextProvider;
 import org.apache.struts2.ObjectFactory;
@@ -50,8 +56,10 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -611,6 +619,52 @@ public class DispatcherTest extends 
StrutsJUnit4InternalTestCase {
                 Locale.getDefault(), dispatcher.getLocale(request));
     }
 
+    @Test
+    public void 
testGetLocale_With_BadDefaultLocale_ValidateOn_UnavailableRequestLocale() {
+        Map<String, String> params = new HashMap<>();
+        params.put(StrutsConstants.STRUTS_LOCALE, 
"This_is_not_a_valid_Locale_string");
+        params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
+        initDispatcher(params);
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getLocale()).thenReturn(new Locale("en", "US", "xzz99"));
+
+        assertEquals("Unparseable struts.locale and rejected request locale 
must fall back to system default",
+                Locale.getDefault(), dispatcher.getLocale(request));
+    }
+
+    @Test
+    public void 
testGetLocale_With_BadDefaultLocale_WarnsWithResolvedLocaleNotRequestLocale() {
+        Map<String, String> params = new HashMap<>();
+        params.put(StrutsConstants.STRUTS_LOCALE, 
"This_is_not_a_valid_Locale_string");
+        params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
+        initDispatcher(params);
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getLocale()).thenReturn(new Locale("en", "US", "xzz99"));
+
+        List<String> warnings = new ArrayList<>();
+        Logger logger = (Logger) LogManager.getLogger(Dispatcher.class);
+        AbstractAppender appender = new AbstractAppender("WW-5670", null, 
null, false, Property.EMPTY_ARRAY) {
+            @Override
+            public void append(LogEvent event) {
+                if (event.getLevel() == Level.WARN) {
+                    warnings.add(event.getMessage().getFormattedMessage());
+                }
+            }
+        };
+        appender.start();
+        logger.addAppender(appender);
+        try {
+            dispatcher.getLocale(request);
+        } finally {
+            logger.removeAppender(appender);
+            appender.stop();
+        }
+
+        assertThat(warnings).singleElement().asString()
+                .contains("defaulting to resolved locale [" + 
Locale.getDefault() + "]")
+                .doesNotContain("request locale");
+    }
+
     @Test
     public void dispatcherReinjectedAfterReload() {
         HttpServletRequest request = mock(HttpServletRequest.class);

Reply via email to