From e2ec114577ec2368a03bd462cc060d30f610cb31 Mon Sep 17 00:00:00 2001 From: Jeffery Xie Date: Tue, 9 Apr 2024 15:33:10 +1000 Subject: [PATCH] WW-5408 add option to not fallback to empty namespace when unresolved --- .../opensymphony/xwork2/XWorkTestCase.java | 35 +++++++++++++++--- .../config/impl/DefaultConfiguration.java | 13 ++++--- .../org/apache/struts2/StrutsConstants.java | 2 ++ .../config/entities/ConstantConfig.java | 10 ++++++ .../xwork2/config/ConfigurationTest.java | 36 +++++++++++++++++++ .../struts2/views/jsp/ui/DebugTagTest.java | 22 +++--------- 6 files changed, 91 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java index 88790fc6f..8a4695e57 100644 --- a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java +++ b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java @@ -36,6 +36,8 @@ import org.apache.commons.lang3.ClassUtils; import java.util.Locale; import java.util.Map; +import static java.util.Collections.singletonMap; + /** * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses * the generic test setup for logic. @@ -56,9 +58,7 @@ public abstract class XWorkTestCase extends TestCase { @Override protected void setUp() throws Exception { configurationManager = XWorkTestCaseHelper.setUp(); - configuration = configurationManager.getConfiguration(); - container = configuration.getContainer(); - actionProxyFactory = container.getInstance(ActionProxyFactory.class); + reloadConfiguration(configurationManager); } @Override @@ -66,13 +66,17 @@ public abstract class XWorkTestCase extends TestCase { XWorkTestCaseHelper.tearDown(configurationManager); } - protected void loadConfigurationProviders(ConfigurationProvider... providers) { - configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers); + private void reloadConfiguration(ConfigurationManager configurationManager) { configuration = configurationManager.getConfiguration(); container = configuration.getContainer(); actionProxyFactory = container.getInstance(ActionProxyFactory.class); } + protected void loadConfigurationProviders(ConfigurationProvider... providers) { + configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers); + reloadConfiguration(configurationManager); + } + protected void loadButSet(Map properties) { loadConfigurationProviders(new StubConfigurationProvider() { @Override @@ -115,4 +119,25 @@ public abstract class XWorkTestCase extends TestCase { .getContextMap(); } + protected void setStrutsConstant(String constant, String value) { + setStrutsConstant(singletonMap(constant, value)); + } + + protected void setStrutsConstant(final Map overwritePropeties) { + configurationManager.addContainerProvider(new StubConfigurationProvider() { + @Override + public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { + for (Map.Entry stringStringEntry : overwritePropeties.entrySet()) { + props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null); + } + } + + @Override + public void destroy() { + } + }); + + configurationManager.reload(); + reloadConfiguration(configurationManager); + } } diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java index 7bf0e7c77..c1851c5ea 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java @@ -459,9 +459,12 @@ public class DefaultConfiguration implements Configuration { boolean appendNamedParameters = Boolean.parseBoolean( container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS) ); + boolean emptyNamespaceFallbackDisabled = Boolean.parseBoolean( + container.getInstance(String.class, StrutsConstants.STRUTS_DISABLE_EMPTY_NAMESPACE_FALLBACK) + ); return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs), - Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters); + Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters, emptyNamespaceFallbackDisabled); } private void setDefaultResults(Map results, PackageConfig packageContext) { @@ -536,14 +539,17 @@ public class DefaultConfiguration implements Configuration { private final Map namespaceActionConfigMatchers; private final NamespaceMatcher namespaceMatcher; private final Map namespaceConfigs; + private final boolean emptyNamespaceFallbackDisabled; public RuntimeConfigurationImpl(Map> namespaceActionConfigs, Map namespaceConfigs, PatternMatcher matcher, - boolean appendNamedParameters) + boolean appendNamedParameters, + boolean emptyNamespaceFallbackDisabled) { this.namespaceActionConfigs = namespaceActionConfigs; this.namespaceConfigs = namespaceConfigs; + this.emptyNamespaceFallbackDisabled = emptyNamespaceFallbackDisabled; this.namespaceActionConfigMatchers = new LinkedHashMap<>(); this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters); @@ -583,11 +589,10 @@ public class DefaultConfiguration implements Configuration { } // fail over to empty namespace - if (config == null && StringUtils.isNotBlank(namespace)) { + if (config == null && StringUtils.isNotBlank(namespace) && ("/".equals(namespace) || !emptyNamespaceFallbackDisabled)) { config = findActionConfigInNamespace("", name); } - return config; } diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index 3d0d1a00d..b5e19a415 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -230,6 +230,8 @@ public final class StrutsConstants { public static final String STRUTS_XWORKCONVERTER = "struts.xworkConverter"; public static final String STRUTS_ALWAYS_SELECT_FULL_NAMESPACE = "struts.mapper.alwaysSelectFullNamespace"; + /** Disable fallback to empty namespace when request namespace didn't match any in action configuration */ + public static final String STRUTS_DISABLE_EMPTY_NAMESPACE_FALLBACK = "struts.disableActionConfigFallbackToEmptyNamespace"; /** The {@link com.opensymphony.xwork2.LocaleProviderFactory} implementation class */ public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory"; 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 2b854243d..b7d462609 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 @@ -90,6 +90,7 @@ public class ConstantConfig { private Boolean freemarkerWrapperAltMap; private BeanConfig xworkConverter; private Boolean mapperAlwaysSelectFullNamespace; + private Boolean proxyDisableEmptyNamespaceFallback; private BeanConfig localeProviderFactory; private String mapperIdParameterName; private Boolean ognlAllowStaticFieldAccess; @@ -225,6 +226,7 @@ public class ConstantConfig { map.put(StrutsConstants.STRUTS_FREEMARKER_WRAPPER_ALT_MAP, Objects.toString(freemarkerWrapperAltMap, null)); map.put(StrutsConstants.STRUTS_XWORKCONVERTER, beanConfToString(xworkConverter)); map.put(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE, Objects.toString(mapperAlwaysSelectFullNamespace, null)); + map.put(StrutsConstants.STRUTS_DISABLE_EMPTY_NAMESPACE_FALLBACK, Objects.toString(proxyDisableEmptyNamespaceFallback, null)); map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER_FACTORY, beanConfToString(localeProviderFactory)); map.put(StrutsConstants.STRUTS_ID_PARAMETER_NAME, mapperIdParameterName); map.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Objects.toString(ognlAllowStaticFieldAccess, null)); @@ -812,6 +814,14 @@ public class ConstantConfig { this.mapperAlwaysSelectFullNamespace = mapperAlwaysSelectFullNamespace; } + public Boolean getProxyDisableEmptyNamespaceFallback() { + return proxyDisableEmptyNamespaceFallback; + } + + public void setProxyDisableEmptyNamespaceFallback(Boolean proxyDisableEmptyNamespaceFallback) { + this.proxyDisableEmptyNamespaceFallback = proxyDisableEmptyNamespaceFallback; + } + public BeanConfig getLocaleProviderFactory() { return localeProviderFactory; } diff --git a/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java b/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java index b52b9d48c..8b9b36618 100644 --- a/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java @@ -31,6 +31,7 @@ import com.opensymphony.xwork2.inject.ContainerBuilder; import com.opensymphony.xwork2.mock.MockInterceptor; import com.opensymphony.xwork2.test.StubConfigurationProvider; import com.opensymphony.xwork2.util.location.LocatableProperties; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.config.StrutsXmlConfigurationProvider; import org.apache.struts2.dispatcher.HttpParameters; @@ -239,6 +240,41 @@ public class ConfigurationTest extends XWorkTestCase { mockContainerProvider.verify(); } + public void testGetActionConfigFallbackToEmptyNamespaceWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsNotDisabled() { + // struts.disableEmptyNamespaceFallback default to null, so it's not disabled + RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration(); + + // check namespace that doesn't match fallback to empty namespace + ActionConfig actionConfig = configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); // fallback to empty namespace (package name is default) + assertEquals("LazyFoo", actionConfig.getName()); + + // check non-empty namespace and name in config still matches + assertNotNull(configuration.getActionConfig("includeTest", "Foo")); + + // check root namespace and name in config still matches + actionConfig = configuration.getActionConfig("/", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); + assertEquals("LazyFoo", actionConfig.getName()); + } + + public void testGetActionConfigReturnNullWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsDisabled() { + // set the struts.disableEmptyNamespaceFallback to true and reload the configuration + setStrutsConstant(StrutsConstants.STRUTS_DISABLE_EMPTY_NAMESPACE_FALLBACK, "true"); + RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration(); + + // check namespace that doesn't match NOT fallback to empty namespace and return null + assertNull(configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo")); + + // check non-empty namespace and name in config still matches + assertNotNull(configuration.getActionConfig("includeTest", "Foo")); + + // check root namespace and name in config still matches + ActionConfig actionConfig = configuration.getActionConfig("/", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); + assertEquals("LazyFoo", actionConfig.getName()); + } + public void testInitForPackageProviders() { loadConfigurationProviders(new StubConfigurationProvider() { diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java index 7f4b54581..b7db51513 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java @@ -217,23 +217,9 @@ public class DebugTagTest extends AbstractUITagTest { /** * Overwrite the Struts Constant and reload container */ - private void setStrutsConstant(final Map overwritePropeties) { - configurationManager.addContainerProvider(new StubConfigurationProvider() { - @Override - public boolean needsReload() { - return true; - } - - @Override - public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { - for (Map.Entry stringStringEntry : overwritePropeties.entrySet()) { - props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null); - } - } - }); - - configurationManager.reload(); - container = configurationManager.getConfiguration().getContainer(); + @Override + protected void setStrutsConstant(final Map overwritePropeties) { + super.setStrutsConstant(overwritePropeties); stack.getActionContext().withContainer(container); } -} \ No newline at end of file +}