+ * The only exception is the {@link MethodAccessor} for the {@link Object} type which has its own extension point.
+ *
+ * @see #setMethodAccessor(MethodAccessor)
+ * @see #registerAdditionalMethodAccessors()
+ */
@Inject
protected void setContainer(Container container) throws ClassNotFoundException {
- Set names = container.getInstanceNames(PropertyAccessor.class);
+ this.container = container;
+ registerPropertyAccessors();
+ registerNullHandlers();
+ registerAdditionalMethodAccessors();
+ }
+
+ /**
+ * Note that the default {@link MethodAccessor} for handling {@link Object} methods is registered in
+ * {@link #setMethodAccessor} and can be configured using the extension point
+ * {@link StrutsConstants#STRUTS_METHOD_ACCESSOR}.
+ */
+ protected void registerAdditionalMethodAccessors() {
+ Set names = container.getInstanceNames(MethodAccessor.class);
for (String name : names) {
- Class> cls = Class.forName(name);
- OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name));
+ Class> cls;
+ try {
+ cls = Class.forName(name);
+ if (cls.equals(Object.class)) {
+ // The Object method accessor can only be configured using the struts.methodAccessor extension point
+ continue;
+ }
+ if (cls.equals(CompoundRoot.class)) {
+ // TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
+ continue;
+ }
+ } catch (ClassNotFoundException e) {
+ // Since this interface is also used as an extension point for the Object MethodAccessor, we expect
+ // there to be beans with names that don't correspond to classes. We can safely ignore these.
+ continue;
+ }
+ MethodAccessor methodAccessor = container.getInstance(MethodAccessor.class, name);
+ OgnlRuntime.setMethodAccessor(cls, methodAccessor);
+ LOG.debug("Registered custom OGNL MethodAccessor [{}] for class [{}]", methodAccessor.getClass().getName(), cls.getName());
}
+ }
- names = container.getInstanceNames(MethodAccessor.class);
+ protected void registerNullHandlers() throws ClassNotFoundException {
+ Set names = container.getInstanceNames(NullHandler.class);
for (String name : names) {
Class> cls = Class.forName(name);
- OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
+ NullHandler nullHandler = container.getInstance(NullHandler.class, name);
+ OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(nullHandler));
+ LOG.debug("Registered custom OGNL NullHandler [{}] for class [{}]", nullHandler.getClass().getName(), cls.getName());
}
+ }
- names = container.getInstanceNames(NullHandler.class);
+ protected void registerPropertyAccessors() throws ClassNotFoundException {
+ Set names = container.getInstanceNames(PropertyAccessor.class);
for (String name : names) {
Class> cls = Class.forName(name);
- OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(container.getInstance(NullHandler.class, name)));
+ if (cls.equals(CompoundRoot.class)) {
+ // TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
+ continue;
+ }
+ PropertyAccessor propertyAccessor = container.getInstance(PropertyAccessor.class, name);
+ OgnlRuntime.setPropertyAccessor(cls, propertyAccessor);
+ LOG.debug("Registered custom OGNL PropertyAccessor [{}] for class [{}]", propertyAccessor.getClass().getName(), cls.getName());
}
- this.container = container;
}
/**
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
index f5a329459f..4af56ff9e1 100644
--- a/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
+++ b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
@@ -32,37 +32,37 @@ public interface AcceptedPatternsChecker {
* @param value to check
* @return object containing result of matched pattern and pattern itself
*/
- public IsAccepted isAccepted(String value);
+ IsAccepted isAccepted(String value);
/**
* Sets excluded patterns during runtime
*
* @param commaDelimitedPatterns comma delimited string with patterns
*/
- public void setAcceptedPatterns(String commaDelimitedPatterns);
+ void setAcceptedPatterns(String commaDelimitedPatterns);
/**
* Set excluded patterns during runtime
*
* @param patterns array of additional excluded patterns
*/
- public void setAcceptedPatterns(String[] patterns);
+ void setAcceptedPatterns(String[] patterns);
/**
* Sets excluded patterns during runtime
*
* @param patterns set of additional patterns
*/
- public void setAcceptedPatterns(Set patterns);
+ void setAcceptedPatterns(Set patterns);
/**
* Allow access list of all defined excluded patterns
*
* @return set of excluded patterns
*/
- public Set getAcceptedPatterns();
+ Set getAcceptedPatterns();
- public final static class IsAccepted {
+ final class IsAccepted {
private final boolean accepted;
private final String acceptedPattern;
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
index 6fa54d0d43..086c75d0b6 100644
--- a/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
+++ b/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
@@ -32,37 +32,37 @@ public interface ExcludedPatternsChecker {
* @param value to check
* @return object containing result of matched pattern and pattern itself
*/
- public IsExcluded isExcluded(String value);
+ IsExcluded isExcluded(String value);
/**
* Sets excluded patterns during runtime
*
* @param commaDelimitedPatterns comma delimited string with patterns
*/
- public void setExcludedPatterns(String commaDelimitedPatterns);
+ void setExcludedPatterns(String commaDelimitedPatterns);
/**
* Sets excluded patterns during runtime
*
* @param patterns array of additional excluded patterns
*/
- public void setExcludedPatterns(String[] patterns);
+ void setExcludedPatterns(String[] patterns);
/**
* Sets excluded patterns during runtime
*
* @param patterns set of additional patterns
*/
- public void setExcludedPatterns(Set patterns);
+ void setExcludedPatterns(Set patterns);
/**
* Allow access list of all defined excluded patterns
*
* @return set of excluded patterns
*/
- public Set getExcludedPatterns();
+ Set getExcludedPatterns();
- public final static class IsExcluded {
+ final class IsExcluded {
private final boolean excluded;
private final String excludedPattern;
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index f39258613e..939b3bddb0 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -223,6 +223,9 @@ public final class StrutsConstants {
/** Extension point for the Struts CompoundRootAccessor */
public static final String STRUTS_COMPOUND_ROOT_ACCESSOR = "struts.compoundRootAccessor";
+ /** Extension point for the Struts MethodAccessor */
+ public static final String STRUTS_METHOD_ACCESSOR = "struts.methodAccessor";
+
/** The name of the xwork converter implementation */
public static final String STRUTS_XWORKCONVERTER = "struts.xworkConverter";
diff --git a/core/src/main/java/org/apache/struts2/components/Radio.java b/core/src/main/java/org/apache/struts2/components/Radio.java
index 8dde28a75d..88a6afc453 100644
--- a/core/src/main/java/org/apache/struts2/components/Radio.java
+++ b/core/src/main/java/org/apache/struts2/components/Radio.java
@@ -66,10 +66,6 @@ protected String getDefaultTemplate() {
return TEMPLATE;
}
- public void evaluateExtraParams() {
- super.evaluateExtraParams();
- }
-
/**
* Radio tag requires lazy evaluation as list of tags is dynamically generated using
*
@@ -80,8 +76,4 @@ protected boolean lazyEvaluation() {
return true;
}
- protected Class> getValueClassType() {
- return String.class;
- }
-
}
diff --git a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
index 4895fc6f83..a52a67749f 100644
--- a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
+++ b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
@@ -61,6 +61,7 @@
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
+import ognl.MethodAccessor;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.components.UrlRenderer;
import org.apache.struts2.components.date.DateFormatter;
@@ -389,6 +390,7 @@ public void register(ContainerBuilder builder, LocatableProperties props) {
alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON);
alias(RootAccessor.class, StrutsConstants.STRUTS_COMPOUND_ROOT_ACCESSOR, builder, props);
+ alias(MethodAccessor.class, StrutsConstants.STRUTS_METHOD_ACCESSOR, builder, props);
alias(XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER, builder, props);
alias(CollectionConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, builder, props);
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ApplicationMap.java b/core/src/main/java/org/apache/struts2/dispatcher/ApplicationMap.java
index de912c988a..e91b6eef81 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/ApplicationMap.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/ApplicationMap.java
@@ -151,11 +151,16 @@ public Object put(final String key, final Object value) {
* @param key the attribute to remove.
* @return the entry that was just removed.
*/
- public Object remove(final String key) {
+ @Override
+ public Object remove(Object key) {
+ if (key == null) {
+ return null;
+ }
+
entries = null;
Object value = get(key);
- context.removeAttribute(key);
+ context.removeAttribute(key.toString());
return value;
}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ContainerHolder.java b/core/src/main/java/org/apache/struts2/dispatcher/ContainerHolder.java
index 9565732ace..2d1c61657e 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/ContainerHolder.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/ContainerHolder.java
@@ -23,25 +23,25 @@
/**
* Simple class to hold Container instance per thread to minimise number of attempts
* to read configuration and build each time a new configuration.
- *
+ *
* As ContainerHolder operates just per thread (which means per request) there is no need
* to check if configuration changed during the same request. If changed between requests,
* first call to store Container in ContainerHolder will be with the new configuration.
*/
class ContainerHolder {
- private static ThreadLocal instance = new ThreadLocal<>();
+ private static final ThreadLocal instance = new ThreadLocal<>();
- public static void store(Container instance) {
- ContainerHolder.instance.set(instance);
+ public static void store(Container newInstance) {
+ instance.set(newInstance);
}
public static Container get() {
- return ContainerHolder.instance.get();
+ return instance.get();
}
public static void clear() {
- ContainerHolder.instance.remove();
+ instance.remove();
}
}
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 227f31e13c..8668616c27 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java
@@ -64,6 +64,7 @@
import org.apache.struts2.config.StrutsJavaConfiguration;
import org.apache.struts2.config.StrutsJavaConfigurationProvider;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
@@ -119,6 +120,12 @@ public class Dispatcher {
*/
private static final List dispatcherListeners = new CopyOnWriteArrayList<>();
+ /**
+ * This field exists so {@link #getContainer()} can determine whether to (re-)inject this instance in the case of
+ * a {@link ConfigurationManager} reload.
+ */
+ private Container injectedContainer;
+
/**
* Store state of StrutsConstants.STRUTS_DEVMODE setting.
*/
@@ -144,11 +151,6 @@ public class Dispatcher {
*/
private String multipartSaveDir;
- /**
- * Stores the value of {@link StrutsConstants#STRUTS_MULTIPART_PARSER} setting
- */
- private String multipartHandlerName;
-
/**
* Stores the value of {@link StrutsConstants#STRUTS_MULTIPART_ENABLED}
*/
@@ -192,6 +194,11 @@ public class Dispatcher {
* Store ConfigurationManager instance, set on init.
*/
protected ConfigurationManager configurationManager;
+ private ObjectFactory objectFactory;
+ private ActionProxyFactory actionProxyFactory;
+ private LocaleProviderFactory localeProviderFactory;
+ private StaticContentLoader staticContentLoader;
+ private ActionMapper actionMapper;
/**
* Provide the dispatcher instance for the current thread.
@@ -211,6 +218,13 @@ public static void setInstance(Dispatcher instance) {
Dispatcher.instance.set(instance);
}
+ /**
+ * Removes the dispatcher instance for this thread.
+ */
+ public static void clearInstance() {
+ Dispatcher.instance.remove();
+ }
+
/**
* Add a dispatcher lifecycle listener.
*
@@ -306,9 +320,12 @@ public void setMultipartSaveDir(String val) {
multipartSaveDir = val;
}
- @Inject(StrutsConstants.STRUTS_MULTIPART_PARSER)
+ /**
+ * @deprecated since 6.4.0, no replacement.
+ */
+ @Deprecated(since = "6.4.9", forRemoval = true)
public void setMultipartHandler(String val) {
- multipartHandlerName = val;
+ // no-op
}
@Inject(value = StrutsConstants.STRUTS_MULTIPART_ENABLED, required = false)
@@ -326,11 +343,21 @@ public void setValueStackFactory(ValueStackFactory valueStackFactory) {
this.valueStackFactory = valueStackFactory;
}
+ public ValueStackFactory getValueStackFactory() {
+ return valueStackFactory;
+ }
+
@Inject(StrutsConstants.STRUTS_HANDLE_EXCEPTION)
public void setHandleException(String handleException) {
this.handleException = Boolean.parseBoolean(handleException);
}
+ @Inject(StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND)
+ public void setDispatchersParametersWorkaround(String dispatchersParametersWorkaround) {
+ this.paramsWorkaroundEnabled = Boolean.parseBoolean(dispatchersParametersWorkaround)
+ || (servletContext != null && StringUtils.contains(servletContext.getServerInfo(), "WebLogic"));
+ }
+
public boolean isHandleException() {
return handleException;
}
@@ -340,12 +367,48 @@ public void setDispatcherErrorHandler(DispatcherErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
+ @Inject
+ public void setObjectFactory(ObjectFactory objectFactory) {
+ this.objectFactory = objectFactory;
+ }
+
+ @Inject
+ public void setActionProxyFactory(ActionProxyFactory actionProxyFactory) {
+ this.actionProxyFactory = actionProxyFactory;
+ }
+
+ public ActionProxyFactory getActionProxyFactory() {
+ return actionProxyFactory;
+ }
+
+ @Inject
+ public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
+ this.localeProviderFactory = localeProviderFactory;
+ }
+
+ @Inject
+ public void setStaticContentLoader(StaticContentLoader staticContentLoader) {
+ this.staticContentLoader = staticContentLoader;
+ }
+
+ public StaticContentLoader getStaticContentLoader() {
+ return staticContentLoader;
+ }
+
+ @Inject
+ public void setActionMapper(ActionMapper actionMapper) {
+ this.actionMapper = actionMapper;
+ }
+
+ public ActionMapper getActionMapper() {
+ return actionMapper;
+ }
+
/**
* Releases all instances bound to this dispatcher instance.
*/
public void cleanup() {
// clean up ObjectFactory
- ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);
if (objectFactory == null) {
LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
}
@@ -359,7 +422,7 @@ public void cleanup() {
}
// clean up Dispatcher itself for this thread
- instance.set(null);
+ instance.remove();
servletContext.setAttribute(StrutsStatics.SERVLET_DISPATCHER, null);
// clean up DispatcherListeners
@@ -532,21 +595,6 @@ private void init_DeferredXmlConfigurations() {
loadConfigPaths("struts-deferred.xml");
}
- private Container init_PreloadConfiguration() {
- return getContainer();
- }
-
- private void init_CheckWebLogicWorkaround(Container container) {
- // test whether param-access workaround needs to be enabled
- if (servletContext != null && StringUtils.contains(servletContext.getServerInfo(), "WebLogic")) {
- LOG.info("WebLogic server detected. Enabling Struts parameter access work-around.");
- paramsWorkaroundEnabled = true;
- } else {
- paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
- StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
- }
- }
-
/**
* Load configurations, including both XML and zero-configuration strategies,
* and update optional settings, including whether to reload configurations and resource files.
@@ -567,9 +615,7 @@ public void init() {
init_AliasStandardObjects(); // [7]
init_DeferredXmlConfigurations();
- Container container = init_PreloadConfiguration();
- container.inject(this);
- init_CheckWebLogicWorkaround(container);
+ getContainer(); // Inject this instance
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
@@ -689,7 +735,6 @@ protected ActionProxy prepareActionProxy(Map extraContext, Strin
}
protected ActionProxy createActionProxy(String namespace, String name, String method, Map extraContext) {
- ActionProxyFactory actionProxyFactory = getContainer().getInstance(ActionProxyFactory.class);
return actionProxyFactory.createActionProxy(namespace, name, method, extraContext, true, false);
}
@@ -865,6 +910,7 @@ protected String getSaveDir() {
* @param response The response
*/
public void prepare(HttpServletRequest request, HttpServletResponse response) {
+ getContainer(); // Init ContainerHolder and reinject this instance IF ConfigurationManager was reloaded
String encoding = null;
if (defaultEncoding != null) {
encoding = defaultEncoding;
@@ -937,15 +983,12 @@ public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOExcep
}
if (isMultipartSupportEnabled(request) && isMultipartRequest(request)) {
- MultiPartRequest multiPartRequest = getMultiPartRequest();
- LocaleProviderFactory localeProviderFactory = getContainer().getInstance(LocaleProviderFactory.class);
-
request = new MultiPartRequestWrapper(
- multiPartRequest,
- request,
- getSaveDir(),
- localeProviderFactory.createLocaleProvider(),
- disableRequestAttributeValueStackLookup
+ getMultiPartRequest(),
+ request,
+ getSaveDir(),
+ localeProviderFactory.createLocaleProvider(),
+ disableRequestAttributeValueStackLookup
);
} else {
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
@@ -988,18 +1031,7 @@ protected boolean isMultipartRequest(HttpServletRequest request) {
* @return a multi part request object
*/
protected MultiPartRequest getMultiPartRequest() {
- MultiPartRequest mpr = null;
- //check for alternate implementations of MultiPartRequest
- Set multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
- for (String multiName : multiNames) {
- if (multiName.equals(multipartHandlerName)) {
- mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
- }
- }
- if (mpr == null) {
- mpr = getContainer().getInstance(MultiPartRequest.class);
- }
- return mpr;
+ return getContainer().getInstance(MultiPartRequest.class);
}
/**
@@ -1063,27 +1095,26 @@ public ConfigurationManager getConfigurationManager() {
}
/**
- * Expose the dependency injection container.
+ * Exposes a thread-cached reference of the dependency injection container. If the container is found to have
+ * changed since the last time it was cached, this Dispatcher instance is re-injected to ensure no stale
+ * configuration/dependencies persist.
+ *
+ * A non-cached reference can be obtained by calling {@link #getConfigurationManager()}.
*
* @return Our dependency injection container
*/
public Container getContainer() {
- if (ContainerHolder.get() != null) {
- return ContainerHolder.get();
- }
- ConfigurationManager mgr = getConfigurationManager();
- if (mgr == null) {
- throw new IllegalStateException("The configuration manager shouldn't be null");
- } else {
- Configuration config = mgr.getConfiguration();
- if (config == null) {
- throw new IllegalStateException("Unable to load configuration");
- } else {
- Container container = config.getContainer();
- ContainerHolder.store(container);
- return container;
+ if (ContainerHolder.get() == null) {
+ try {
+ ContainerHolder.store(getConfigurationManager().getConfiguration().getContainer());
+ } catch (NullPointerException e) {
+ throw new IllegalStateException("ConfigurationManager and/or Configuration should not be null", e);
}
}
+ if (injectedContainer != ContainerHolder.get()) {
+ injectedContainer = ContainerHolder.get();
+ injectedContainer.inject(this);
+ }
+ return ContainerHolder.get();
}
-
}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ExecuteOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/ExecuteOperations.java
index cf5cb18275..81418153e0 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/ExecuteOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/ExecuteOperations.java
@@ -18,8 +18,8 @@
*/
package org.apache.struts2.dispatcher;
-import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.RequestUtils;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -54,7 +54,7 @@ public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServ
resourcePath = request.getPathInfo();
}
- StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
+ StaticContentLoader staticResourceLoader = dispatcher.getStaticContentLoader();
if (staticResourceLoader.canHandle(resourcePath)) {
staticResourceLoader.findStaticResource(resourcePath, request, response);
// The framework did its job here
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
index 819e7cdb94..367aeba55c 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
@@ -57,7 +57,7 @@ public Dispatcher initDispatcher(HostConfig filterConfig) {
* @return the static content loader
*/
public StaticContentLoader initStaticContentLoader(HostConfig filterConfig, Dispatcher dispatcher) {
- StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
+ StaticContentLoader loader = dispatcher.getStaticContentLoader();
loader.setHostConfig(filterConfig);
return loader;
}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/MockDispatcher.java b/core/src/main/java/org/apache/struts2/dispatcher/MockDispatcher.java
index 1c7ad289d7..65b8128775 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/MockDispatcher.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/MockDispatcher.java
@@ -21,7 +21,6 @@
import com.opensymphony.xwork2.config.ConfigurationManager;
import jakarta.servlet.ServletContext;
-import java.util.HashMap;
import java.util.Map;
public class MockDispatcher extends Dispatcher {
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Parameter.java b/core/src/main/java/org/apache/struts2/dispatcher/Parameter.java
index edae6c3a17..06aa6783fc 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/Parameter.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/Parameter.java
@@ -18,12 +18,12 @@
*/
package org.apache.struts2.dispatcher;
-import java.util.Objects;
-
import org.apache.commons.text.StringEscapeUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.util.Objects;
+
public interface Parameter {
String getName();
@@ -58,7 +58,7 @@ public String getName() {
@Override
public String getValue() {
String[] values = toStringArray();
- return (values != null && values.length > 0) ? values[0] : null;
+ return values.length > 0 ? values[0] : null;
}
private String[] toStringArray() {
@@ -124,7 +124,7 @@ public String toString() {
class Empty implements Parameter {
- private String name;
+ private final String name;
public Empty(String name) {
this.name = name;
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
index 88352b0ad9..95e0f21989 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java
@@ -20,13 +20,11 @@
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.ValueStackFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsException;
-import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import jakarta.servlet.ServletException;
@@ -78,7 +76,7 @@ public void cleanupRequest(final HttpServletRequest request) {
dispatcher.cleanUpRequest(request);
} finally {
ActionContext.clear();
- Dispatcher.setInstance(null);
+ Dispatcher.clearInstance();
devModeOverride.remove();
}
});
@@ -101,7 +99,7 @@ public ActionContext createActionContext(HttpServletRequest request, HttpServlet
} else {
ctx = ServletActionContext.getActionContext(request); //checks if we are probably in an async
if (ctx == null) {
- ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
+ ValueStack stack = dispatcher.getValueStackFactory().createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = ActionContext.of(stack.getContext()).bind();
}
@@ -188,7 +186,7 @@ public ActionMapping findActionMapping(HttpServletRequest request, HttpServletRe
Object mappingAttr = request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mappingAttr == null || forceLookup) {
try {
- mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
+ mapping = dispatcher.getActionMapper().getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
} else {
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java b/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
index e8c1baa297..8b2c74c9aa 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/RequestMap.java
@@ -125,11 +125,16 @@ public Object put(final String key, final Object value) {
* @param key the name of the attribute to remove.
* @return the value that was removed or null if the value was not found (and hence, not removed).
*/
- public Object remove(final String key) {
+ @Override
+ public Object remove(final Object key) {
+ if (key == null) {
+ return null;
+ }
+
entries = null;
Object value = get(key);
- request.removeAttribute(key);
+ request.removeAttribute(key.toString());
return value;
}
diff --git a/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
index ac9d41708d..ecb1f7f9f1 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ActionMappingParametersInterceptor.java
@@ -76,12 +76,12 @@ public class ActionMappingParametersInterceptor extends ParametersInterceptor {
/**
* Get the parameter map from ActionMapping associated with the provided ActionContext.
*
- * @param ac The action context
+ * @param actionContext The action context
* @return the parameters from the action mapping in the context. If none found, returns an empty map.
*/
@Override
- protected HttpParameters retrieveParameters(ActionContext ac) {
- ActionMapping mapping = ac.getActionMapping();
+ protected HttpParameters retrieveParameters(ActionContext actionContext) {
+ ActionMapping mapping = actionContext.getActionMapping();
if (mapping != null) {
return HttpParameters.create(mapping.getParams()).buildNoNestedWrapping();
} else {
diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java
index 14849dc719..58ef078c6c 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java
@@ -43,7 +43,8 @@ public final class CspInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(CspInterceptor.class);
- private Boolean enforcingMode;
+ private boolean prependServletContext = true;
+ private boolean enforcingMode;
private String reportUri;
@Override
@@ -60,17 +61,22 @@ public String intercept(ActionInvocation invocation) throws Exception {
}
private void applySettings(ActionInvocation invocation, CspSettings cspSettings) {
- if (enforcingMode != null) {
- LOG.trace("Applying: {} to enforcingMode", enforcingMode);
- cspSettings.setEnforcingMode(enforcingMode);
- }
+ HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
+ HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
+
+ LOG.trace("Applying: {} to enforcingMode", enforcingMode);
+ cspSettings.setEnforcingMode(enforcingMode);
+
if (reportUri != null) {
LOG.trace("Applying: {} to reportUri", reportUri);
- cspSettings.setReportUri(reportUri);
- }
+ String finalReportUri = reportUri;
- HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
- HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
+ if (prependServletContext && (request.getContextPath() != null) && (!request.getContextPath().isEmpty())) {
+ finalReportUri = request.getContextPath() + finalReportUri;
+ }
+
+ cspSettings.setReportUri(finalReportUri);
+ }
invocation.addPreResultListener((actionInvocation, resultCode) -> {
LOG.trace("Applying CSP header: {} to the request", cspSettings);
@@ -99,8 +105,23 @@ private Optional buildUri(String reportUri) {
}
}
- public void setEnforcingMode(String value) {
- this.enforcingMode = Boolean.parseBoolean(value);
+ /**
+ * Enables enforcing mode, by default all exceptions are only reported
+ *
+ * @param enforcingMode {@code true} to enable enforcing mode, {@code false} to keep reporting mode.
+ */
+ public void setEnforcingMode(boolean enforcingMode) {
+ this.enforcingMode = enforcingMode;
+ }
+
+ /**
+ * Sets whether to prepend the servlet context path to the {@link #reportUri}.
+ *
+ * @param prependServletContext {@code true} to prepend the location with the servlet context path,
+ * {@code false} otherwise.
+ */
+ public void setPrependServletContext(boolean prependServletContext) {
+ this.prependServletContext = prependServletContext;
}
}
diff --git a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
index c55f97a6a8..efc4a7b04e 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
@@ -43,7 +43,6 @@
import org.apache.struts2.dispatcher.Parameter;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
@@ -51,6 +50,8 @@
import java.util.TreeMap;
import java.util.regex.Pattern;
+import static java.util.Collections.unmodifiableSet;
+import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
@@ -132,138 +133,157 @@ static private int countOGNLCharacters(String s) {
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
- if (!(action instanceof NoParameters)) {
- ActionContext ac = invocation.getInvocationContext();
- HttpParameters parameters = retrieveParameters(ac);
+ if (action instanceof NoParameters) {
+ return invocation.invoke();
+ }
- if (LOG.isDebugEnabled()) {
- LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
- }
+ ActionContext actionContext = invocation.getInvocationContext();
+ HttpParameters parameters = retrieveParameters(actionContext);
- if (parameters != null) {
- Map contextMap = ac.getContextMap();
- try {
- ReflectionContextState.setCreatingNullObjects(contextMap, true);
- ReflectionContextState.setDenyMethodExecution(contextMap, true);
- ReflectionContextState.setReportingConversionErrors(contextMap, true);
-
- ValueStack stack = ac.getValueStack();
- setParameters(action, stack, parameters);
- } finally {
- ReflectionContextState.setCreatingNullObjects(contextMap, false);
- ReflectionContextState.setDenyMethodExecution(contextMap, false);
- ReflectionContextState.setReportingConversionErrors(contextMap, false);
- }
- }
+ if (parameters == null) {
+ return invocation.invoke();
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
+ }
+
+ Map contextMap = actionContext.getContextMap();
+ batchApplyReflectionContextState(contextMap, true);
+ try {
+ setParameters(action, actionContext.getValueStack(), parameters);
+ } finally {
+ batchApplyReflectionContextState(contextMap, false);
}
+
return invocation.invoke();
}
/**
* Gets the parameter map to apply from wherever appropriate
*
- * @param ac The action context
+ * @param actionContext The action context
* @return The parameter map to apply
*/
- protected HttpParameters retrieveParameters(ActionContext ac) {
- return ac.getParameters();
+ protected HttpParameters retrieveParameters(ActionContext actionContext) {
+ return actionContext.getParameters();
}
/**
* Adds the parameters into context's ParameterMap
+ *
+ * In this class this is a no-op, since the parameters were fetched from the same location. In subclasses both this
+ * and {@link #retrieveParameters} should be overridden.
*
* @param ac The action context
* @param newParams The parameter map to apply
- *
- * In this class this is a no-op, since the parameters were fetched from the same location.
- * In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
- *
*/
protected void addParametersToContext(ActionContext ac, Map newParams) {
}
+ /**
+ * @deprecated since 6.4.0, use {@link #applyParameters}
+ */
+ @Deprecated
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
- HttpParameters params;
- Map acceptableParameters;
- if (ordered) {
- params = HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
- acceptableParameters = new TreeMap<>(getOrderedComparator());
- } else {
- params = HttpParameters.create().withParent(parameters).build();
- acceptableParameters = new TreeMap<>();
- }
+ applyParameters(action, stack, parameters);
+ }
- for (Map.Entry entry : params.entrySet()) {
- String parameterName = entry.getKey();
- boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
- isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
+ protected void applyParameters(final Object action, ValueStack stack, HttpParameters parameters) {
+ Map acceptableParameters = toAcceptableParameters(parameters, action);
- if (isAcceptableParameter) {
- acceptableParameters.put(parameterName, entry.getValue());
- }
+ ValueStack newStack = toNewStack(stack);
+ batchApplyReflectionContextState(newStack.getContext(), true);
+ applyMemberAccessProperties(newStack);
+
+ applyParametersOnStack(newStack, acceptableParameters, action);
+
+ if (newStack instanceof ClearableValueStack) {
+ stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
}
+ addParametersToContext(ActionContext.getContext(), acceptableParameters);
+ }
+
+ protected void batchApplyReflectionContextState(Map context, boolean value) {
+ ReflectionContextState.setCreatingNullObjects(context, value);
+ ReflectionContextState.setDenyMethodExecution(context, value);
+ ReflectionContextState.setReportingConversionErrors(context, value);
+ }
+
+ protected ValueStack toNewStack(ValueStack stack) {
ValueStack newStack = valueStackFactory.createValueStack(stack);
- boolean clearableStack = newStack instanceof ClearableValueStack;
- if (clearableStack) {
- //if the stack's context can be cleared, do that to prevent OGNL
- //from having access to objects in the stack, see XW-641
+ if (newStack instanceof ClearableValueStack) {
((ClearableValueStack) newStack).clearContextValues();
- Map context = newStack.getContext();
- ReflectionContextState.setCreatingNullObjects(context, true);
- ReflectionContextState.setDenyMethodExecution(context, true);
- ReflectionContextState.setReportingConversionErrors(context, true);
-
- //keep locale from original context
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
}
+ return newStack;
+ }
+
+ protected void applyMemberAccessProperties(ValueStack stack) {
+ if (!(stack instanceof MemberAccessValueStack)) {
+ return;
+ }
+ ((MemberAccessValueStack) stack).useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
+ ((MemberAccessValueStack) stack).useExcludeProperties(excludedPatterns.getExcludedPatterns());
+ }
+
+ protected Map toAcceptableParameters(HttpParameters parameters, Object action) {
+ HttpParameters newParams = initNewHttpParameters(parameters);
+ Map acceptableParameters = initParameterMap();
+
+ for (Map.Entry entry : newParams.entrySet()) {
+ String parameterName = entry.getKey();
+ Parameter parameterValue = entry.getValue();
+ if (isAcceptableParameter(parameterName, action) && isAcceptableParameterValue(parameterValue, action)) {
+ acceptableParameters.put(parameterName, parameterValue);
+ }
+ }
+ return acceptableParameters;
+ }
+
+ protected Map initParameterMap() {
+ if (ordered) {
+ return new TreeMap<>(getOrderedComparator());
+ } else {
+ return new TreeMap<>();
+ }
+ }
- boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
- if (memberAccessStack) {
- //block or allow access to properties
- //see WW-2761 for more details
- MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
- accessValueStack.useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
- accessValueStack.useExcludeProperties(excludedPatterns.getExcludedPatterns());
+ protected HttpParameters initNewHttpParameters(HttpParameters parameters) {
+ if (ordered) {
+ return HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
+ } else {
+ return HttpParameters.create().withParent(parameters).build();
}
+ }
- for (Map.Entry entry : acceptableParameters.entrySet()) {
- String name = entry.getKey();
- Parameter value = entry.getValue();
+ protected void applyParametersOnStack(ValueStack stack, Map parameters, Object action) {
+ for (Map.Entry entry : parameters.entrySet()) {
try {
- newStack.setParameter(name, value.getObject());
+ stack.setParameter(entry.getKey(), entry.getValue().getObject());
} catch (RuntimeException e) {
if (devMode) {
- notifyDeveloperParameterException(action, name, e.getMessage());
+ notifyDeveloperParameterException(action, entry.getKey(), e.getMessage());
}
}
}
-
- if (clearableStack) {
- stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
- }
-
- addParametersToContext(ActionContext.getContext(), acceptableParameters);
}
protected void notifyDeveloperParameterException(Object action, String property, String message) {
- String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
+ String logMsg = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
- developerNotification = tp.getText("devmode.notification",
- "Developer Notification:\n{0}",
- new String[]{developerNotification}
- );
+ logMsg = tp.getText("devmode.notification", "Developer Notification:\n{0}", new String[]{logMsg});
}
-
- LOG.error(developerNotification);
+ LOG.error(logMsg);
if (action instanceof ValidationAware) {
- // see https://issues.apache.org/jira/browse/WW-4066
- Collection messages = ((ValidationAware) action).getActionMessages();
+ ValidationAware validationAware = (ValidationAware) action;
+ Collection messages = validationAware.getActionMessages();
messages.add(message);
- ((ValidationAware) action).setActionMessages(messages);
+ validationAware.setActionMessages(messages);
}
}
@@ -275,8 +295,11 @@ protected void notifyDeveloperParameterException(Object action, String property,
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameter(String name, Object action) {
- ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
- return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
+ return acceptableName(name) && isAcceptableParameterNameAware(name, action);
+ }
+
+ protected boolean isAcceptableParameterNameAware(String name, Object action) {
+ return !(action instanceof ParameterNameAware) || ((ParameterNameAware) action).acceptableParameterName(name);
}
/**
@@ -287,13 +310,11 @@ protected boolean isAcceptableParameter(String name, Object action) {
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
- ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
- boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
- if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
- // Additional validations to process
- acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
- }
- return acceptableParamValue;
+ return isAcceptableParameterValueAware(param, action) && acceptableValue(param.getName(), param.getValue());
+ }
+
+ protected boolean isAcceptableParameterValueAware(Parameter param, Object action) {
+ return !(action instanceof ParameterValueAware) || ((ParameterValueAware) action).acceptableParameterValue(param.getValue());
}
/**
@@ -311,16 +332,16 @@ protected String getParameterLogMap(HttpParameters parameters) {
if (parameters == null) {
return "NONE";
}
+ return parameters.entrySet().stream()
+ .map(entry -> String.format("%s => %s ", entry.getKey(), entry.getValue().getValue()))
+ .collect(joining());
+ }
- StringBuilder logEntry = new StringBuilder();
- for (Map.Entry entry : parameters.entrySet()) {
- logEntry.append(entry.getKey());
- logEntry.append(" => ");
- logEntry.append(entry.getValue().getValue());
- logEntry.append(" ");
- }
-
- return logEntry.toString();
+ /**
+ * @deprecated since 6.4.0, use {@link #isAcceptableName}
+ */
+ protected boolean acceptableName(String name) {
+ return isAcceptableName(name);
}
/**
@@ -332,24 +353,30 @@ protected String getParameterLogMap(HttpParameters parameters) {
* @param name - Name to check
* @return true if accepted
*/
- protected boolean acceptableName(String name) {
+ protected boolean isAcceptableName(String name) {
if (isIgnoredDMI(name)) {
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
return false;
}
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
- if (devMode && accepted) { // notify only when in devMode
+ if (devMode && accepted) {
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
}
return accepted;
}
private boolean isIgnoredDMI(String name) {
- if (dmiEnabled) {
- return DMI_IGNORED_PATTERN.matcher(name).matches();
- } else {
+ if (!dmiEnabled) {
return false;
}
+ return DMI_IGNORED_PATTERN.matcher(name).matches();
+ }
+
+ /**
+ * @deprecated since 6.4.0, use {@link #isAcceptableValue}
+ */
+ protected boolean acceptableValue(String name, String value) {
+ return isAcceptableValue(name, value);
}
/**
@@ -362,8 +389,8 @@ private boolean isIgnoredDMI(String name) {
* @param value - value to check
* @return true if accepted
*/
- protected boolean acceptableValue(String name, String value) {
- boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
+ protected boolean isAcceptableValue(String name, String value) {
+ boolean accepted = value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value));
if (!accepted) {
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
if (devMode) {
@@ -378,7 +405,7 @@ protected boolean acceptableValue(String name, String value) {
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
- if (devMode) { // warn only when in devMode
+ if (devMode) {
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
@@ -392,22 +419,23 @@ protected boolean isWithinLengthLimit(String name) {
protected boolean isAccepted(String paramName) {
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
- if (result.isAccepted()) {
- return true;
- } else if (devMode) { // warn only when in devMode
- LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
- "https://struts.apache.org/security/#accepted--excluded-patterns",
- paramName, result.getAcceptedPattern());
- } else {
- LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
+ if (!result.isAccepted()) {
+ if (devMode) {
+ LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
+ "https://struts.apache.org/security/#accepted--excluded-patterns",
+ paramName, result.getAcceptedPattern());
+ } else {
+ LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
+ }
+ return false;
}
- return false;
+ return true;
}
protected boolean isExcluded(String paramName) {
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
if (result.isExcluded()) {
- if (devMode) { // warn only when in devMode
+ if (devMode) {
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
@@ -460,11 +488,11 @@ protected boolean isParamValueAccepted(String value) {
}
private boolean hasParamValuesToExclude() {
- return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
+ return excludedValuePatterns != null && !excludedValuePatterns.isEmpty();
}
private boolean hasParamValuesToAccept() {
- return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
+ return acceptedValuePatterns != null && !acceptedValuePatterns.isEmpty();
}
/**
@@ -530,7 +558,7 @@ public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
- acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
+ acceptedValuePatterns = unmodifiableSet(acceptedValuePatterns);
}
}
@@ -555,7 +583,7 @@ public void setExcludedValuePatterns(String commaDelimitedPatterns) {
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
- excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
+ excludedValuePatterns = unmodifiableSet(excludedValuePatterns);
}
}
}
diff --git a/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java b/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
index b7d39bfcfd..a0de7073af 100644
--- a/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
+++ b/core/src/main/java/org/apache/struts2/util/StrutsTestCaseHelper.java
@@ -28,19 +28,17 @@
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import java.util.HashMap;
import java.util.Map;
+import static java.util.Collections.emptyMap;
+
/**
- * Generic test setup methods to be used with any unit testing framework.
+ * Generic test setup methods to be used with any unit testing framework.
*/
public class StrutsTestCaseHelper {
-
- public static Dispatcher initDispatcher(ServletContext ctx, Map params) {
- if (params == null) {
- params = new HashMap<>();
- }
- Dispatcher du = new DispatcherWrapper(ctx, params);
+
+ public static Dispatcher initDispatcher(ServletContext ctx, Map params) {
+ Dispatcher du = new DispatcherWrapper(ctx, params != null ? params : emptyMap());
du.init();
Dispatcher.setInstance(du);
@@ -52,8 +50,16 @@ public static Dispatcher initDispatcher(ServletContext ctx, Map p
return du;
}
- public static void tearDown() throws Exception {
- Dispatcher.setInstance(null);
+ public static void tearDown(Dispatcher dispatcher) {
+ if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
+ dispatcher.cleanup();
+ }
+ tearDown();
+ }
+
+ public static void tearDown() {
+ (new Dispatcher(null, null)).cleanUpAfterInit(); // Clear ContainerHolder
+ Dispatcher.clearInstance();
ActionContext.clear();
}
diff --git a/core/src/main/resources/struts-beans.xml b/core/src/main/resources/struts-beans.xml
index 754bccd078..93614fa0b5 100644
--- a/core/src/main/resources/struts-beans.xml
+++ b/core/src/main/resources/struts-beans.xml
@@ -208,6 +208,11 @@
+
+
+
diff --git a/core/src/main/resources/template/simple/a-close.ftl b/core/src/main/resources/template/simple/a-close.ftl
index 4cacaf5edd..1c808e18ba 100644
--- a/core/src/main/resources/template/simple/a-close.ftl
+++ b/core/src/main/resources/template/simple/a-close.ftl
@@ -25,6 +25,9 @@
<#if parameters.href??>
href="${parameters.href?no_esc}"<#rt/>
#if>
+<#if parameters.disabled!false>
+ disabled="disabled"<#rt/>
+#if>
<#if parameters.tabindex??>
tabindex="${parameters.tabindex}"<#rt/>
#if>
diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
index ebd8096749..3bdfd67fcc 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java
@@ -90,43 +90,27 @@ public static Integer staticInteger100Method() {
@Override
public void setUp() throws Exception {
super.setUp();
- ognlUtil = container.getInstance(OgnlUtil.class);
- vs = createValueStack(true);
- }
-
- private OgnlValueStack createValueStack(boolean allowStaticFieldAccess) {
- OgnlValueStack stack = new OgnlValueStack(
- container.getInstance(XWorkConverter.class),
- (CompoundRootAccessor) container.getInstance(RootAccessor.class),
- container.getInstance(TextProvider.class, "system"), allowStaticFieldAccess);
- container.inject(stack);
- return stack;
+ refreshContainerFields();
}
- /**
- * @return current OgnlValueStackFactory instance from current container
- */
- private OgnlValueStackFactory getValueStackFactory() {
- return (OgnlValueStackFactory) container.getInstance(ValueStackFactory.class);
+ protected void refreshContainerFields() {
+ ognlUtil = container.getInstance(OgnlUtil.class);
+ vs = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack();
}
/**
- * Reloads container and gets a new OgnlValueStackFactory with specified new configuration.
+ * Reloads container and sets a new OgnlValueStackFactory with specified new configuration.
* Intended for testing OgnlValueStack instance(s) that are minimally configured.
* This should help ensure no underlying configuration/injection side-effects are responsible
* for the behaviour of fundamental access control flags).
*
* @param allowStaticField new allowStaticField configuration
- * @return a new OgnlValueStackFactory with specified new configuration
*/
- private OgnlValueStackFactory reloadValueStackFactory(boolean allowStaticField) {
- try {
- reloadTestContainerConfiguration(allowStaticField);
- } catch (Exception ex) {
- fail("Unable to reload container configuration and configure ognlValueStackFactory - exception: " + ex);
- }
-
- return getValueStackFactory();
+ private void reloadContainer(boolean allowStaticField) {
+ Map properties = new HashMap<>();
+ properties.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Boolean.toString(allowStaticField));
+ loadButSet(properties);
+ refreshContainerFields();
}
public void testExpOverridesCanStackExpUp() throws Exception {
@@ -1159,31 +1143,29 @@ public void testWarnAboutInvalidProperties() {
* when a default configuration is used.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() throws IllegalAccessException {
- OgnlValueStackFactory ognlValueStackFactory = getValueStackFactory();
- OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
Object accessedValue;
assertTrue("OgnlValueStackFactory staticFieldAccess (default flags) not true?",
- reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
+ reflectField(vs.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
// but prevent non-public field access. It should also deny static method access.
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
assertNull("able to access static method (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
assertNull("accessed final package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
assertNull("accessed package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
assertNull("accessed final protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
assertNull("accessed protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
assertNull("accessed final private field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
assertNull("accessed private field (result not null) ?", accessedValue);
}
@@ -1192,31 +1174,30 @@ public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() throws Il
* when static access flag is set to false.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticAccess() throws IllegalAccessException {
- OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(false);
- OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
+ reloadContainer(false);
Object accessedValue;
assertFalse("OgnlValueStackFactory staticFieldAccess (set false) not false?",
- reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
+ reflectField(vs.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should prevent public field access,
// and prevent non-public field access. It should also deny static method access.
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
assertNull("able to access static method (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
assertNull("able to access static final public field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
assertNull("able to access static public field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
assertNull("accessed final package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
assertNull("accessed package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
assertNull("accessed final protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
assertNull("accessed protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
assertNull("accessed final private field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
assertNull("accessed private field (result not null) ?", accessedValue);
}
@@ -1225,45 +1206,33 @@ public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticAccess() throws I
* when static access flag is set to true.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryAllStaticAccess() throws IllegalAccessException {
- OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(true);
- OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
+ reloadContainer(true);
Object accessedValue;
assertTrue("OgnlValueStackFactory staticFieldAccess (set true) not true?",
- reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
+ reflectField(vs.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
// but prevent non-public field access. It should also allow static method access.
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
assertNull("able to access static method (result non-null)!!!", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
assertNull("accessed final package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
assertNull("accessed package field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
assertNull("accessed final protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
assertNull("accessed protected field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
assertNull("accessed final private field (result not null) ?", accessedValue);
- accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
+ accessedValue = vs.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
assertNull("accessed private field (result not null) ?", accessedValue);
}
- private void reloadTestContainerConfiguration(boolean allowStaticField) throws Exception {
- loadConfigurationProviders(new StubConfigurationProvider() {
- @Override
- public void register(ContainerBuilder builder,
- LocatableProperties props) throws ConfigurationException {
- props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, String.valueOf(allowStaticField));
- }
- });
- ognlUtil = container.getInstance(OgnlUtil.class);
- }
-
static class BadJavaBean {
private int count;
private int count2;
diff --git a/core/src/test/java/org/apache/struts2/StrutsInternalTestCase.java b/core/src/test/java/org/apache/struts2/StrutsInternalTestCase.java
index 30616303fa..ade928efb0 100644
--- a/core/src/test/java/org/apache/struts2/StrutsInternalTestCase.java
+++ b/core/src/test/java/org/apache/struts2/StrutsInternalTestCase.java
@@ -18,12 +18,14 @@
*/
package org.apache.struts2;
+import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.XWorkTestCase;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.util.StrutsTestCaseHelper;
import org.apache.struts2.views.jsp.StrutsMockServletContext;
+
import java.util.HashMap;
import java.util.Map;
@@ -38,22 +40,22 @@ public abstract class StrutsInternalTestCase extends XWorkTestCase {
/**
* Sets up the configuration settings, XWork configuration, and
* message resources
- *
+ *
* @throws java.lang.Exception
*/
@Override
protected void setUp() throws Exception {
- super.setUp();
PrepareOperations.clearDevModeOverride(); // Clear DevMode override every time (consistent ThreadLocal state for tests).
initDispatcher(null);
}
-
+
protected Dispatcher initDispatcher(Map params) {
servletContext = new StrutsMockServletContext();
dispatcher = StrutsTestCaseHelper.initDispatcher(servletContext, params);
configurationManager = dispatcher.getConfigurationManager();
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
+ actionProxyFactory = container.getInstance(ActionProxyFactory.class);
container.inject(dispatcher);
return dispatcher;
}
@@ -66,31 +68,25 @@ protected Dispatcher initDispatcher(Map params) {
* @return instance of {@see Dispatcher}
*/
protected Dispatcher initDispatcherWithConfigs(String configs) {
- Map params = new HashMap();
+ Map params = new HashMap<>();
params.put("config", configs);
return initDispatcher(params);
}
@Override
protected void tearDown() throws Exception {
- super.tearDown();
- // maybe someone else already destroyed Dispatcher
- if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
- dispatcher.cleanup();
- dispatcher = null;
- }
- StrutsTestCaseHelper.tearDown();
+ StrutsTestCaseHelper.tearDown(dispatcher);
}
/**
- * Compare if two objects are considered equal according to their fields as accessed
+ * Compare if two objects are considered equal according to their fields as accessed
* via reflection.
- *
- * Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform
+ *
+ * Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform
* the check, and compares transient fields as well. This may fail when run while a security manager is
* active, due to a need to user reflection.
- *
- *
+ *
+ *
* @param obj1 the first {@link Object} to compare against the other.
* @param obj2 the second {@link Object} to compare against the other.
* @return true if the objects are equal based on field comparisons by reflection, false otherwise.
diff --git a/core/src/test/java/org/apache/struts2/StrutsJUnit4InternalTestCase.java b/core/src/test/java/org/apache/struts2/StrutsJUnit4InternalTestCase.java
new file mode 100644
index 0000000000..8d72753927
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/StrutsJUnit4InternalTestCase.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2;
+
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.XWorkJUnit4TestCase;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.util.StrutsTestCaseHelper;
+import org.apache.struts2.views.jsp.StrutsMockServletContext;
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.Map;
+
+public class StrutsJUnit4InternalTestCase extends XWorkJUnit4TestCase {
+
+ protected StrutsMockServletContext servletContext;
+ protected Dispatcher dispatcher;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ initDispatcher();
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ StrutsTestCaseHelper.tearDown(dispatcher);
+ }
+
+ protected void initDispatcher() {
+ initDispatcher(null);
+ }
+
+ protected void initDispatcher(Map params) {
+ StrutsTestCaseHelper.tearDown();
+ servletContext = new StrutsMockServletContext();
+ dispatcher = StrutsTestCaseHelper.initDispatcher(servletContext, params);
+ configurationManager = dispatcher.getConfigurationManager();
+ configuration = configurationManager.getConfiguration();
+ container = configuration.getContainer();
+ actionProxyFactory = container.getInstance(ActionProxyFactory.class);
+ }
+}
diff --git a/core/src/test/java/org/apache/struts2/TestAction.java b/core/src/test/java/org/apache/struts2/TestAction.java
index 5543b44623..77f784a618 100644
--- a/core/src/test/java/org/apache/struts2/TestAction.java
+++ b/core/src/test/java/org/apache/struts2/TestAction.java
@@ -53,6 +53,7 @@ public class TestAction extends ActionSupport {
private Long id;
private List enumList;
private List intList;
+ private Boolean someBool;
private final Map texts = new HashMap<>();
@@ -254,4 +255,12 @@ public List getIntList() {
public void setIntList(List intList) {
this.intList = intList;
}
+
+ public Boolean getSomeBool() {
+ return someBool;
+ }
+
+ public void setSomeBool(Boolean someBool) {
+ this.someBool = someBool;
+ }
}
diff --git a/core/src/test/java/org/apache/struts2/config/SettingsTest.java b/core/src/test/java/org/apache/struts2/config/SettingsTest.java
index c31a53adbd..90b824ae43 100644
--- a/core/src/test/java/org/apache/struts2/config/SettingsTest.java
+++ b/core/src/test/java/org/apache/struts2/config/SettingsTest.java
@@ -36,7 +36,7 @@ public void testSettings() {
Settings settings = new DefaultSettings();
assertEquals("12345", settings.get(StrutsConstants.STRUTS_MULTIPART_MAXSIZE));
- assertEquals("\temp", settings.get(StrutsConstants.STRUTS_MULTIPART_SAVEDIR));
+ assertEquals("\\temp", settings.get(StrutsConstants.STRUTS_MULTIPART_SAVEDIR));
assertEquals("test,org/apache/struts2/othertest", settings.get( StrutsConstants.STRUTS_CUSTOM_PROPERTIES));
assertEquals("testvalue", settings.get("testkey"));
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 03aa83e024..aabe90e703 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
@@ -18,27 +18,29 @@
*/
package org.apache.struts2.dispatcher;
-import com.mockobjects.dynamic.C;
-import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.StubValueStack;
-import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
+import com.opensymphony.xwork2.test.StubConfigurationProvider;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.StrutsJUnit4InternalTestCase;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.util.ObjectFactoryDestroyable;
+import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -46,18 +48,36 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import java.util.Collections;
+
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
/**
* Test case for Dispatcher.
*/
-public class DispatcherTest extends StrutsInternalTestCase {
+public class DispatcherTest extends StrutsJUnit4InternalTestCase {
+ @Test
public void testDefaultResourceBundlePropertyLoaded() {
LocalizedTextProvider localizedTextProvider = container.getInstance(LocalizedTextProvider.class);
@@ -71,115 +91,107 @@ public void testDefaultResourceBundlePropertyLoaded() {
"Error uploading: some error messages");
}
+ @Test
public void testPrepareSetEncodingProperly() {
HttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
- Dispatcher du = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- }});
- du.prepare(req, res);
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
+ dispatcher.prepare(req, res);
- assertEquals(req.getCharacterEncoding(), "utf-8");
- assertEquals(res.getCharacterEncoding(), "utf-8");
+ assertEquals(req.getCharacterEncoding(), UTF_8.name());
+ assertEquals(res.getCharacterEncoding(), UTF_8.name());
}
+ @Test
public void testEncodingForXMLHttpRequest() {
// given
MockHttpServletRequest req = new MockHttpServletRequest();
req.addHeader("X-Requested-With", "XMLHttpRequest");
- req.setCharacterEncoding("UTF-8");
+ req.setCharacterEncoding(UTF_8.name());
HttpServletResponse res = new MockHttpServletResponse();
- Dispatcher du = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "latin-2");
- }});
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, StandardCharsets.ISO_8859_1.name()));
// when
- du.prepare(req, res);
+ dispatcher.prepare(req, res);
// then
- assertEquals(req.getCharacterEncoding(), "UTF-8");
- assertEquals(res.getCharacterEncoding(), "UTF-8");
+ assertEquals(req.getCharacterEncoding(), UTF_8.name());
+ assertEquals(res.getCharacterEncoding(), UTF_8.name());
}
+ @Test
public void testSetEncodingIfDiffer() {
// given
- Mock mock = new Mock(HttpServletRequest.class);
- mock.expectAndReturn("getCharacterEncoding", "utf-8");
- mock.expectAndReturn("getHeader", "X-Requested-With", "");
- mock.expectAndReturn("getCharacterEncoding", "utf-8");
- HttpServletRequest req = (HttpServletRequest) mock.proxy();
+ HttpServletRequest req = mock(HttpServletRequest.class);
+ when(req.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(req.getHeader("X-Requested-With")).thenReturn("");
HttpServletResponse res = new MockHttpServletResponse();
- Dispatcher du = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- }});
-
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
// when
- du.prepare(req, res);
+ dispatcher.prepare(req, res);
// then
-
- assertEquals(req.getCharacterEncoding(), "utf-8");
- assertEquals(res.getCharacterEncoding(), "utf-8");
- mock.verify();
+ assertEquals(UTF_8.name(), req.getCharacterEncoding());
+ assertEquals(UTF_8.name(), res.getCharacterEncoding());
}
+ @Test
public void testPrepareSetEncodingPropertyWithMultipartRequest() {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
req.setContentType("multipart/form-data");
- Dispatcher du = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- }});
- du.prepare(req, res);
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
+ dispatcher.prepare(req, res);
- assertEquals("utf-8", req.getCharacterEncoding());
- assertEquals("utf-8", res.getCharacterEncoding());
+ assertEquals(UTF_8.name(), req.getCharacterEncoding());
+ assertEquals(UTF_8.name(), res.getCharacterEncoding());
}
+ @Test
public void testPrepareMultipartRequest() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=asdcvb345asd");
- Dispatcher du = initDispatcher(Collections.emptyMap());
- du.prepare(req, res);
- HttpServletRequest wrapped = du.wrapRequest(req);
- assertTrue(wrapped instanceof MultiPartRequestWrapper);
+ dispatcher.prepare(req, res);
+
+ assertTrue(dispatcher.wrapRequest(req) instanceof MultiPartRequestWrapper);
}
+ @Test
public void testPrepareMultipartRequestAllAllowedCharacters() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=01=23a.bC:D((e)d'z?p+o_r,e-");
- Dispatcher du = initDispatcher(Collections.emptyMap());
- du.prepare(req, res);
- HttpServletRequest wrapped = du.wrapRequest(req);
- assertTrue(wrapped instanceof MultiPartRequestWrapper);
+ dispatcher.prepare(req, res);
+
+ assertTrue(dispatcher.wrapRequest(req) instanceof MultiPartRequestWrapper);
}
+ @Test
public void testPrepareMultipartRequestIllegalCharacter() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=01=2;3a.bC:D((e)d'z?p+o_r,e-");
- Dispatcher du = initDispatcher(Collections.emptyMap());
- du.prepare(req, res);
- HttpServletRequest wrapped = du.wrapRequest(req);
- assertFalse(wrapped instanceof MultiPartRequestWrapper);
+ dispatcher.prepare(req, res);
+
+ assertFalse(dispatcher.wrapRequest(req) instanceof MultiPartRequestWrapper);
}
+ @Test
public void testDispatcherListener() {
final DispatcherListenerState state = new DispatcherListenerState();
@@ -198,39 +210,32 @@ public void dispatcherInitialized(Dispatcher du) {
assertFalse(state.isDestroyed);
assertFalse(state.isInitialized);
- Dispatcher du = initDispatcher(new HashMap<>());
+ dispatcher.init();
assertTrue(state.isInitialized);
- du.cleanup();
+ dispatcher.cleanup();
assertTrue(state.isDestroyed);
}
+ @Test
public void testConfigurationManager() {
- Dispatcher du;
- final InternalConfigurationManager configurationManager = new InternalConfigurationManager(Container.DEFAULT_NAME);
- try {
- du = new MockDispatcher(new MockServletContext(), new HashMap<>(), configurationManager);
- du.init();
- Dispatcher.setInstance(du);
+ configurationManager = spy(new ConfigurationManager(Container.DEFAULT_NAME));
+ dispatcher = spyDispatcherWithConfigurationManager(new Dispatcher(new MockServletContext(), emptyMap()), configurationManager);
- assertFalse(configurationManager.destroyConfiguration);
+ dispatcher.init();
- du.cleanup();
+ verify(configurationManager, never()).destroyConfiguration();
- assertTrue(configurationManager.destroyConfiguration);
+ dispatcher.cleanup();
- } finally {
- Dispatcher.setInstance(null);
- }
+ verify(configurationManager).destroyConfiguration();
}
+ @Test
public void testInitLoadsDefaultConfig() {
- Dispatcher du = new Dispatcher(new MockServletContext(), new HashMap<>());
- du.init();
- Configuration config = du.getConfigurationManager().getConfiguration();
- assertNotNull(config);
+ assertNotNull(configuration);
Set expected = new HashSet<>();
expected.add("struts-default.xml");
expected.add("struts-beans.xml");
@@ -238,135 +243,109 @@ public void testInitLoadsDefaultConfig() {
expected.add("struts-plugin.xml");
expected.add("struts.xml");
expected.add("struts-deferred.xml");
- assertEquals(expected, config.getLoadedFileNames());
- assertTrue(config.getPackageConfigs().size() > 0);
- PackageConfig packageConfig = config.getPackageConfig("struts-default");
- assertTrue(packageConfig.getInterceptorConfigs().size() > 0);
- assertTrue(packageConfig.getResultTypeConfigs().size() > 0);
+ assertEquals(expected, configuration.getLoadedFileNames());
+ assertFalse(configuration.getPackageConfigs().isEmpty());
+ PackageConfig packageConfig = configuration.getPackageConfig("struts-default");
+ assertFalse(packageConfig.getInterceptorConfigs().isEmpty());
+ assertFalse(packageConfig.getResultTypeConfigs().isEmpty());
}
+ @Test
public void testObjectFactoryDestroy() {
+ InnerDestroyableObjectFactory destroyedObjectFactory = new InnerDestroyableObjectFactory();
+ dispatcher.setObjectFactory(destroyedObjectFactory);
- ConfigurationManager cm = new ConfigurationManager(Container.DEFAULT_NAME);
- Dispatcher du = new MockDispatcher(new MockServletContext(), new HashMap<>(), cm);
- Mock mockConfiguration = new Mock(Configuration.class);
- cm.setConfiguration((Configuration) mockConfiguration.proxy());
-
- Mock mockContainer = new Mock(Container.class);
- final InnerDestroyableObjectFactory destroyedObjectFactory = new InnerDestroyableObjectFactory();
- destroyedObjectFactory.setContainer((Container) mockContainer.proxy());
- mockContainer.expectAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), destroyedObjectFactory);
-
- mockConfiguration.expectAndReturn("getContainer", mockContainer.proxy());
- mockConfiguration.expect("destroy");
- mockConfiguration.matchAndReturn("getPackageConfigs", new HashMap());
-
- du.init();
assertFalse(destroyedObjectFactory.destroyed);
- du.cleanup();
+ dispatcher.cleanup();
assertTrue(destroyedObjectFactory.destroyed);
- mockConfiguration.verify();
- mockContainer.verify();
}
+ @Test
public void testInterceptorDestroy() {
- Mock mockInterceptor = new Mock(Interceptor.class);
- mockInterceptor.matchAndReturn("hashCode", 0);
- mockInterceptor.expect("destroy");
-
- InterceptorMapping interceptorMapping = new InterceptorMapping("test", (Interceptor) mockInterceptor.proxy());
-
+ Interceptor mockedInterceptor = mock(Interceptor.class);
+ InterceptorMapping interceptorMapping = new InterceptorMapping("test", mockedInterceptor);
InterceptorStackConfig isc = new InterceptorStackConfig.Builder("test").addInterceptor(interceptorMapping).build();
-
PackageConfig packageConfig = new PackageConfig.Builder("test").addInterceptorStackConfig(isc).build();
- Map packageConfigs = new HashMap<>();
- packageConfigs.put("test", packageConfig);
+ configurationManager = spy(new ConfigurationManager(Container.DEFAULT_NAME));
+ dispatcher = spyDispatcherWithConfigurationManager(new Dispatcher(new MockServletContext(), emptyMap()), configurationManager);
- Mock mockContainer = new Mock(Container.class);
- mockContainer.matchAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), new ObjectFactory());
-
- Mock mockConfiguration = new Mock(Configuration.class);
- mockConfiguration.matchAndReturn("getPackageConfigs", packageConfigs);
- mockConfiguration.matchAndReturn("getContainer", mockContainer.proxy());
- mockConfiguration.expect("destroy");
+ dispatcher.init();
- ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
- configurationManager.setConfiguration((Configuration) mockConfiguration.proxy());
+ configuration = spy(configurationManager.getConfiguration());
+ configurationManager.setConfiguration(configuration);
+ when(configuration.getPackageConfigs()).thenReturn(singletonMap("test", packageConfig));
- Dispatcher dispatcher = new MockDispatcher(new MockServletContext(), new HashMap<>(), configurationManager);
- dispatcher.init();
dispatcher.cleanup();
- mockInterceptor.verify();
- mockContainer.verify();
- mockConfiguration.verify();
+ verify(mockedInterceptor).destroy();
+ verify(configuration).destroy();
}
+ @Test
public void testMultipartSupportEnabledByDefault() {
HttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
- Dispatcher du = initDispatcher(Collections.emptyMap());
- du.prepare(req, res);
+ dispatcher.prepare(req, res);
- assertTrue(du.isMultipartSupportEnabled(req));
+ assertTrue(dispatcher.isMultipartSupportEnabled(req));
}
+ @Test
public void testIsMultipartRequest() {
MockHttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
req.setMethod("POST");
- Dispatcher du = initDispatcher(Collections.emptyMap());
- du.prepare(req, res);
+
+ dispatcher.prepare(req, res);
req.setContentType("multipart/form-data");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=UTF-8");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=ISO-8859-1");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=Windows-1250");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=US-ASCII");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263; charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263 ;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263 ; charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data ;boundary=---------------------------207103069210263;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("Multipart/Form-Data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
- assertTrue(du.isMultipartRequest(req));
+ assertTrue(dispatcher.isMultipartRequest(req));
}
+ @Test
public void testServiceActionResumePreviousProxy() throws Exception {
- Dispatcher du = initDispatcher(Collections.emptyMap());
-
MockActionInvocation mai = new MockActionInvocation();
ActionContext.getContext().withActionInvocation(mai);
@@ -381,17 +360,15 @@ public void testServiceActionResumePreviousProxy() throws Exception {
assertFalse(actionProxy.isExecutedCalled());
- du.setDevMode("false");
- du.setHandleException("false");
- du.serviceAction(req, null, new ActionMapping());
+ dispatcher.setDevMode("false");
+ dispatcher.setHandleException("false");
+ dispatcher.serviceAction(req, null, new ActionMapping());
assertTrue("should execute previous proxy", actionProxy.isExecutedCalled());
}
+ @Test
public void testServiceActionCreatesNewProxyIfDifferentMapping() throws Exception {
- Dispatcher du = initDispatcher(Collections.emptyMap());
- container.inject(du);
-
MockActionInvocation mai = new MockActionInvocation();
ActionContext.getContext().withActionInvocation(mai);
@@ -412,7 +389,7 @@ public void testServiceActionCreatesNewProxyIfDifferentMapping() throws Exceptio
ActionMapping newActionMapping = new ActionMapping();
newActionMapping.setName("hello");
- du.serviceAction(request, response, newActionMapping);
+ dispatcher.serviceAction(request, response, newActionMapping);
assertFalse(previousActionProxy.isExecutedCalled());
}
@@ -421,196 +398,201 @@ public void testServiceActionCreatesNewProxyIfDifferentMapping() throws Exceptio
* Verify proper default (true) handleExceptionState for Dispatcher and that
* it properly reflects a manually configured change to false.
*/
+ @Test
public void testHandleException() {
- Dispatcher du = initDispatcher(new HashMap<>());
- assertTrue("Default Dispatcher handleException state not true ?", du.isHandleException());
+ assertTrue("Default Dispatcher handleException state not true ?", dispatcher.isHandleException());
- Dispatcher du2 = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_HANDLE_EXCEPTION, "false");
- }});
- assertFalse("Modified Dispatcher handleException state not false ?", du2.isHandleException());
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_HANDLE_EXCEPTION, "false"));
+ assertFalse("Modified Dispatcher handleException state not false ?", dispatcher.isHandleException());
}
/**
* Verify proper default (false) devMode for Dispatcher and that
* it properly reflects a manually configured change to true.
*/
+ @Test
public void testDevMode() {
- Dispatcher du = initDispatcher(new HashMap<>());
- assertFalse("Default Dispatcher devMode state not false ?", du.isDevMode());
+ assertFalse("Default Dispatcher devMode state not false ?", dispatcher.isDevMode());
- Dispatcher du2 = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_DEVMODE, "true");
- }});
- assertTrue("Modified Dispatcher devMode state not true ?", du2.isDevMode());
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_DEVMODE, "true"));
+ assertTrue("Modified Dispatcher devMode state not true ?", dispatcher.isDevMode());
}
+ @Test
public void testGetLocale_With_DefaultLocale_FromConfiguration() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- // Not setting a Struts Locale here, so we should receive the default "de_DE" from the test configuration.
- }});
+ // Not setting a Struts Locale here, so we should receive the default "de_DE" from the test configuration.
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.GERMANY, context.getLocale()); // Expect the Dispatcher defaultLocale value "de_DE" from the test configuration.
- mock.verify();
}
+ @Test
public void testGetLocale_With_DefaultLocale_fr_CA() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
+ initDispatcher(new HashMap() {{
+ put(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name());
put(StrutsConstants.STRUTS_LOCALE, Locale.CANADA_FRENCH.toString()); // Set the Dispatcher defaultLocale to fr_CA.
}});
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.CANADA_FRENCH, context.getLocale()); // Expect the Dispatcher defaultLocale value.
- mock.verify();
}
+ @Test
public void testGetLocale_With_BadDefaultLocale_RequestLocale_en_UK() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- mock.expectAndReturn("getLocale", Locale.UK); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
+ when(request.getLocale()).thenReturn(Locale.UK);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
+ initDispatcher(new HashMap() {{
+ put(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name());
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set Dispatcher defaultLocale to an invalid value.
}});
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.UK, context.getLocale()); // Expect the request set value from Mock.
- mock.verify();
}
+ @Test
public void testGetLocale_With_BadDefaultLocale_And_RuntimeException() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- mock.expectAndThrow("getLocale", new IllegalStateException("Test theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
+ when(request.getLocale()).thenReturn(Locale.UK);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
+ initDispatcher(new HashMap() {{
+ put(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name());
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set the Dispatcher defaultLocale to an invalid value.
}});
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ when(request.getLocale()).thenThrow(new IllegalStateException("Test theoretical state preventing HTTP Request Locale access"));
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.getDefault(), context.getLocale()); // Expect the system default value, when BOTH Dispatcher default Locale AND request access fail.
- mock.verify();
}
+ @Test
public void testGetLocale_With_NullDefaultLocale() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
+ when(request.getLocale()).thenReturn(Locale.CANADA_FRENCH);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- // Attempting to set StrutsConstants.STRUTS_LOCALE to null here via parameters causes an NPE.
- }});
+ // Attempting to set StrutsConstants.STRUTS_LOCALE to null here via parameters causes an NPE.
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
- testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
+ dispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.CANADA_FRENCH, context.getLocale()); // Expect the request set value from Mock.
- mock.verify();
}
+ @Test
public void testGetLocale_With_NullDefaultLocale_And_RuntimeException() {
// Given
- Mock mock = new Mock(HttpServletRequest.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
- mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
- mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
- mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
- mock.expectAndReturn("getParameterMap", new HashMap()); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
- mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
- mock.expectAndThrow("getLocale", new IllegalStateException("Test some theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
- HttpServletRequest request = (HttpServletRequest) mock.proxy();
+ when(request.getCharacterEncoding()).thenReturn(UTF_8.name());
+ when(request.getHeader("X-Requested-With")).thenReturn("");
+ when(request.getParameterMap()).thenReturn(emptyMap());
+ when(request.getSession(anyBoolean())).thenReturn(mockHttpSession);
+ when(request.getLocale()).thenReturn(Locale.CANADA_FRENCH);
HttpServletResponse response = new MockHttpServletResponse();
- Dispatcher testDispatcher = initDispatcher(new HashMap() {{
- put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
- // Attempting to set StrutsConstants.STRUTS_LOCALE to null via parameters causes an NPE.
- }});
+ // Attempting to set StrutsConstants.STRUTS_LOCALE to null via parameters causes an NPE.
+ initDispatcher(singletonMap(StrutsConstants.STRUTS_I18N_ENCODING, UTF_8.name()));
- testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
+ dispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
// When
- testDispatcher.prepare(request, response);
- ActionContext context = ActionContext.of(createTestContextMap(testDispatcher, request, response));
+ dispatcher.prepare(request, response);
+ when(request.getLocale()).thenThrow(new IllegalStateException("Test theoretical state preventing HTTP Request Locale access"));
+ ActionContext context = ActionContext.of(createTestContextMap(dispatcher, request, response));
// Then
assertEquals(Locale.getDefault(), context.getLocale()); // Expect the system default value when Mock request access fails.
- mock.verify();
+ }
+
+ @Test
+ public void dispatcherReinjectedAfterReload() {
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ dispatcher.prepare(request, response);
+
+ assertEquals(Locale.GERMANY, dispatcher.getLocale(request));
+
+ configurationManager.addContainerProvider(new StubConfigurationProvider() {
+ @Override
+ public void register(ContainerBuilder builder,
+ LocatableProperties props) throws ConfigurationException {
+ props.setProperty(StrutsConstants.STRUTS_LOCALE, "fr_CA");
+ }
+ });
+ configurationManager.reload();
+ dispatcher.cleanUpRequest(request);
+ dispatcher.prepare(request, response);
+
+ assertEquals(Locale.CANADA_FRENCH, dispatcher.getLocale(request));
+ }
+
+ public static Dispatcher spyDispatcherWithConfigurationManager(Dispatcher dispatcher, ConfigurationManager configurationManager) {
+ Dispatcher spiedDispatcher = spy(dispatcher);
+ doReturn(configurationManager).when(spiedDispatcher).createConfigurationManager(any());
+ return spiedDispatcher;
}
/**
@@ -641,21 +623,6 @@ protected static Map createTestContextMap(Dispatcher dispatcher,
response);
}
- static class InternalConfigurationManager extends ConfigurationManager {
- public boolean destroyConfiguration = false;
-
- public InternalConfigurationManager(String name) {
- super(name);
- }
-
- @Override
- public synchronized void destroyConfiguration() {
- super.destroyConfiguration();
- destroyConfiguration = true;
- }
- }
-
-
static class DispatcherListenerState {
public boolean isInitialized = false;
public boolean isDestroyed = false;
diff --git a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java
index c382a61048..c711eccd75 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java
@@ -22,6 +22,7 @@
import com.opensymphony.xwork2.mock.MockActionInvocation;
import org.apache.logging.log4j.util.Strings;
import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.TestAction;
import org.apache.struts2.action.CspSettingsAware;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.interceptor.csp.CspInterceptor;
@@ -45,7 +46,7 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
public void test_whenRequestReceived_thenNonceIsSetInSession_andCspHeaderContainsIt() throws Exception {
String reportUri = "/barfoo";
- String reporting = "false";
+ boolean reporting = false;
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(reporting);
@@ -58,7 +59,7 @@ public void test_whenRequestReceived_thenNonceIsSetInSession_andCspHeaderContain
public void test_whenNonceAlreadySetInSession_andRequestReceived_thenNewNonceIsSet() throws Exception {
String reportUri = "https://www.google.com/";
- String enforcingMode = "true";
+ boolean enforcingMode = true;
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.setAttribute("nonce", "foo");
@@ -73,7 +74,7 @@ public void test_whenNonceAlreadySetInSession_andRequestReceived_thenNewNonceIsS
public void testEnforcingCspHeadersSet() throws Exception {
String reportUri = "/csp-reports";
- String enforcingMode = "true";
+ boolean enforcingMode = true;
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.setAttribute("nonce", "foo");
@@ -88,7 +89,7 @@ public void testEnforcingCspHeadersSet() throws Exception {
public void testReportingCspHeadersSet() throws Exception {
String reportUri = "/csp-reports";
- String enforcingMode = "false";
+ boolean enforcingMode = false;
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.setAttribute("nonce", "foo");
@@ -101,7 +102,7 @@ public void testReportingCspHeadersSet() throws Exception {
}
public void test_uriSetOnlyWhenSetIsCalled() throws Exception {
- String enforcingMode = "false";
+ boolean enforcingMode = false;
interceptor.setEnforcingMode(enforcingMode);
interceptor.intercept(mai);
@@ -115,7 +116,7 @@ public void test_uriSetOnlyWhenSetIsCalled() throws Exception {
}
public void testCannotParseUri() {
- String enforcingMode = "false";
+ boolean enforcingMode = false;
interceptor.setEnforcingMode(enforcingMode);
try {
@@ -127,7 +128,7 @@ public void testCannotParseUri() {
}
public void testCannotParseRelativeUri() {
- String enforcingMode = "false";
+ boolean enforcingMode = false;
interceptor.setEnforcingMode(enforcingMode);
try {
@@ -139,13 +140,41 @@ public void testCannotParseRelativeUri() {
}
public void testCustomPreResultListener() throws Exception {
+ boolean enforcingMode = false;
mai.setAction(new CustomerCspAction("/report-uri"));
- interceptor.setEnforcingMode("false");
+ interceptor.setEnforcingMode(enforcingMode);
+ interceptor.intercept(mai);
+ checkHeader("/report-uri", enforcingMode);
+ }
+
+ public void testPrependContext() throws Exception {
+ boolean enforcingMode = true;
+ mai.setAction(new TestAction());
+ request.setContextPath("/app");
+
+ interceptor.setEnforcingMode(enforcingMode);
+ interceptor.setReportUri("/report-uri");
+
interceptor.intercept(mai);
- checkHeader("/report-uri", "false");
+
+ checkHeader("/app/report-uri", enforcingMode);
+ }
+
+ public void testNoPrependContext() throws Exception {
+ boolean enforcingMode = true;
+ mai.setAction(new TestAction());
+ request.setContextPath("/app");
+
+ interceptor.setEnforcingMode(enforcingMode);
+ interceptor.setReportUri("/report-uri");
+ interceptor.setPrependServletContext(false);
+
+ interceptor.intercept(mai);
+
+ checkHeader("/report-uri", enforcingMode);
}
- public void checkHeader(String reportUri, String enforcingMode) {
+ public void checkHeader(String reportUri, boolean enforcingMode) {
String expectedCspHeader;
if (Strings.isEmpty(reportUri)) {
expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ",
@@ -163,7 +192,7 @@ public void checkHeader(String reportUri, String enforcingMode) {
}
String header;
- if (enforcingMode.equals("true")) {
+ if (enforcingMode) {
header = response.getHeader(CspSettings.CSP_ENFORCE_HEADER);
} else {
header = response.getHeader(CspSettings.CSP_REPORT_HEADER);
diff --git a/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java b/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java
index 401946e2d8..19faf41ea6 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java
@@ -113,7 +113,7 @@ public void testMultipleProcesses() throws InterruptedException {
executor.execute(bp);
}
- Thread.sleep(500);
+ Thread.sleep(800);
for (BackgroundProcess bp : bps) {
assertTrue("Process is still active: " + bp, bp.isDone());
diff --git a/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
index 23057fbcfb..a142014b3e 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
@@ -1052,7 +1052,7 @@ public boolean hasActionErrors() {
}
public boolean hasActionMessages() {
- return messages.size() > 0;
+ return !messages.isEmpty();
}
public boolean hasErrors() {
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
index 4d3899b610..2cf04791dd 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
@@ -34,7 +34,6 @@
import org.apache.struts2.dispatcher.ApplicationMap;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.HttpParameters;
-import org.apache.struts2.dispatcher.MockDispatcher;
import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.dispatcher.SessionMap;
@@ -42,9 +41,10 @@
import jakarta.servlet.jsp.JspWriter;
import java.io.File;
import java.io.StringWriter;
-import java.util.HashMap;
import java.util.Map;
+import static java.util.Collections.emptyMap;
+
/**
* Base class to extend for unit testing UI Tags.
*/
@@ -108,7 +108,7 @@ protected void createMocks() {
pageContext.setJspWriter(jspWriter);
mockContainer = new Mock(Container.class);
- MockDispatcher du = new MockDispatcher(pageContext.getServletContext(), new HashMap<>(), configurationManager);
+ Dispatcher du = new Dispatcher(pageContext.getServletContext(), emptyMap());
du.init();
Dispatcher.setInstance(du);
session = new SessionMap(request);
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
index 7c3f80d715..9e45ed7a75 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
@@ -276,6 +276,23 @@ public void testSimpleWithBodyNotHTMLEscaped() throws Exception {
verifyResource("href-5.txt");
}
+ public void testSimpleDisabled() throws Exception {
+ createAction();
+
+ AnchorTag tag = createTag();
+ tag.setHref("a");
+ tag.setDisabled("true");
+
+ StrutsBodyContent body = new StrutsBodyContent(null);
+ body.print("should have disabled attribute");
+ tag.setBodyContent(body);
+
+ tag.doStartTag();
+ tag.doEndTag();
+
+ verifyResource("href-6.txt");
+ }
+
public void testInjectEscapeHtmlBodyFlag() throws Exception {
// given
initDispatcherWithConfigs("struts-default.xml, struts-escape-body.xml");
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
index 72e4cbf06c..47a5caad7b 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
@@ -64,6 +64,37 @@ public void testMapWithBooleanAsKey() throws Exception {
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
+ public void testMapWithBooleanAsKeyWithoutForceValue() throws Exception {
+ TestAction testAction = (TestAction) action;
+
+ Map map = new LinkedHashMap<>();
+ map.put(Boolean.TRUE, "male");
+ map.put(Boolean.FALSE, "female");
+ testAction.setMap(map);
+
+ testAction.setSomeBool(false);
+
+ RadioTag tag = new RadioTag();
+ tag.setPageContext(pageContext);
+ tag.setLabel("mylabel");
+ tag.setName("myname");
+ tag.setValue("someBool");
+ tag.setList("map");
+ tag.setTheme("simple");
+
+ tag.doStartTag();
+ tag.doEndTag();
+
+ verify(RadioTag.class.getResource("Radio-11.txt"));
+
+ // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+ RadioTag freshTag = new RadioTag();
+ freshTag.setPageContext(pageContext);
+ assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ }
+
public void testMapWithBooleanAsKey_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
@@ -165,12 +196,13 @@ public void testMapCheckedUsingEnum() throws Exception {
List enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
testAction.setEnumList(enumList);
+ testAction.setStatus(SomeEnum.INIT);
RadioTag tag = new RadioTag();
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
- tag.setValue("INIT");
+ tag.setValue("status");
tag.setList("enumList");
tag.doStartTag();
@@ -191,13 +223,14 @@ public void testMapCheckedUsingEnum_clearTagStateSet() throws Exception {
List enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
testAction.setEnumList(enumList);
+ testAction.setStatus(SomeEnum.INIT);
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
- tag.setValue("INIT");
+ tag.setValue("status");
tag.setList("enumList");
tag.doStartTag();
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-11.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-11.txt
new file mode 100644
index 0000000000..8f591387b9
--- /dev/null
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-11.txt
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/href-6.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/href-6.txt
new file mode 100644
index 0000000000..1e75ca9aac
--- /dev/null
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/href-6.txt
@@ -0,0 +1 @@
+should have disabled attribute
\ No newline at end of file
diff --git a/core/src/test/resources/struts.properties b/core/src/test/resources/struts.properties
index baf7481564..7d905d214a 100644
--- a/core/src/test/resources/struts.properties
+++ b/core/src/test/resources/struts.properties
@@ -24,7 +24,7 @@
struts.i18n.encoding=ISO-8859-1
struts.locale=de_DE
-struts.multipart.saveDir=\temp
+struts.multipart.saveDir=\\temp
struts.multipart.maxSize=12345
### Load custom property files (does not override struts.properties!)
diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsJUnit4TestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsJUnit4TestCase.java
index ef2afef77c..75e061c840 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsJUnit4TestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsJUnit4TestCase.java
@@ -203,9 +203,9 @@ public void finishExecution() {
* Sets up the configuration settings, XWork configuration, and
* message resources
*/
+ @Override
@Before
public void setUp() throws Exception {
- super.setUp();
initServletMockObjects();
setupBeforeInitDispatcher();
initDispatcherParams();
@@ -239,14 +239,10 @@ protected String getConfigPath() {
return null;
}
+ @Override
@After
public void tearDown() throws Exception {
- super.tearDown();
- if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
- dispatcher.cleanup();
- dispatcher = null;
- }
- StrutsTestCaseHelper.tearDown();
+ StrutsTestCaseHelper.tearDown(dispatcher);
}
}
diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
index 6267a1d440..16e9454792 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
@@ -169,8 +169,8 @@ protected void injectStrutsDependencies(Object object) {
* Sets up the configuration settings, XWork configuration, and
* message resources
*/
+ @Override
protected void setUp() throws Exception {
- super.setUp();
initServletMockObjects();
setupBeforeInitDispatcher();
dispatcher = initDispatcher(dispatcherInitParams);
@@ -200,14 +200,9 @@ protected Dispatcher initDispatcher(Map params) {
return du;
}
+ @Override
protected void tearDown() throws Exception {
- super.tearDown();
- // maybe someone else already destroyed Dispatcher
- if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
- dispatcher.cleanup();
- dispatcher = null;
- }
- StrutsTestCaseHelper.tearDown();
+ StrutsTestCaseHelper.tearDown(dispatcher);
}
}
diff --git a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
index e435ccc0db..37b033aa88 100644
--- a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
+++ b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/OldDecorator2NewStrutsDecorator.java
@@ -1,199 +1,204 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.sitemesh;
-
-import com.opensymphony.module.sitemesh.RequestConstants;
-import com.opensymphony.sitemesh.Content;
-import com.opensymphony.sitemesh.webapp.SiteMeshWebAppContext;
-import com.opensymphony.sitemesh.webapp.decorator.BaseWebAppDecorator;
-import com.opensymphony.xwork2.*;
-import com.opensymphony.xwork2.interceptor.PreResultListener;
-import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.ValueStackFactory;
-import freemarker.template.Configuration;
-import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.dispatcher.Dispatcher;
-
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Locale;
-
-/**
- * Adapts a SiteMesh 2 {@link com.opensymphony.module.sitemesh.Decorator} to a
- * SiteMesh 3 {@link com.opensymphony.sitemesh.Decorator}.
- *
- * @since SiteMesh 3
- */
-public abstract class OldDecorator2NewStrutsDecorator extends BaseWebAppDecorator implements RequestConstants {
-
- protected com.opensymphony.module.sitemesh.Decorator oldDecorator;
- private static String customEncoding;
-
- public OldDecorator2NewStrutsDecorator(com.opensymphony.module.sitemesh.Decorator oldDecorator) {
- this.oldDecorator = oldDecorator;
- }
-
- public OldDecorator2NewStrutsDecorator() {
- oldDecorator = null;
- }
-
-
- /**
- * Applies the decorator, using the relevent contexts
- *
- * @param content The content
- * @param request The servlet request
- * @param response The servlet response
- * @param servletContext The servlet context
- * @param ctx The action context for this request, populated with the server state
- */
- protected abstract void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ActionContext ctx) throws ServletException, IOException;
-
- /**
- * Applies the decorator, creating the relevent contexts and delegating to
- * the extended applyDecorator().
- *
- * @param content The content
- * @param request The servlet request
- * @param response The servlet response
- * @param servletContext The servlet context
- * @param webAppContext The web app context
- */
-
- protected void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, SiteMeshWebAppContext webAppContext) throws IOException, ServletException {
-
- // see if the URI path (webapp) is set
- if (oldDecorator.getURIPath() != null) {
- // in a security conscious environment, the servlet container
- // may return null for a given URL
- if (servletContext.getContext(oldDecorator.getURIPath()) != null) {
- servletContext = servletContext.getContext(oldDecorator.getURIPath());
- }
- }
-
- ActionContext ctx = ServletActionContext.getActionContext(request);
- if (ctx == null) {
- // ok, one isn't associated with the request, so let's create one using the current Dispatcher
- ValueStack vs = Dispatcher.getInstance().getContainer().getInstance(ValueStackFactory.class).createValueStack();
- vs.getContext().putAll(Dispatcher.getInstance().createContextMap(request, response, null));
- ctx = ActionContext.of(vs.getContext());
- if (ctx.getActionInvocation() == null) {
- // put in a dummy ActionSupport so basic functionality still works
- ActionSupport action = new ActionSupport();
- vs.push(action);
- ctx.withActionInvocation(new DummyActionInvocation(action));
- }
- }
-
- // delegate to the actual page decorator
- render(content, request, response, servletContext, ctx);
-
- }
-
- /**
- * Returns the locale used for the {@link freemarker.template.Configuration#getTemplate(String, java.util.Locale)} call. The base implementation
- * simply returns the locale setting of the action (assuming the action implements {@link LocaleProvider}) or, if
- * the action does not the configuration's locale is returned. Override this method to provide different behaviour,
- */
- protected Locale getLocale(ActionInvocation invocation, Configuration configuration) {
- if (invocation.getAction() instanceof LocaleProvider) {
- return ((LocaleProvider) invocation.getAction()).getLocale();
- } else {
- return configuration.getLocale();
- }
- }
-
-
- /**
- * Gets the L18N encoding of the system. The default is UTF-8.
- */
- protected String getEncoding() {
- String encoding = customEncoding;
- if (encoding == null) {
- encoding = System.getProperty("file.encoding");
- }
- if (encoding == null) {
- encoding = "UTF-8";
- }
- return encoding;
- }
-
-
- static class DummyActionInvocation implements ActionInvocation {
-
- ActionSupport action;
-
- public DummyActionInvocation(ActionSupport action) {
- this.action = action;
- }
-
- public Object getAction() {
- return action;
- }
-
- public boolean isExecuted() {
- return false;
- }
-
- public ActionContext getInvocationContext() {
- return null;
- }
-
- public ActionProxy getProxy() {
- return null;
- }
-
- public Result getResult() throws Exception {
- return null;
- }
-
- public String getResultCode() {
- return null;
- }
-
- public void setResultCode(String resultCode) {
- }
-
- public ValueStack getStack() {
- return null;
- }
-
- public void addPreResultListener(PreResultListener listener) {
- }
-
- public String invoke() throws Exception {
- return null;
- }
-
- public String invokeActionOnly() throws Exception {
- return null;
- }
-
- public void setActionEventListener(ActionEventListener listener) {
- }
-
- public void init(ActionProxy proxy) {
- }
-
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.sitemesh;
+
+import com.opensymphony.module.sitemesh.RequestConstants;
+import com.opensymphony.sitemesh.Content;
+import com.opensymphony.sitemesh.webapp.SiteMeshWebAppContext;
+import com.opensymphony.sitemesh.webapp.decorator.BaseWebAppDecorator;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionEventListener;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionSupport;
+import com.opensymphony.xwork2.LocaleProvider;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.interceptor.PreResultListener;
+import com.opensymphony.xwork2.util.ValueStack;
+import freemarker.template.Configuration;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.Dispatcher;
+
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * Adapts a SiteMesh 2 {@link com.opensymphony.module.sitemesh.Decorator} to a
+ * SiteMesh 3 {@link com.opensymphony.sitemesh.Decorator}.
+ *
+ * @since SiteMesh 3
+ */
+public abstract class OldDecorator2NewStrutsDecorator extends BaseWebAppDecorator implements RequestConstants {
+
+ protected com.opensymphony.module.sitemesh.Decorator oldDecorator;
+ private static String customEncoding;
+
+ public OldDecorator2NewStrutsDecorator(com.opensymphony.module.sitemesh.Decorator oldDecorator) {
+ this.oldDecorator = oldDecorator;
+ }
+
+ public OldDecorator2NewStrutsDecorator() {
+ oldDecorator = null;
+ }
+
+
+ /**
+ * Applies the decorator, using the relevent contexts
+ *
+ * @param content The content
+ * @param request The servlet request
+ * @param response The servlet response
+ * @param servletContext The servlet context
+ * @param ctx The action context for this request, populated with the server state
+ */
+ protected abstract void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ActionContext ctx) throws ServletException, IOException;
+
+ /**
+ * Applies the decorator, creating the relevent contexts and delegating to
+ * the extended applyDecorator().
+ *
+ * @param content The content
+ * @param request The servlet request
+ * @param response The servlet response
+ * @param servletContext The servlet context
+ * @param webAppContext The web app context
+ */
+
+ protected void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, SiteMeshWebAppContext webAppContext) throws IOException, ServletException {
+
+ // see if the URI path (webapp) is set
+ if (oldDecorator.getURIPath() != null) {
+ // in a security conscious environment, the servlet container
+ // may return null for a given URL
+ if (servletContext.getContext(oldDecorator.getURIPath()) != null) {
+ servletContext = servletContext.getContext(oldDecorator.getURIPath());
+ }
+ }
+
+ ActionContext ctx = ServletActionContext.getActionContext(request);
+ if (ctx == null) {
+ // ok, one isn't associated with the request, so let's create one using the current Dispatcher
+ ValueStack vs = Dispatcher.getInstance().getValueStackFactory().createValueStack();
+ vs.getContext().putAll(Dispatcher.getInstance().createContextMap(request, response, null));
+ ctx = ActionContext.of(vs.getContext());
+ if (ctx.getActionInvocation() == null) {
+ // put in a dummy ActionSupport so basic functionality still works
+ ActionSupport action = new ActionSupport();
+ vs.push(action);
+ ctx.withActionInvocation(new DummyActionInvocation(action));
+ }
+ }
+
+ // delegate to the actual page decorator
+ render(content, request, response, servletContext, ctx);
+
+ }
+
+ /**
+ * Returns the locale used for the {@link freemarker.template.Configuration#getTemplate(String, java.util.Locale)} call. The base implementation
+ * simply returns the locale setting of the action (assuming the action implements {@link LocaleProvider}) or, if
+ * the action does not the configuration's locale is returned. Override this method to provide different behaviour,
+ */
+ protected Locale getLocale(ActionInvocation invocation, Configuration configuration) {
+ if (invocation.getAction() instanceof LocaleProvider) {
+ return ((LocaleProvider) invocation.getAction()).getLocale();
+ } else {
+ return configuration.getLocale();
+ }
+ }
+
+
+ /**
+ * Gets the L18N encoding of the system. The default is UTF-8.
+ */
+ protected String getEncoding() {
+ String encoding = customEncoding;
+ if (encoding == null) {
+ encoding = System.getProperty("file.encoding");
+ }
+ if (encoding == null) {
+ encoding = "UTF-8";
+ }
+ return encoding;
+ }
+
+
+ static class DummyActionInvocation implements ActionInvocation {
+
+ ActionSupport action;
+
+ public DummyActionInvocation(ActionSupport action) {
+ this.action = action;
+ }
+
+ public Object getAction() {
+ return action;
+ }
+
+ public boolean isExecuted() {
+ return false;
+ }
+
+ public ActionContext getInvocationContext() {
+ return null;
+ }
+
+ public ActionProxy getProxy() {
+ return null;
+ }
+
+ public Result getResult() throws Exception {
+ return null;
+ }
+
+ public String getResultCode() {
+ return null;
+ }
+
+ public void setResultCode(String resultCode) {
+ }
+
+ public ValueStack getStack() {
+ return null;
+ }
+
+ public void addPreResultListener(PreResultListener listener) {
+ }
+
+ public String invoke() throws Exception {
+ return null;
+ }
+
+ public String invokeActionOnly() throws Exception {
+ return null;
+ }
+
+ public void setActionEventListener(ActionEventListener listener) {
+ }
+
+ public void init(ActionProxy proxy) {
+ }
+
+ }
+
+}
diff --git a/plugins/testng/src/main/java/org/apache/struts2/testng/StrutsTestCase.java b/plugins/testng/src/main/java/org/apache/struts2/testng/StrutsTestCase.java
index 630eb2df69..6df81ff667 100644
--- a/plugins/testng/src/main/java/org/apache/struts2/testng/StrutsTestCase.java
+++ b/plugins/testng/src/main/java/org/apache/struts2/testng/StrutsTestCase.java
@@ -18,13 +18,13 @@
*/
package org.apache.struts2.testng;
-import java.util.Map;
-
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.util.StrutsTestCaseHelper;
+import org.springframework.mock.web.MockServletContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
-import org.springframework.mock.web.MockServletContext;
+
+import java.util.Map;
/**
* Base test class for TestNG unit tests. Provides common Struts variables
@@ -33,12 +33,12 @@
public class StrutsTestCase extends TestNGXWorkTestCase {
@BeforeTest
+ @Override
protected void setUp() throws Exception {
- super.setUp();
initDispatcher(null);
}
- protected Dispatcher initDispatcher(Map params) {
+ protected Dispatcher initDispatcher(Map params) {
Dispatcher du = StrutsTestCaseHelper.initDispatcher(new MockServletContext(), params);
configurationManager = du.getConfigurationManager();
configuration = configurationManager.getConfiguration();
@@ -56,8 +56,8 @@ protected T createAction(Class clazz) {
}
@AfterTest
+ @Override
protected void tearDown() throws Exception {
- super.tearDown();
StrutsTestCaseHelper.tearDown();
}
}
diff --git a/pom.xml b/pom.xml
index 259242da83..26ee8bafa5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,6 +107,8 @@
UTF-82023-11-12T10:00:00Z17
+ 17
+ 179.6
@@ -379,7 +381,7 @@
org.apache.maven.pluginsmaven-release-plugin
- 3.0.0-M1
+ 3.0.1maven-jar-plugin
@@ -980,7 +982,7 @@
org.apache.commonscommons-compress
- 1.24.0
+ 1.25.0