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);