From 260551ceb83097efc7d551ea05e40ea9c637e9cc Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Mon, 12 Oct 2026 11:16:40 +0200 Subject: [PATCH] WW-5766 feat(config): map struts.scopedConstantProvider in ConstantConfig Java-based configuration can now select a custom ScopedConstantProvider through ConstantConfig instead of overriding getAllAsStringsMap(). The selection tests load providers in the Dispatcher's order rather than through StrutsDefaultConfigurationProvider: that bootstrap binds ScopedConstantProvider under the default name, so alias() skips the constant and the test could never observe the mapping. Co-Authored-By: Claude Opus 5.5 --- .../config/entities/ConstantConfig.java | 15 +++++ .../StrutsJavaConfigurationProviderTest.java | 67 +++++++++++++++++++ .../config/entities/ConstantConfigTest.java | 14 ++++ 3 files changed, 96 insertions(+) diff --git a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java index 081584c784..b8905c09fc 100644 --- a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java +++ b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.struts2.StrutsConstants; +import org.apache.struts2.config.ScopedConstantProvider; import org.apache.struts2.dispatcher.StaticContentLoader; import java.util.ArrayList; @@ -146,6 +147,7 @@ public class ConstantConfig { private String strictMethodInvocationMethodRegex; private BeanConfig textProviderFactory; private BeanConfig localizedTextProvider; + private BeanConfig scopedConstantProvider; private Boolean disallowProxyObjectAccess; private Boolean disallowProxyMemberAccess; private Integer ognlAutoGrowthCollectionLimit; @@ -282,6 +284,7 @@ public Map getAllAsStringsMap() { map.put(StrutsConstants.STRUTS_SMI_METHOD_REGEX, strictMethodInvocationMethodRegex); map.put(StrutsConstants.STRUTS_TEXT_PROVIDER_FACTORY, beanConfToString(textProviderFactory)); map.put(StrutsConstants.STRUTS_LOCALIZED_TEXT_PROVIDER, beanConfToString(localizedTextProvider)); + map.put(StrutsConstants.STRUTS_SCOPED_CONSTANT_PROVIDER, beanConfToString(scopedConstantProvider)); map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, Objects.toString(disallowProxyObjectAccess, null)); map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null)); map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null)); @@ -1372,6 +1375,18 @@ public void setLocalizedTextProvider(Class clazz) { this.localizedTextProvider = new BeanConfig(clazz, clazz.getName()); } + public BeanConfig getScopedConstantProvider() { + return scopedConstantProvider; + } + + public void setScopedConstantProvider(BeanConfig scopedConstantProvider) { + this.scopedConstantProvider = scopedConstantProvider; + } + + public void setScopedConstantProvider(Class clazz) { + this.scopedConstantProvider = new BeanConfig(clazz, clazz.getName()); + } + public Boolean getDisallowProxyObjectAccess() { return disallowProxyObjectAccess; } diff --git a/core/src/test/java/org/apache/struts2/config/StrutsJavaConfigurationProviderTest.java b/core/src/test/java/org/apache/struts2/config/StrutsJavaConfigurationProviderTest.java index 634b9acbe3..e0b7248c37 100644 --- a/core/src/test/java/org/apache/struts2/config/StrutsJavaConfigurationProviderTest.java +++ b/core/src/test/java/org/apache/struts2/config/StrutsJavaConfigurationProviderTest.java @@ -24,10 +24,12 @@ import java.util.Optional; import java.util.Set; +import org.apache.struts2.ActionContext; import org.apache.struts2.StrutsConstants; import org.apache.struts2.config.entities.BeanConfig; import org.apache.struts2.config.entities.BeanSelectionConfig; import org.apache.struts2.config.entities.ConstantConfig; +import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -40,6 +42,9 @@ import org.apache.struts2.util.location.LocatableProperties; public class StrutsJavaConfigurationProviderTest { + + private ConfigurationManager configurationManager; + @Test public void testRegister() throws Exception { final ConstantConfig constantConfig = new ConstantConfig(); @@ -152,6 +157,68 @@ public Optional beanSelection() { testBeanSelectionProvider.aliasCallCoverage(CodeCoverageTestClass1.class, builder, props, CodeCoverageTestClass1.ALIAS_KEY, Scope.THREAD); } + @Test + public void testScopedConstantProviderSelectedThroughConstantConfig() { + ConstantConfig constantConfig = new ConstantConfig(); + constantConfig.setScopedConstantProvider(new BeanConfig(CustomScopedConstantProvider.class, "custom")); + StrutsJavaConfiguration javaConfig = javaConfiguration(constantConfig, + new BeanConfig(CustomScopedConstantProvider.class, "custom", ScopedConstantProvider.class)); + + Container container = load(javaConfig).getContainer(); + + Assert.assertTrue(container.getInstance(ScopedConstantProvider.class) instanceof CustomScopedConstantProvider); + } + + @Test + public void testScopableConstantsBeanSatisfiesScopedConstantValidation() { + StrutsJavaConfiguration javaConfig = javaConfiguration(new ConstantConfig(), + new BeanConfig(SampleScopableConstants.class, "sample", ScopableConstants.class)); + + Configuration configuration = load(javaConfig, "org/apache/struts2/config/struts-scoped-constants-no-registry.xml"); + + Assert.assertEquals(Collections.singletonMap("sample.scopable", "x"), + configuration.getPackageConfig("unregistered").getScopedConstants()); + } + + @After + public void tearDown() { + if (configurationManager != null) { + configurationManager.destroyConfiguration(); + } + ActionContext.clear(); + } + + private Configuration load(StrutsJavaConfiguration javaConfig, String... xmlConfigs) { + configurationManager = new ConfigurationManager(Container.DEFAULT_NAME); + configurationManager.addContainerProvider(new DefaultPropertiesProvider()); + configurationManager.addContainerProvider(new StrutsXmlConfigurationProvider("struts-default.xml")); + for (String xmlConfig : xmlConfigs) { + configurationManager.addContainerProvider(new StrutsXmlConfigurationProvider(xmlConfig)); + } + configurationManager.addContainerProvider(new StrutsJavaConfigurationProvider(javaConfig)); + configurationManager.addContainerProvider(new StrutsBeanSelectionProvider()); + return configurationManager.getConfiguration(); + } + + private static StrutsJavaConfiguration javaConfiguration(ConstantConfig constantConfig, BeanConfig... beans) { + return new StrutsJavaConfiguration() { + @Override + public List beans() { + return Arrays.asList(beans); + } + + @Override + public List constants() { + return Collections.singletonList(constantConfig); + } + + @Override + public List unknownHandlerStack() { + return null; + } + }; + } + final class CodeCoverageTestClass1 extends Object { public static final String ALIAS_KEY = "testAliasKey"; public static final String ALIAS_VALUE = "testAliasValue"; diff --git a/core/src/test/java/org/apache/struts2/config/entities/ConstantConfigTest.java b/core/src/test/java/org/apache/struts2/config/entities/ConstantConfigTest.java index 0daf006d6a..2688b1a91c 100644 --- a/core/src/test/java/org/apache/struts2/config/entities/ConstantConfigTest.java +++ b/core/src/test/java/org/apache/struts2/config/entities/ConstantConfigTest.java @@ -21,6 +21,7 @@ import org.apache.struts2.TestBean; import org.apache.struts2.inject.Container; import org.apache.struts2.StrutsConstants; +import org.apache.struts2.config.CustomScopedConstantProvider; import org.apache.struts2.dispatcher.StaticContentLoader; import org.junit.Assert; import org.junit.Test; @@ -69,6 +70,19 @@ public void testGetAllAsStringsMap() { Assert.assertEquals(expectedLanguage, map.get(StrutsConstants.STRUTS_LOCALE)); } + @Test + public void testScopedConstantProviderToString() { + ConstantConfig constantConfig = new ConstantConfig(); + Assert.assertNull(constantConfig.getAllAsStringsMap().get(StrutsConstants.STRUTS_SCOPED_CONSTANT_PROVIDER)); + + constantConfig.setScopedConstantProvider(new BeanConfig(CustomScopedConstantProvider.class, "custom")); + Assert.assertEquals("custom", constantConfig.getAllAsStringsMap().get(StrutsConstants.STRUTS_SCOPED_CONSTANT_PROVIDER)); + + constantConfig.setScopedConstantProvider(CustomScopedConstantProvider.class); + Assert.assertEquals(CustomScopedConstantProvider.class.getName(), + constantConfig.getAllAsStringsMap().get(StrutsConstants.STRUTS_SCOPED_CONSTANT_PROVIDER)); + } + @Test public void testEmptyClassesToString() { ConstantConfig constantConfig = new ConstantConfig();