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

commit 706045e61d01971bd12736308f0e7bcbceac6852
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Nov 2 10:28:31 2026 +0100

    WW-4164 fix(core): address Sonar findings on extension support
    
    Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---
 .../org/apache/struts2/config/impl/DefaultConfiguration.java     | 6 +++---
 core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java | 2 +-
 .../struts2/dispatcher/mapper/ActionExtensionResolver.java       | 7 ++++---
 .../org/apache/struts2/dispatcher/mapper/ActionExtensions.java   | 4 ++--
 .../apache/struts2/dispatcher/mapper/DefaultActionMapper.java    | 9 +++++----
 .../apache/struts2/dispatcher/ActionExtensionEndToEndTest.java   | 4 +++-
 .../apache/struts2/dispatcher/mapper/ActionExtensionsTest.java   | 4 ++--
 7 files changed, 20 insertions(+), 16 deletions(-)

diff --git 
a/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java 
b/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
index a53170105..30dcb0968 100644
--- 
a/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
+++ 
b/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
@@ -563,9 +563,9 @@ public class DefaultConfiguration implements Configuration {
 
         warnIfHttpMethodNotEnforced(baseConfig, interceptors);
 
-        List<String> extensions = !baseConfig.getExtensions().isEmpty()
-                ? baseConfig.getExtensions()
-                : packageContext.getFullExtensions();
+        List<String> extensions = baseConfig.getExtensions().isEmpty()
+                ? packageContext.getFullExtensions()
+                : baseConfig.getExtensions();
         warnIfExtensionNotEnforced(baseConfig, extensions, interceptors);
 
         String methodRegex = container.getInstance(String.class, 
StrutsConstants.STRUTS_SMI_METHOD_REGEX);
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 331648c1c..cee6fa5c5 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -756,7 +756,7 @@ public class Dispatcher {
         if (container.getInstance(ActionMapper.class).getClass() == 
DefaultActionMapper.class) {
             return;
         }
-        boolean declared = 
configurationManager.getConfiguration().getRuntimeConfiguration().getActionConfigs().values().stream()
+        boolean declared = 
container.getInstance(Configuration.class).getRuntimeConfiguration().getActionConfigs().values().stream()
                 .flatMap(actions -> actions.values().stream())
                 .anyMatch(config -> !config.getExtensions().isEmpty());
         if (declared) {
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensionResolver.java
 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensionResolver.java
index f322b10f0..bdbafac9f 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensionResolver.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensionResolver.java
@@ -28,6 +28,7 @@ import org.apache.struts2.config.entities.ActionConfig;
 import org.apache.struts2.inject.Inject;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Chooses the extension of a generated action URL from the extensions the 
target action accepts, so links,
@@ -40,7 +41,7 @@ public class ActionExtensionResolver {
     private Configuration configuration;
     private List<String> extensions;
     private boolean allowDynamicMethodCalls;
-    private volatile DeclarationState declarationState;
+    private final AtomicReference<DeclarationState> declarationState = new 
AtomicReference<>();
 
     /**
      * @param configuration the configuration used to look up the target action
@@ -113,14 +114,14 @@ public class ActionExtensionResolver {
     }
 
     private boolean anyActionDeclaresExtensions(RuntimeConfiguration 
runtimeConfiguration) {
-        DeclarationState cached = declarationState;
+        DeclarationState cached = declarationState.get();
         if (cached != null && cached.runtimeConfiguration == 
runtimeConfiguration) {
             return cached.anyDeclared;
         }
         boolean anyDeclared = 
runtimeConfiguration.getActionConfigs().values().stream()
             .flatMap(actionConfigs -> actionConfigs.values().stream())
             .anyMatch(actionConfig -> !actionConfig.getExtensions().isEmpty());
-        declarationState = new DeclarationState(runtimeConfiguration, 
anyDeclared);
+        declarationState.set(new DeclarationState(runtimeConfiguration, 
anyDeclared));
         return anyDeclared;
     }
 
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensions.java 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensions.java
index ec90209fa..99a0f0614 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensions.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ActionExtensions.java
@@ -38,12 +38,12 @@ public final class ActionExtensions {
      * Parses a comma separated extension list; a trailing comma adds an empty 
entry.
      *
      * @param value the raw value, may be {@code null}
-     * @return an unmodifiable list in declared order, or {@code null} when 
{@code value} is {@code null}
+     * @return an unmodifiable list in declared order, or an empty list when 
{@code value} is {@code null}
      * @since 7.5.0
      */
     public static List<String> parse(String value) {
         if (value == null) {
-            return null;
+            return List.of();
         }
         if (value.isEmpty()) {
             return List.of("");
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
index c4ddf8575..c39bd8be4 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
@@ -40,6 +40,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 
 import static org.apache.commons.lang3.StringUtils.normalizeSpace;
@@ -137,7 +138,7 @@ public class DefaultActionMapper implements ActionMapper {
 
     protected List<String> extensions = List.of("action", "");
 
-    private volatile DeclaredExtensions declaredExtensions;
+    private final AtomicReference<DeclaredExtensions> declaredExtensions = new 
AtomicReference<>();
 
     protected Container container;
 
@@ -253,7 +254,7 @@ public class DefaultActionMapper implements ActionMapper {
         } else {
             this.extensions = null;
         }
-        this.declaredExtensions = null;
+        this.declaredExtensions.set(null);
     }
 
     @Override
@@ -529,7 +530,7 @@ public class DefaultActionMapper implements ActionMapper {
     }
 
     private DeclaredExtensions declaredExtensions(RuntimeConfiguration 
runtimeConfiguration) {
-        DeclaredExtensions cached = declaredExtensions;
+        DeclaredExtensions cached = declaredExtensions.get();
         if (cached != null && cached.runtimeConfiguration == 
runtimeConfiguration) {
             return cached;
         }
@@ -549,7 +550,7 @@ public class DefaultActionMapper implements ActionMapper {
             
.sorted(Comparator.comparingInt(String::length).reversed().thenComparing(Comparator.naturalOrder()))
             .toList();
         DeclaredExtensions computed = new 
DeclaredExtensions(runtimeConfiguration, declaredOnly, emptyDeclaredOnly);
-        declaredExtensions = computed;
+        declaredExtensions.set(computed);
         return computed;
     }
 
diff --git 
a/core/src/test/java/org/apache/struts2/dispatcher/ActionExtensionEndToEndTest.java
 
b/core/src/test/java/org/apache/struts2/dispatcher/ActionExtensionEndToEndTest.java
index 0086defcc..d5c64479a 100644
--- 
a/core/src/test/java/org/apache/struts2/dispatcher/ActionExtensionEndToEndTest.java
+++ 
b/core/src/test/java/org/apache/struts2/dispatcher/ActionExtensionEndToEndTest.java
@@ -27,6 +27,7 @@ import org.apache.struts2.action.Action;
 import org.apache.struts2.components.URL;
 import org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.util.StrutsTestCaseHelper;
 import org.apache.struts2.util.ValueStack;
 import org.apache.struts2.util.ValueStackFactory;
 import org.springframework.mock.web.MockFilterChain;
@@ -60,7 +61,8 @@ public class ActionExtensionEndToEndTest extends 
StrutsInternalTestCase {
 
     @Override
     protected void setUp() throws Exception {
-        PrepareOperations.clearDevModeOverride();
+        super.setUp();
+        StrutsTestCaseHelper.tearDown(dispatcher);
         Map<String, String> params = new HashMap<>();
         params.put("config", 
"struts-default.xml,org/apache/struts2/dispatcher/struts-extension-one-package.xml");
         params.put(StrutsConstants.STRUTS_ACTION_EXTENSION, "action,,");
diff --git 
a/core/src/test/java/org/apache/struts2/dispatcher/mapper/ActionExtensionsTest.java
 
b/core/src/test/java/org/apache/struts2/dispatcher/mapper/ActionExtensionsTest.java
index 3a1f46fe1..b3ec0ee3b 100644
--- 
a/core/src/test/java/org/apache/struts2/dispatcher/mapper/ActionExtensionsTest.java
+++ 
b/core/src/test/java/org/apache/struts2/dispatcher/mapper/ActionExtensionsTest.java
@@ -38,8 +38,8 @@ public class ActionExtensionsTest extends TestCase {
         assertThat(ActionExtensions.parse("")).isEqualTo(List.of(""));
     }
 
-    public void testParseNull() {
-        assertThat(ActionExtensions.parse(null)).isNull();
+    public void testParseNullIsEmpty() {
+        assertThat(ActionExtensions.parse(null)).isEmpty();
     }
 
     public void testAllowsNullAsEmpty() {

Reply via email to