mirror of
https://github.com/apache/struts.git
synced 2026-08-06 07:06:58 +00:00
Merge pull request #850 from apache/merge-master-to-7xx-2024-01-20
[Struts-7] Merge master to 7xx 2024-01-20
This commit is contained in:
@@ -58,7 +58,7 @@ jobs:
|
||||
publish_results: true
|
||||
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # 4.0.0
|
||||
uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # 4.1.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
Vendored
+11
-12
@@ -125,18 +125,17 @@ pipeline {
|
||||
steps {
|
||||
sh './mvnw -B package -DskipTests'
|
||||
sshPublisher(publishers: [
|
||||
sshPublisherDesc(
|
||||
configName: 'Nightlies',
|
||||
transfers: [
|
||||
sshTransfer(
|
||||
remoteDirectory: '/struts/snapshot',
|
||||
removePrefix: 'assembly/target/assembly/out',
|
||||
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
|
||||
cleanRemote: true
|
||||
)
|
||||
],
|
||||
verbose: true
|
||||
)
|
||||
sshPublisherDesc(
|
||||
configName: 'Nightlies',
|
||||
transfers: [
|
||||
sshTransfer(
|
||||
remoteDirectory: '/struts/snapshot',
|
||||
removePrefix: 'assembly/target/assembly/out',
|
||||
sourceFiles: 'assembly/target/assembly/out/struts-*.zip'
|
||||
)
|
||||
],
|
||||
verbose: true
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
<include>ognl:ognl</include>
|
||||
<include>org.apache.commons:commons-fileupload2-jakarta</include>
|
||||
<include>org.apache.commons:commons-io</include>
|
||||
<include>com.github.ben-manes.caffeine:caffeine</include>
|
||||
<include>org.javassist:javassist</include>
|
||||
</includes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
|
||||
@@ -51,10 +51,6 @@ public abstract class XWorkJUnit4TestCase {
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
configurationManager = null;
|
||||
configuration = null;
|
||||
container = null;
|
||||
actionProxyFactory = null;
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
|
||||
@@ -64,10 +64,6 @@ public abstract class XWorkTestCase extends TestCase {
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
configurationManager = null;
|
||||
configuration = null;
|
||||
container = null;
|
||||
actionProxyFactory = null;
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
@@ -77,6 +73,16 @@ public abstract class XWorkTestCase extends TestCase {
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
protected void loadButSet(Map<String, ?> properties) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
properties.forEach((k, v) -> props.setProperty(k, String.valueOf(v)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> void loadButAdd(final Class<T> type, final T impl) {
|
||||
loadButAdd(type, Container.DEFAULT_NAME, impl);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
@@ -100,6 +101,7 @@ import com.opensymphony.xwork2.util.fs.DefaultFileManager;
|
||||
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import ognl.MethodAccessor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -323,12 +325,8 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
protected ActionContext setContext(Container cont) {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
if (context == null) {
|
||||
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
|
||||
context = ActionContext.of(vs.getContext()).bind();
|
||||
}
|
||||
return context;
|
||||
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
|
||||
return ActionContext.of(vs.getContext()).bind();
|
||||
}
|
||||
|
||||
protected Container createBootstrapContainer(List<ContainerProvider> providers) {
|
||||
@@ -389,6 +387,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
|
||||
.factory(RootAccessor.class, CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
|
||||
-4
@@ -47,7 +47,6 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
|
||||
@@ -66,7 +65,6 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFileParser;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
@@ -142,8 +140,6 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
|
||||
@@ -31,6 +31,8 @@ import ognl.MethodAccessor;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -40,6 +42,8 @@ import java.util.Set;
|
||||
*/
|
||||
public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
|
||||
|
||||
protected XWorkConverter xworkConverter;
|
||||
protected RootAccessor compoundRootAccessor;
|
||||
protected TextProvider textProvider;
|
||||
@@ -57,6 +61,11 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
OgnlRuntime.setMethodAccessor(CompoundRoot.class, compoundRootAccessor);
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected void setMethodAccessor(MethodAccessor methodAccessor) {
|
||||
OgnlRuntime.setMethodAccessor(Object.class, methodAccessor);
|
||||
}
|
||||
|
||||
@Inject("system")
|
||||
protected void setTextProvider(TextProvider textProvider) {
|
||||
this.textProvider = textProvider;
|
||||
@@ -79,26 +88,76 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
return newStack.getActionContext().withContainer(container).withValueStack(newStack).getValueStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link PropertyAccessor}'s, {@link MethodAccessor}'s and {@link NullHandler}'s are registered on a per-class
|
||||
* basis by defining a bean adhering to the corresponding interface with a name corresponding to the class it is
|
||||
* intended to handle.
|
||||
* <p>
|
||||
* 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 {
|
||||
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<String> names = container.getInstanceNames(MethodAccessor.class);
|
||||
for (String name : names) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerNullHandlers() throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(NullHandler.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(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());
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerPropertyAccessors() throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.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());
|
||||
}
|
||||
|
||||
names = container.getInstanceNames(MethodAccessor.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
|
||||
}
|
||||
|
||||
names = container.getInstanceNames(NullHandler.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(container.getInstance(NullHandler.class, name)));
|
||||
}
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String> patterns);
|
||||
void setAcceptedPatterns(Set<String> patterns);
|
||||
|
||||
/**
|
||||
* Allow access list of all defined excluded patterns
|
||||
*
|
||||
* @return set of excluded patterns
|
||||
*/
|
||||
public Set<Pattern> getAcceptedPatterns();
|
||||
Set<Pattern> getAcceptedPatterns();
|
||||
|
||||
public final static class IsAccepted {
|
||||
final class IsAccepted {
|
||||
|
||||
private final boolean accepted;
|
||||
private final String acceptedPattern;
|
||||
|
||||
@@ -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<String> patterns);
|
||||
void setExcludedPatterns(Set<String> patterns);
|
||||
|
||||
/**
|
||||
* Allow access list of all defined excluded patterns
|
||||
*
|
||||
* @return set of excluded patterns
|
||||
*/
|
||||
public Set<Pattern> getExcludedPatterns();
|
||||
Set<Pattern> getExcludedPatterns();
|
||||
|
||||
public final static class IsExcluded {
|
||||
final class IsExcluded {
|
||||
|
||||
private final boolean excluded;
|
||||
private final String excludedPattern;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -66,10 +66,6 @@ public class Radio extends ListUIBean {
|
||||
return TEMPLATE;
|
||||
}
|
||||
|
||||
public void evaluateExtraParams() {
|
||||
super.evaluateExtraParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
|
||||
*
|
||||
@@ -80,8 +76,4 @@ public class Radio extends ListUIBean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Class<?> getValueClassType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
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 class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
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);
|
||||
|
||||
@@ -151,11 +151,16 @@ public class ApplicationMap extends AbstractMap<String, Object> implements Seria
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -23,25 +23,25 @@ import com.opensymphony.xwork2.inject.Container;
|
||||
/**
|
||||
* Simple class to hold Container instance per thread to minimise number of attempts
|
||||
* to read configuration and build each time a new configuration.
|
||||
*
|
||||
* <p>
|
||||
* 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<Container> instance = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Container> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ import org.apache.struts2.config.StrutsBeanSelectionProvider;
|
||||
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<DispatcherListener> 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 class Dispatcher {
|
||||
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 class Dispatcher {
|
||||
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 class Dispatcher {
|
||||
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 class Dispatcher {
|
||||
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 class Dispatcher {
|
||||
}
|
||||
|
||||
// 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 @@ public class Dispatcher {
|
||||
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 class Dispatcher {
|
||||
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 @@ public class Dispatcher {
|
||||
}
|
||||
|
||||
protected ActionProxy createActionProxy(String namespace, String name, String method, Map<String, Object> extraContext) {
|
||||
ActionProxyFactory actionProxyFactory = getContainer().getInstance(ActionProxyFactory.class);
|
||||
return actionProxyFactory.createActionProxy(namespace, name, method, extraContext, true, false);
|
||||
}
|
||||
|
||||
@@ -865,6 +910,7 @@ public class Dispatcher {
|
||||
* @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 class Dispatcher {
|
||||
}
|
||||
|
||||
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 @@ public class Dispatcher {
|
||||
* @return a multi part request object
|
||||
*/
|
||||
protected MultiPartRequest getMultiPartRequest() {
|
||||
MultiPartRequest mpr = null;
|
||||
//check for alternate implementations of MultiPartRequest
|
||||
Set<String> 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 class Dispatcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 class ExecuteOperations {
|
||||
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
|
||||
|
||||
@@ -57,7 +57,7 @@ public class InitOperations {
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.apache.struts2.dispatcher;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MockDispatcher extends Dispatcher {
|
||||
|
||||
@@ -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 interface Parameter {
|
||||
@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 interface Parameter {
|
||||
|
||||
class Empty implements Parameter {
|
||||
|
||||
private String name;
|
||||
private final String name;
|
||||
|
||||
public Empty(String name) {
|
||||
this.name = name;
|
||||
|
||||
@@ -20,13 +20,11 @@ package org.apache.struts2.dispatcher;
|
||||
|
||||
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 class PrepareOperations {
|
||||
dispatcher.cleanUpRequest(request);
|
||||
} finally {
|
||||
ActionContext.clear();
|
||||
Dispatcher.setInstance(null);
|
||||
Dispatcher.clearInstance();
|
||||
devModeOverride.remove();
|
||||
}
|
||||
});
|
||||
@@ -101,7 +99,7 @@ public class PrepareOperations {
|
||||
} 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 class PrepareOperations {
|
||||
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 {
|
||||
|
||||
@@ -125,11 +125,16 @@ public class RequestMap extends AbstractMap<String, Object> implements Serializa
|
||||
* @param key the name of the attribute to remove.
|
||||
* @return the value that was removed or <tt>null</tt> 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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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 {
|
||||
|
||||
@@ -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,18 +61,23 @@ public final class CspInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
|
||||
private void applySettings(ActionInvocation invocation, CspSettings cspSettings) {
|
||||
if (enforcingMode != null) {
|
||||
LOG.trace("Applying: {} to enforcingMode", enforcingMode);
|
||||
cspSettings.setEnforcingMode(enforcingMode);
|
||||
}
|
||||
if (reportUri != null) {
|
||||
LOG.trace("Applying: {} to reportUri", reportUri);
|
||||
cspSettings.setReportUri(reportUri);
|
||||
}
|
||||
|
||||
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);
|
||||
String finalReportUri = reportUri;
|
||||
|
||||
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);
|
||||
cspSettings.addCspHeaders(request, response);
|
||||
@@ -99,8 +105,23 @@ public final class CspInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+162
-134
@@ -43,7 +43,6 @@ import org.apache.struts2.dispatcher.HttpParameters;
|
||||
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.Set;
|
||||
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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
@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 (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
Map<String, Object> 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 (action instanceof NoParameters) {
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
ActionContext actionContext = invocation.getInvocationContext();
|
||||
HttpParameters parameters = retrieveParameters(actionContext);
|
||||
|
||||
if (parameters == null) {
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
|
||||
}
|
||||
|
||||
Map<String, Object> 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
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #applyParameters}
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
HttpParameters params;
|
||||
Map<String, Parameter> 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<String, Parameter> 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<String, Parameter> acceptableParameters = toAcceptableParameters(parameters, action);
|
||||
|
||||
if (isAcceptableParameter) {
|
||||
acceptableParameters.put(parameterName, entry.getValue());
|
||||
}
|
||||
}
|
||||
ValueStack newStack = toNewStack(stack);
|
||||
batchApplyReflectionContextState(newStack.getContext(), true);
|
||||
applyMemberAccessProperties(newStack);
|
||||
|
||||
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
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
ReflectionContextState.setReportingConversionErrors(context, true);
|
||||
applyParametersOnStack(newStack, acceptableParameters, action);
|
||||
|
||||
//keep locale from original context
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : acceptableParameters.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Parameter value = entry.getValue();
|
||||
try {
|
||||
newStack.setParameter(name, value.getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearableStack) {
|
||||
if (newStack instanceof ClearableValueStack) {
|
||||
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
|
||||
}
|
||||
|
||||
addParametersToContext(ActionContext.getContext(), acceptableParameters);
|
||||
}
|
||||
|
||||
protected void batchApplyReflectionContextState(Map<String, Object> 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);
|
||||
if (newStack instanceof ClearableValueStack) {
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
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<String, Parameter> toAcceptableParameters(HttpParameters parameters, Object action) {
|
||||
HttpParameters newParams = initNewHttpParameters(parameters);
|
||||
Map<String, Parameter> acceptableParameters = initParameterMap();
|
||||
|
||||
for (Map.Entry<String, Parameter> 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<String, Parameter> initParameterMap() {
|
||||
if (ordered) {
|
||||
return new TreeMap<>(getOrderedComparator());
|
||||
} else {
|
||||
return new TreeMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpParameters initNewHttpParameters(HttpParameters parameters) {
|
||||
if (ordered) {
|
||||
return HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
|
||||
} else {
|
||||
return HttpParameters.create().withParent(parameters).build();
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyParametersOnStack(ValueStack stack, Map<String, Parameter> parameters, Object action) {
|
||||
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
|
||||
try {
|
||||
stack.setParameter(entry.getKey(), entry.getValue().getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, entry.getKey(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<String> messages = ((ValidationAware) action).getActionMessages();
|
||||
ValidationAware validationAware = (ValidationAware) action;
|
||||
Collection<String> messages = validationAware.getActionMessages();
|
||||
messages.add(message);
|
||||
((ValidationAware) action).setActionMessages(messages);
|
||||
validationAware.setActionMessages(messages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +295,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
* @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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
* @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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
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<String, Parameter> 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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
* @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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
* @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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
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 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
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 class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
|
||||
acceptedValuePatterns = unmodifiableSet(acceptedValuePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,7 +583,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
|
||||
excludedValuePatterns = unmodifiableSet(excludedValuePatterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,19 +28,17 @@ import org.apache.struts2.dispatcher.DispatcherErrorHandler;
|
||||
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<String,String> params) {
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
}
|
||||
Dispatcher du = new DispatcherWrapper(ctx, params);
|
||||
|
||||
public static Dispatcher initDispatcher(ServletContext ctx, Map<String, String> params) {
|
||||
Dispatcher du = new DispatcherWrapper(ctx, params != null ? params : emptyMap());
|
||||
du.init();
|
||||
Dispatcher.setInstance(du);
|
||||
|
||||
@@ -52,8 +50,16 @@ public class StrutsTestCaseHelper {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,11 @@
|
||||
<bean type="ognl.PropertyAccessor" name="org.apache.struts2.dispatcher.Parameter"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor"/>
|
||||
|
||||
<bean type="ognl.MethodAccessor" name="struts"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor"/>
|
||||
|
||||
<!-- DEPRECATED since 6.4.0 - Retained for backwards compatibility with custom beans
|
||||
that may expect it to be present. Inject the DEFAULT bean of 'ognl.MethodAccessor' above instead. -->
|
||||
<bean type="ognl.MethodAccessor" name="java.lang.Object"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor"/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -90,43 +90,27 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
refreshContainerFields();
|
||||
}
|
||||
|
||||
protected void refreshContainerFields() {
|
||||
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;
|
||||
vs = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current OgnlValueStackFactory instance from current container
|
||||
*/
|
||||
private OgnlValueStackFactory getValueStackFactory() {
|
||||
return (OgnlValueStackFactory) container.getInstance(ValueStackFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String, String> 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 class OgnlValueStackTest extends XWorkTestCase {
|
||||
* 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 class OgnlValueStackTest extends XWorkTestCase {
|
||||
* 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 class OgnlValueStackTest extends XWorkTestCase {
|
||||
* 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;
|
||||
|
||||
@@ -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<String,String> 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 @@ public abstract class StrutsInternalTestCase extends XWorkTestCase {
|
||||
* @return instance of {@see Dispatcher}
|
||||
*/
|
||||
protected Dispatcher initDispatcherWithConfigs(String configs) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
Map<String, String> 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.
|
||||
|
||||
@@ -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<String, String> 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);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ public class TestAction extends ActionSupport {
|
||||
private Long id;
|
||||
private List<SomeEnum> enumList;
|
||||
private List<Integer> intList;
|
||||
private Boolean someBool;
|
||||
|
||||
private final Map<String, String> texts = new HashMap<>();
|
||||
|
||||
@@ -254,4 +255,12 @@ public class TestAction extends ActionSupport {
|
||||
public void setIntList(List<Integer> intList) {
|
||||
this.intList = intList;
|
||||
}
|
||||
|
||||
public Boolean getSomeBool() {
|
||||
return someBool;
|
||||
}
|
||||
|
||||
public void setSomeBool(Boolean someBool) {
|
||||
this.someBool = someBool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class SettingsTest extends StrutsInternalTestCase {
|
||||
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"));
|
||||
|
||||
@@ -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 org.springframework.mock.web.MockServletContext;
|
||||
|
||||
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 class DispatcherTest extends StrutsInternalTestCase {
|
||||
"Error uploading: some error messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrepareSetEncodingProperly() {
|
||||
HttpServletRequest req = new MockHttpServletRequest();
|
||||
HttpServletResponse res = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher du = initDispatcher(new HashMap<String, String>() {{
|
||||
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<String, String>() {{
|
||||
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<String, String>() {{
|
||||
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<String, String>() {{
|
||||
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 class DispatcherTest extends StrutsInternalTestCase {
|
||||
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<String> expected = new HashSet<>();
|
||||
expected.add("struts-default.xml");
|
||||
expected.add("struts-beans.xml");
|
||||
@@ -238,135 +243,109 @@ public class DispatcherTest extends StrutsInternalTestCase {
|
||||
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<String, PackageConfig>());
|
||||
|
||||
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<String, PackageConfig> 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");
|
||||
|
||||
ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
|
||||
configurationManager.setConfiguration((Configuration) mockConfiguration.proxy());
|
||||
|
||||
Dispatcher dispatcher = new MockDispatcher(new MockServletContext(), new HashMap<>(), configurationManager);
|
||||
dispatcher.init();
|
||||
|
||||
configuration = spy(configurationManager.getConfiguration());
|
||||
configurationManager.setConfiguration(configuration);
|
||||
when(configuration.getPackageConfigs()).thenReturn(singletonMap("test", packageConfig));
|
||||
|
||||
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 class DispatcherTest extends StrutsInternalTestCase {
|
||||
|
||||
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 class DispatcherTest extends StrutsInternalTestCase {
|
||||
|
||||
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 class DispatcherTest extends StrutsInternalTestCase {
|
||||
* 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<String, String>() {{
|
||||
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<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
initDispatcher(new HashMap<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
initDispatcher(new HashMap<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
initDispatcher(new HashMap<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
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<String, Object>()); // 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<String, String>() {{
|
||||
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 @@ public class DispatcherTest extends StrutsInternalTestCase {
|
||||
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;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.opensymphony.xwork2.ActionContext;
|
||||
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 class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
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 class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
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 class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
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 class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
public void test_uriSetOnlyWhenSetIsCalled() throws Exception {
|
||||
String enforcingMode = "false";
|
||||
boolean enforcingMode = false;
|
||||
interceptor.setEnforcingMode(enforcingMode);
|
||||
|
||||
interceptor.intercept(mai);
|
||||
@@ -115,7 +116,7 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
public void testCannotParseUri() {
|
||||
String enforcingMode = "false";
|
||||
boolean enforcingMode = false;
|
||||
interceptor.setEnforcingMode(enforcingMode);
|
||||
|
||||
try {
|
||||
@@ -127,7 +128,7 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
public void testCannotParseRelativeUri() {
|
||||
String enforcingMode = "false";
|
||||
boolean enforcingMode = false;
|
||||
interceptor.setEnforcingMode(enforcingMode);
|
||||
|
||||
try {
|
||||
@@ -139,13 +140,41 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
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", "false");
|
||||
checkHeader("/report-uri", enforcingMode);
|
||||
}
|
||||
|
||||
public void checkHeader(String reportUri, String 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("/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, 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 class CspInterceptorTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
String header;
|
||||
if (enforcingMode.equals("true")) {
|
||||
if (enforcingMode) {
|
||||
header = response.getHeader(CspSettings.CSP_ENFORCE_HEADER);
|
||||
} else {
|
||||
header = response.getHeader(CspSettings.CSP_REPORT_HEADER);
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@ public class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
|
||||
executor.execute(bp);
|
||||
}
|
||||
|
||||
Thread.sleep(500);
|
||||
Thread.sleep(800);
|
||||
|
||||
for (BackgroundProcess bp : bps) {
|
||||
assertTrue("Process is still active: " + bp, bp.isDone());
|
||||
|
||||
+1
-1
@@ -1052,7 +1052,7 @@ class ValidateAction implements ValidationAware {
|
||||
}
|
||||
|
||||
public boolean hasActionMessages() {
|
||||
return messages.size() > 0;
|
||||
return !messages.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.apache.struts2.TestAction;
|
||||
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.http.HttpServletResponse;
|
||||
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 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
|
||||
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);
|
||||
|
||||
@@ -276,6 +276,23 @@ public class AnchorTest extends AbstractUITagTest {
|
||||
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");
|
||||
|
||||
@@ -64,6 +64,37 @@ public class RadioTest extends AbstractUITagTest {
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testMapWithBooleanAsKeyWithoutForceValue() throws Exception {
|
||||
TestAction testAction = (TestAction) action;
|
||||
|
||||
Map<Boolean, String> 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 class RadioTest extends AbstractUITagTest {
|
||||
|
||||
List<SomeEnum> 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 class RadioTest extends AbstractUITagTest {
|
||||
|
||||
List<SomeEnum> 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();
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<input type="radio" name="myname" id="mynametrue" value="true"/>
|
||||
<label for="mynametrue">male</label>
|
||||
<input type="radio" name="myname" id="mynamefalse" checked="checked" value="false"/>
|
||||
<label for="mynamefalse">female</label>
|
||||
@@ -0,0 +1 @@
|
||||
<a id="mylink" href="a" disabled="disabled">should have disabled attribute</a>
|
||||
@@ -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!)
|
||||
|
||||
@@ -203,9 +203,9 @@ public abstract class StrutsJUnit4TestCase<T> extends XWorkJUnit4TestCase {
|
||||
* 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 @@ public abstract class StrutsJUnit4TestCase<T> extends XWorkJUnit4TestCase {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ public abstract class StrutsTestCase extends XWorkTestCase {
|
||||
* 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 @@ public abstract class StrutsTestCase extends XWorkTestCase {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+204
-199
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 @@ import org.springframework.mock.web.MockServletContext;
|
||||
public class StrutsTestCase extends TestNGXWorkTestCase {
|
||||
|
||||
@BeforeTest
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
initDispatcher(null);
|
||||
}
|
||||
|
||||
protected Dispatcher initDispatcher(Map<String,String> params) {
|
||||
protected Dispatcher initDispatcher(Map<String, String> params) {
|
||||
Dispatcher du = StrutsTestCaseHelper.initDispatcher(new MockServletContext(), params);
|
||||
configurationManager = du.getConfigurationManager();
|
||||
configuration = configurationManager.getConfiguration();
|
||||
@@ -56,8 +56,8 @@ public class StrutsTestCase extends TestNGXWorkTestCase {
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
StrutsTestCaseHelper.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.outputTimestamp>2023-11-12T10:00:00Z</project.build.outputTimestamp>
|
||||
<maven.compiler.release>17</maven.compiler.release>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
|
||||
<!-- dependency versions in alphanumeric order -->
|
||||
<asm.version>9.6</asm.version>
|
||||
@@ -379,7 +381,7 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<!-- See https://issues.apache.org/jira/browse/MRELEASE-1029 -->
|
||||
<version>3.0.0-M1</version>
|
||||
<version>3.0.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -980,7 +982,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.24.0</version>
|
||||
<version>1.25.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user