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

lukaszlenart pushed a commit to branch WW-5668-i18n-cache-bounds-6x
in repository https://gitbox.apache.org/repos/asf/struts.git

commit d3af2dc2f5075ac65ce7ab50d9e818405d2b1c5c
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Aug 1 09:24:01 2026 +0200

    WW-5668 Add opt-in request-locale resolution consistency to Dispatcher
    
    Adds struts.locale.validateRequestLocale (default false) so request-derived
    locales can be restricted to the JVM's available-locale set, matching what
    I18nInterceptor already applies to its own locale sources.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../java/org/apache/struts2/StrutsConstants.java   |  8 ++++
 .../org/apache/struts2/dispatcher/Dispatcher.java  | 48 +++++++++++++++++++++-
 .../org/apache/struts2/default.properties          |  3 ++
 .../apache/struts2/dispatcher/DispatcherTest.java  | 40 ++++++++++++++++++
 4 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java 
b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index 22c753bab..d6b6a5b19 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -100,6 +100,14 @@ public final class StrutsConstants {
     /** The default locale for the Struts application */
     public static final String STRUTS_LOCALE = "struts.locale";
 
+    /**
+     * When enabled, request-derived locales (from {@code Accept-Language}, 
used when {@code struts.locale} is
+     * unset) are restricted to the JVM's available-locale set; unavailable 
values fall back to the default.
+     *
+     * @since 6.11.0
+     */
+    public static final String STRUTS_LOCALE_VALIDATE_REQUEST = 
"struts.locale.validateRequestLocale";
+
     /** Whether to use a Servlet request parameter workaround necessary for 
some versions of WebLogic */
     public static final String STRUTS_DISPATCHER_PARAMETERSWORKAROUND = 
"struts.dispatcher.parametersWorkaround";
 
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 55f0228ba..4b774dedf 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -152,6 +152,11 @@ public class Dispatcher {
      */
     private String defaultLocale;
 
+    /**
+     * Store state of {@link StrutsConstants#STRUTS_LOCALE_VALIDATE_REQUEST} 
setting.
+     */
+    private boolean validateRequestLocale = false;
+
     /**
      * Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
      */
@@ -311,6 +316,18 @@ public class Dispatcher {
         defaultLocale = val;
     }
 
+    /**
+     * Modify state of {@link StrutsConstants#STRUTS_LOCALE_VALIDATE_REQUEST} 
setting.
+     *
+     * @param val New setting
+     *
+     * @since 6.11.0
+     */
+    @Inject(value = StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, required = 
false)
+    public void setValidateRequestLocale(String val) {
+        validateRequestLocale = Boolean.parseBoolean(val);
+    }
+
     /**
      * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
      *
@@ -950,7 +967,7 @@ public class Dispatcher {
                 locale = LocaleUtils.toLocale(defaultLocale);
             } catch (IllegalArgumentException e) {
                 try {
-                    locale = request.getLocale();
+                    locale = resolveRequestLocale(request);
                     LOG.warn(new ParameterizedMessage("Cannot convert 
'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
                                     defaultLocale, locale), e);
                 } catch (RuntimeException rex) {
@@ -961,7 +978,7 @@ public class Dispatcher {
             }
         } else {
             try {
-                locale = request.getLocale();
+                locale = resolveRequestLocale(request);
             } catch (RuntimeException rex) {
                 LOG.warn("Cannot get locale from HTTP Request, falling back to 
system default locale", rex);
                 locale = Locale.getDefault();
@@ -970,6 +987,33 @@ public class Dispatcher {
         return locale;
     }
 
+    /**
+     * Resolves the request locale. When {@code 
struts.locale.validateRequestLocale} is enabled and the
+     * request locale is not part of the JVM's available-locale set, falls 
back to the configured
+     * {@code struts.locale} when set and parseable, otherwise the JVM 
default. When disabled (default),
+     * returns the request locale unchanged.
+     *
+     * @param request the current request
+     * @return the locale to use for this request
+     *
+     * @since 6.11.0
+     */
+    protected Locale resolveRequestLocale(HttpServletRequest request) {
+        Locale locale = request.getLocale();
+        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);
+            }
+        }
+        LOG.debug("Request locale [{}] is not available; falling back to 
system default locale", locale);
+        return Locale.getDefault();
+    }
+
     /**
      * Return the path to save uploaded files to (this is configurable).
      *
diff --git a/core/src/main/resources/org/apache/struts2/default.properties 
b/core/src/main/resources/org/apache/struts2/default.properties
index e48bbd015..9bf2ae038 100644
--- a/core/src/main/resources/org/apache/struts2/default.properties
+++ b/core/src/main/resources/org/apache/struts2/default.properties
@@ -24,6 +24,9 @@
 
 ### This can be used to set your default locale and encoding scheme
 # struts.locale=en_US
+### When true, restrict request-derived locales (Accept-Language, used when 
struts.locale is unset) to the
+### JVM's available-locale set; unavailable values fall back to the default 
locale. Defaults to false.
+struts.locale.validateRequestLocale=false
 struts.i18n.encoding=UTF-8
 
 ### if specified, the default object factory can be overridden here
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 6cce6e601..96e6bd2d1 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
@@ -570,6 +570,46 @@ public class DispatcherTest extends 
StrutsJUnit4InternalTestCase {
         assertEquals(Locale.getDefault(), context.getLocale());  // Expect the 
system default value when Mock request access fails.
     }
 
+    @Test
+    public void testValidateRequestLocaleOffPassesThrough() {
+        initDispatcher(new HashMap<>());
+        dispatcher.setDefaultLocale(null);  // Force struts.locale unset; the 
test-config default would otherwise mask the request locale.
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        // A syntactically valid but not JVM-available locale.
+        Locale exotic = new Locale("en", "US", "xzz99");
+        when(request.getLocale()).thenReturn(exotic);
+
+        assertEquals("Default off must pass the request locale through 
unchanged",
+                exotic, dispatcher.getLocale(request));
+    }
+
+    @Test
+    public void testValidateRequestLocaleOnKeepsAvailableLocale() {
+        Map<String, String> params = new HashMap<>();
+        params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
+        initDispatcher(params);
+        dispatcher.setDefaultLocale(null);  // Force struts.locale unset; the 
test-config default would otherwise mask the request locale.
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getLocale()).thenReturn(Locale.UK);
+
+        assertEquals("Available request locale must be kept", Locale.UK, 
dispatcher.getLocale(request));
+    }
+
+    @Test
+    public void testValidateRequestLocaleOnFallsBackForUnavailableLocale() {
+        Map<String, String> params = new HashMap<>();
+        params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
+        initDispatcher(params);
+        dispatcher.setDefaultLocale(null);  // Force struts.locale unset; the 
test-config default would otherwise mask the request locale.
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        Locale exotic = new Locale("en", "US", "xzz99");
+        when(request.getLocale()).thenReturn(exotic);
+
+        // struts.locale unset in this dispatcher -> fall back to the JVM 
default.
+        assertEquals("Unavailable request locale must fall back to system 
default",
+                Locale.getDefault(), dispatcher.getLocale(request));
+    }
+
     @Test
     public void dispatcherReinjectedAfterReload() {
         HttpServletRequest request = mock(HttpServletRequest.class);

Reply via email to