mirror of
https://github.com/apache/struts.git
synced 2026-08-06 15:17:00 +00:00
Merge pull request #1072 from apache/fix/WW-5468-modeldriven-2
WW-5468 Exempt ModelDriven Actions from @StrutsParameter requirement
This commit is contained in:
+1
-1
@@ -42,7 +42,7 @@ public class ModelDrivenAction extends ActionSupport implements ModelDriven {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public Gangster getModel() {
|
||||
return new Gangster();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
/**
|
||||
* ModelDriven Actions provide a model object to be pushed onto the ValueStack
|
||||
* in addition to the Action itself, allowing a FormBean type approach like Struts.
|
||||
@@ -28,9 +30,13 @@ public interface ModelDriven<T> {
|
||||
|
||||
/**
|
||||
* Gets the model to be pushed onto the ValueStack instead of the Action itself.
|
||||
* <p>
|
||||
* Please be aware that all setters and getters of every depth on the object returned by this method are available
|
||||
* for user parameter injection!
|
||||
*
|
||||
* @return the model
|
||||
*/
|
||||
@StrutsParameter(depth = Integer.MAX_VALUE)
|
||||
T getModel();
|
||||
|
||||
}
|
||||
|
||||
+39
-20
@@ -20,6 +20,7 @@ package org.apache.struts2.interceptor.parameter;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
@@ -258,19 +259,19 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
protected ValueStack toNewStack(ValueStack stack) {
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
if (newStack instanceof ClearableValueStack) {
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
if (newStack instanceof ClearableValueStack clearable) {
|
||||
clearable.clearContextValues();
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
return newStack;
|
||||
}
|
||||
|
||||
protected void applyMemberAccessProperties(ValueStack stack) {
|
||||
if (!(stack instanceof MemberAccessValueStack)) {
|
||||
if (!(stack instanceof MemberAccessValueStack accessValueStack)) {
|
||||
return;
|
||||
}
|
||||
((MemberAccessValueStack) stack).useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
((MemberAccessValueStack) stack).useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
accessValueStack.useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
accessValueStack.useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
}
|
||||
|
||||
protected Map<String, Parameter> toAcceptableParameters(HttpParameters parameters, Object action) {
|
||||
@@ -332,7 +333,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
protected boolean isAcceptableParameterNameAware(String name, Object action) {
|
||||
return !(action instanceof ParameterNameAware) || ((ParameterNameAware) action).acceptableParameterName(name);
|
||||
return !(action instanceof ParameterNameAware nameAware) || nameAware.acceptableParameterName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +349,15 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
long paramDepth = name.codePoints().mapToObj(c -> (char) c).filter(NESTING_CHARS::contains).count();
|
||||
|
||||
if (action instanceof ModelDriven<?> && !ActionContext.getContext().getValueStack().peek().equals(action)) {
|
||||
LOG.debug("Model driven Action detected, exempting from @StrutsParameter annotation requirement and OGNL allowlisting model type");
|
||||
// (Exempted by annotation on com.opensymphony.xwork2.ModelDriven#getModel)
|
||||
return hasValidAnnotatedMember("model", action, paramDepth + 1);
|
||||
}
|
||||
|
||||
if (requireAnnotationsTransitionMode && paramDepth == 0) {
|
||||
LOG.debug("Annotation transition mode enabled, exempting non-nested parameter [{}] from @StrutsParameter annotation requirement", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -365,6 +374,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
* save computation by checking this last.
|
||||
*/
|
||||
protected boolean hasValidAnnotatedMember(String rootProperty, Object action, long paramDepth) {
|
||||
LOG.debug("Checking Action [{}] for a matching, correctly annotated member for property [{}]",
|
||||
action.getClass().getSimpleName(), rootProperty);
|
||||
BeanInfo beanInfo = getBeanInfo(action);
|
||||
if (beanInfo == null) {
|
||||
return hasValidAnnotatedField(action, rootProperty, paramDepth);
|
||||
@@ -390,7 +401,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
if (getPermittedInjectionDepth(relevantMethod) < paramDepth) {
|
||||
String logMessage = format(
|
||||
"Parameter injection for method [%s] on action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
|
||||
"Parameter injection for method [%s] on Action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
|
||||
relevantMethod.getName(),
|
||||
relevantMethod.getDeclaringClass().getName());
|
||||
if (devMode) {
|
||||
@@ -400,8 +411,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
LOG.debug("Success: Matching annotated method [{}] found for property [{}] of depth [{}] on Action [{}]",
|
||||
relevantMethod.getName(), propDesc.getName(), paramDepth, action.getClass().getSimpleName());
|
||||
if (paramDepth >= 1) {
|
||||
allowlistClass(relevantMethod.getReturnType());
|
||||
allowlistClass(propDesc.getPropertyType());
|
||||
}
|
||||
if (paramDepth >= 2) {
|
||||
allowlistReturnTypeIfParameterized(relevantMethod);
|
||||
@@ -438,19 +451,23 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
protected boolean hasValidAnnotatedField(Object action, String fieldName, long paramDepth) {
|
||||
LOG.debug("No matching annotated method found for property [{}] of depth [{}] on Action [{}], now also checking for public field",
|
||||
fieldName, paramDepth, action.getClass().getSimpleName());
|
||||
Field field;
|
||||
try {
|
||||
field = action.getClass().getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
LOG.debug("Matching field for property [{}] not found on Action [{}]", fieldName, action.getClass().getSimpleName());
|
||||
return false;
|
||||
}
|
||||
if (!Modifier.isPublic(field.getModifiers())) {
|
||||
LOG.debug("Matching field [{}] is not public on Action [{}]", field.getName(), action.getClass().getSimpleName());
|
||||
return false;
|
||||
}
|
||||
if (getPermittedInjectionDepth(field) < paramDepth) {
|
||||
String logMessage = format(
|
||||
"Parameter injection for field [%s] on action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
|
||||
fieldName,
|
||||
"Parameter injection for field [%s] on Action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
|
||||
field.getName(),
|
||||
action.getClass().getName());
|
||||
if (devMode) {
|
||||
notifyDeveloperOfError(LOG, action, logMessage);
|
||||
@@ -459,6 +476,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
LOG.debug("Success: Matching annotated public field [{}] found for property of depth [{}] on Action [{}]",
|
||||
field.getName(), paramDepth, action.getClass().getSimpleName());
|
||||
if (paramDepth >= 1) {
|
||||
allowlistClass(field.getType());
|
||||
}
|
||||
@@ -512,7 +531,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
protected boolean isAcceptableParameterValueAware(Parameter param, Object action) {
|
||||
return !(action instanceof ParameterValueAware) || ((ParameterValueAware) action).acceptableParameterValue(param.getValue());
|
||||
return !(action instanceof ParameterValueAware valueAware) || valueAware.acceptableParameterValue(param.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -606,7 +625,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
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",
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
@@ -621,8 +640,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
if (result.isExcluded()) {
|
||||
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());
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
@@ -640,8 +659,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
if (excludedValuePattern.matcher(value).matches()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
|
||||
}
|
||||
@@ -663,8 +682,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] was not accepted!", value);
|
||||
}
|
||||
@@ -734,7 +753,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
acceptedValuePatterns, patterns);
|
||||
acceptedValuePatterns, patterns);
|
||||
}
|
||||
acceptedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
@@ -759,7 +778,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
LOG.debug("Setting excluded value patterns to [{}]", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
excludedValuePatterns, patterns);
|
||||
excludedValuePatterns, patterns);
|
||||
}
|
||||
excludedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
|
||||
@@ -44,9 +44,8 @@ public class ModelDrivenAction extends ActionSupport implements ModelDriven {
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack after the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public TestBean getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,8 @@ public class ModelDrivenAnnotationAction extends ActionSupport implements ModelD
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack after the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public AnnotatedTestBean getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -178,7 +178,7 @@ public class ModelDrivenInterceptorTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
|
||||
public class ModelDrivenAction extends ActionSupport implements ModelDriven {
|
||||
public class ModelDrivenAction extends ActionSupport implements ModelDriven<Object> {
|
||||
|
||||
@Override
|
||||
public Object getModel() {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package com.opensymphony.xwork2.test;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDrivenAction;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
|
||||
/**
|
||||
@@ -35,9 +34,8 @@ public class ModelDrivenAction2 extends ModelDrivenAction {
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack after the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 3)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public TestBean2 getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package com.opensymphony.xwork2.test;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDrivenAnnotationAction;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,9 +35,8 @@ public class ModelDrivenAnnotationAction2 extends ModelDrivenAnnotationAction {
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack after the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 3)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public AnnotationTestBean2 getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.opensymphony.xwork2.test.subtest;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDrivenAction;
|
||||
import com.opensymphony.xwork2.TestBean;
|
||||
|
||||
/**
|
||||
* Extends ModelDrivenAction to return a null model.
|
||||
@@ -31,7 +32,7 @@ public class NullModelDrivenAction extends ModelDrivenAction {
|
||||
* @return the model to be pushed onto the ValueStack instead of the Action itself
|
||||
*/
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public TestBean getModel() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -19,7 +19,7 @@
|
||||
package com.opensymphony.xwork2.validator;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
import com.opensymphony.xwork2.TestBean;
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,9 +33,8 @@ public class VisitorValidatorModelAction extends VisitorValidatorTestAction impl
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack instead of the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public TestBean getModel() {
|
||||
return getBean();
|
||||
}
|
||||
}
|
||||
|
||||
+30
-5
@@ -18,6 +18,9 @@
|
||||
*/
|
||||
package org.apache.struts2.interceptor.parameter;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.StubValueStack;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
@@ -63,6 +66,7 @@ public class StrutsParameterAnnotationTest {
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
threadAllowlist.clearAllowlist();
|
||||
ActionContext.clear();
|
||||
}
|
||||
|
||||
private void testParameter(Object action, String paramName, boolean shouldContain) {
|
||||
@@ -80,7 +84,7 @@ public class StrutsParameterAnnotationTest {
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> getParentClasses(Class<?> ...clazzes) {
|
||||
private Set<Class<?>> getParentClasses(Class<?>... clazzes) {
|
||||
Set<Class<?>> set = new HashSet<>();
|
||||
for (Class<?> clazz : clazzes) {
|
||||
set.add(clazz);
|
||||
@@ -258,8 +262,21 @@ public class StrutsParameterAnnotationTest {
|
||||
testParameter(new MethodAction(), "publicStrNotAnnotated", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publicModelPojo() {
|
||||
var action = new ModelAction();
|
||||
|
||||
class FieldAction {
|
||||
// Emulate ModelDrivenInterceptor running previously
|
||||
var valueStack = new StubValueStack();
|
||||
valueStack.push(action.getModel());
|
||||
ActionContext.of().withValueStack(valueStack).bind();
|
||||
|
||||
testParameter(action, "name", true);
|
||||
testParameter(action, "name.nested", true);
|
||||
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Object.class, Pojo.class));
|
||||
}
|
||||
|
||||
static class FieldAction {
|
||||
@StrutsParameter
|
||||
private String privateStr;
|
||||
|
||||
@@ -275,7 +292,7 @@ public class StrutsParameterAnnotationTest {
|
||||
public Pojo publicPojoDepthZero;
|
||||
|
||||
@StrutsParameter(depth = 1)
|
||||
public Pojo publicPojoDepthOne ;
|
||||
public Pojo publicPojoDepthOne;
|
||||
|
||||
@StrutsParameter(depth = 2)
|
||||
public Pojo publicPojoDepthTwo;
|
||||
@@ -290,7 +307,7 @@ public class StrutsParameterAnnotationTest {
|
||||
public Map<String, Pojo> publicPojoMapDepthTwo;
|
||||
}
|
||||
|
||||
class MethodAction {
|
||||
static class MethodAction {
|
||||
|
||||
@StrutsParameter
|
||||
private void setPrivateStr(String str) {
|
||||
@@ -343,6 +360,14 @@ public class StrutsParameterAnnotationTest {
|
||||
}
|
||||
}
|
||||
|
||||
class Pojo {
|
||||
static class ModelAction implements ModelDriven<Pojo> {
|
||||
|
||||
@Override
|
||||
public Pojo getModel() {
|
||||
return new Pojo();
|
||||
}
|
||||
}
|
||||
|
||||
static class Pojo {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import static com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPattern
|
||||
|
||||
/**
|
||||
* Unit test for {@link StreamResult}.
|
||||
*
|
||||
*/
|
||||
public class StreamResultTest extends StrutsInternalTestCase {
|
||||
|
||||
@@ -126,12 +125,12 @@ public class StreamResultTest extends StrutsInternalTestCase {
|
||||
|
||||
result.doExecute("helloworld", mai);
|
||||
|
||||
//check that that headers are not set by default
|
||||
//check that that headers are not set by default
|
||||
assertNull(response.getHeader("Pragma"));
|
||||
assertNull(response.getHeader("Cache-Control"));
|
||||
}
|
||||
|
||||
public void testAllowCacheFalse() throws Exception {
|
||||
public void testAllowCacheFalse() throws Exception {
|
||||
result.setInputName("streamForImage");
|
||||
result.setAllowCaching(false);
|
||||
result.doExecute("helloworld", mai);
|
||||
@@ -266,7 +265,6 @@ public class StreamResultTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
response = null;
|
||||
|
||||
+1
-4
@@ -20,17 +20,14 @@ package org.apache.struts.beanvalidation.actions;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import org.apache.struts.beanvalidation.models.Person;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.apache.struts.beanvalidation.models.Person;
|
||||
|
||||
public class ModelDrivenAction extends ActionSupport implements ModelDriven<Person>, ModelDrivenActionInterface {
|
||||
|
||||
@Valid
|
||||
private final Person model = new Person();
|
||||
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Person getModel() {
|
||||
return model;
|
||||
|
||||
+1
-4
@@ -20,18 +20,15 @@ package org.apache.struts.beanvalidation.actions;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import jakarta.validation.Valid;
|
||||
import org.apache.struts.beanvalidation.constraints.ValidationGroup;
|
||||
import org.apache.struts.beanvalidation.models.Person;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
public class ValidateGroupAction extends ActionSupport implements ModelDriven<Person> {
|
||||
|
||||
@Valid
|
||||
private final Person model = new Person();
|
||||
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Person getModel() {
|
||||
return model;
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.dispatcher.Dispatcher;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -75,7 +74,7 @@ public class StrutsRestTestCase<T> extends StrutsJUnit4TestCase<T> {
|
||||
ActionMapping mapping = getActionMapping(request);
|
||||
|
||||
assertNotNull(mapping);
|
||||
Dispatcher.getInstance().serviceAction(request, response, mapping);
|
||||
dispatcher.serviceAction(request, response, mapping);
|
||||
|
||||
if (response.getStatus() != HttpServletResponse.SC_OK)
|
||||
throw new ServletException("Error code [" + response.getStatus() + "], Error: ["
|
||||
|
||||
+172
-174
@@ -35,7 +35,6 @@ import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
import org.apache.struts2.ognl.StrutsOgnlGuard;
|
||||
import org.apache.struts2.result.HttpHeaderResult;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -51,97 +50,97 @@ import static jakarta.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
|
||||
public class RestActionInvocationTest extends TestCase {
|
||||
|
||||
RestActionInvocation restActionInvocation;
|
||||
MockHttpServletRequest request;
|
||||
MockHttpServletResponse response;
|
||||
RestActionInvocation restActionInvocation;
|
||||
MockHttpServletRequest request;
|
||||
MockHttpServletResponse response;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
restActionInvocation = new RestActionInvocationTester();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
ServletActionContext.setRequest(request);
|
||||
ServletActionContext.setResponse(response);
|
||||
restActionInvocation = new RestActionInvocationTester();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
ServletActionContext.setRequest(request);
|
||||
ServletActionContext.setResponse(response);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the correct action results: null, String, HttpHeaders, Result
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSaveResult() throws Exception {
|
||||
/**
|
||||
* Test the correct action results: null, String, HttpHeaders, Result
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSaveResult() throws Exception {
|
||||
|
||||
Object methodResult = "index";
|
||||
ActionConfig actionConfig = restActionInvocation.getProxy().getConfig();
|
||||
assertEquals("index", restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
Object methodResult = "index";
|
||||
ActionConfig actionConfig = restActionInvocation.getProxy().getConfig();
|
||||
assertEquals("index", restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
|
||||
setUp();
|
||||
methodResult = new DefaultHttpHeaders("show");
|
||||
assertEquals("show", restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
assertEquals(methodResult, restActionInvocation.httpHeaders);
|
||||
setUp();
|
||||
methodResult = new DefaultHttpHeaders("show");
|
||||
assertEquals("show", restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
assertEquals(methodResult, restActionInvocation.httpHeaders);
|
||||
|
||||
setUp();
|
||||
methodResult = new HttpHeaderResult(HttpServletResponse.SC_ACCEPTED);
|
||||
assertEquals(null, restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
assertEquals(methodResult, restActionInvocation.createResult());
|
||||
setUp();
|
||||
methodResult = new HttpHeaderResult(HttpServletResponse.SC_ACCEPTED);
|
||||
assertEquals(null, restActionInvocation.saveResult(actionConfig, methodResult));
|
||||
assertEquals(methodResult, restActionInvocation.createResult());
|
||||
|
||||
setUp();
|
||||
try {
|
||||
methodResult = new Object();
|
||||
restActionInvocation.saveResult(actionConfig, methodResult);
|
||||
setUp();
|
||||
try {
|
||||
methodResult = new Object();
|
||||
restActionInvocation.saveResult(actionConfig, methodResult);
|
||||
|
||||
// ko
|
||||
assertFalse(true);
|
||||
// ko
|
||||
assertFalse(true);
|
||||
|
||||
} catch (ConfigurationException c) {
|
||||
// ok, object not allowed
|
||||
}
|
||||
}
|
||||
} catch (ConfigurationException c) {
|
||||
// ok, object not allowed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the target selection: exception, error messages, model and null
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSelectTarget() throws Exception {
|
||||
/**
|
||||
* Test the target selection: exception, error messages, model and null
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSelectTarget() throws Exception {
|
||||
|
||||
// Exception
|
||||
Exception e = new Exception();
|
||||
restActionInvocation.getStack().set("exception", e);
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(e, restActionInvocation.target);
|
||||
// Exception
|
||||
Exception e = new Exception();
|
||||
restActionInvocation.getStack().set("exception", e);
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(e, restActionInvocation.target);
|
||||
|
||||
// Error messages
|
||||
setUp();
|
||||
String actionMessage = "Error!";
|
||||
RestActionSupport action = (RestActionSupport)restActionInvocation.getAction();
|
||||
action.addActionError(actionMessage);
|
||||
Map<String, Object> errors = new HashMap<String, Object>();
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add(actionMessage);
|
||||
errors.put("actionErrors", list);
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(errors, restActionInvocation.target);
|
||||
// Error messages
|
||||
setUp();
|
||||
String actionMessage = "Error!";
|
||||
RestActionSupport action = (RestActionSupport)restActionInvocation.getAction();
|
||||
action.addActionError(actionMessage);
|
||||
Map<String, Object> errors = new HashMap<String, Object>();
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add(actionMessage);
|
||||
errors.put("actionErrors", list);
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(errors, restActionInvocation.target);
|
||||
|
||||
// Model with get and no content in post, put, delete
|
||||
setUp();
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
request.setMethod("GET");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(model, restActionInvocation.target);
|
||||
request.setMethod("POST");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
request.setMethod("PUT");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
request.setMethod("DELETE");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
// Model with get and no content in post, put, delete
|
||||
setUp();
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
request.setMethod("GET");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(model, restActionInvocation.target);
|
||||
request.setMethod("POST");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
request.setMethod("PUT");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
request.setMethod("DELETE");
|
||||
restActionInvocation.selectTarget();
|
||||
assertEquals(null, restActionInvocation.target);
|
||||
|
||||
// disable content restriction to GET only
|
||||
model = new ArrayList<String>();
|
||||
@@ -155,108 +154,108 @@ public class RestActionInvocationTest extends TestCase {
|
||||
assertEquals(model.get(0), "Item1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the not modified status code.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testResultNotModified() throws Exception {
|
||||
/**
|
||||
* Test the not modified status code.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testResultNotModified() throws Exception {
|
||||
|
||||
request.addHeader("If-None-Match", "123");
|
||||
request.setMethod("GET");
|
||||
request.addHeader("If-None-Match", "123");
|
||||
request.setMethod("GET");
|
||||
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 123;
|
||||
}
|
||||
};
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 123;
|
||||
}
|
||||
};
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
|
||||
restActionInvocation.processResult();
|
||||
assertEquals(SC_NOT_MODIFIED, response.getStatus());
|
||||
restActionInvocation.processResult();
|
||||
assertEquals(SC_NOT_MODIFIED, response.getStatus());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the default error result.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testDefaultErrorResult() throws Exception {
|
||||
/**
|
||||
* Test the default error result.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testDefaultErrorResult() throws Exception {
|
||||
|
||||
// Exception
|
||||
Exception e = new Exception();
|
||||
restActionInvocation.getStack().set("exception", e);
|
||||
request.setMethod("GET");
|
||||
// Exception
|
||||
Exception e = new Exception();
|
||||
restActionInvocation.getStack().set("exception", e);
|
||||
request.setMethod("GET");
|
||||
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
|
||||
restActionInvocation.setDefaultErrorResultName("default-error");
|
||||
ResultConfig resultConfig = new ResultConfig.Builder("default-error",
|
||||
"org.apache.struts2.result.HttpHeaderResult")
|
||||
.addParam("status", "123").build();
|
||||
ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest",
|
||||
"RestAction", "org.apache.rest.RestAction")
|
||||
.addResultConfig(resultConfig)
|
||||
.build();
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setConfig(actionConfig);
|
||||
restActionInvocation.setDefaultErrorResultName("default-error");
|
||||
ResultConfig resultConfig = new ResultConfig.Builder("default-error",
|
||||
"org.apache.struts2.result.HttpHeaderResult")
|
||||
.addParam("status", "123").build();
|
||||
ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest",
|
||||
"RestAction", "org.apache.rest.RestAction")
|
||||
.addResultConfig(resultConfig)
|
||||
.build();
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setConfig(actionConfig);
|
||||
|
||||
restActionInvocation.processResult();
|
||||
assertEquals(123, response.getStatus());
|
||||
restActionInvocation.processResult();
|
||||
assertEquals(123, response.getStatus());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testNoResult() throws Exception {
|
||||
public void testNoResult() throws Exception {
|
||||
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
request.setMethod("GET");
|
||||
restActionInvocation.setResultCode("index");
|
||||
RestAction restAction = (RestAction)restActionInvocation.getAction();
|
||||
List<String> model = new ArrayList<String>();
|
||||
model.add("Item");
|
||||
restAction.model = model;
|
||||
request.setMethod("GET");
|
||||
restActionInvocation.setResultCode("index");
|
||||
|
||||
try {
|
||||
restActionInvocation.processResult();
|
||||
try {
|
||||
restActionInvocation.processResult();
|
||||
|
||||
// ko
|
||||
assertFalse(true);
|
||||
// ko
|
||||
assertFalse(true);
|
||||
|
||||
} catch (ConfigurationException c) {
|
||||
// ok, no result
|
||||
}
|
||||
} catch (ConfigurationException c) {
|
||||
// ok, no result
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the global execution
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInvoke() throws Exception {
|
||||
/**
|
||||
* Test the global execution
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInvoke() throws Exception {
|
||||
|
||||
// Default index method return 'success'
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setMethod("index");
|
||||
// Default index method return 'success'
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setMethod("index");
|
||||
|
||||
// Define result 'success'
|
||||
ResultConfig resultConfig = new ResultConfig.Builder("success",
|
||||
"org.apache.struts2.result.HttpHeaderResult")
|
||||
.addParam("status", "123").build();
|
||||
ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest",
|
||||
"RestAction", "org.apache.rest.RestAction")
|
||||
.addResultConfig(resultConfig)
|
||||
.build();
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setConfig(actionConfig);
|
||||
// Define result 'success'
|
||||
ResultConfig resultConfig = new ResultConfig.Builder("success",
|
||||
"org.apache.struts2.result.HttpHeaderResult")
|
||||
.addParam("status", "123").build();
|
||||
ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest",
|
||||
"RestAction", "org.apache.rest.RestAction")
|
||||
.addResultConfig(resultConfig)
|
||||
.build();
|
||||
((MockActionProxy)restActionInvocation.getProxy()).setConfig(actionConfig);
|
||||
|
||||
request.setMethod("GET");
|
||||
request.setMethod("GET");
|
||||
|
||||
restActionInvocation.setOgnlUtil(new OgnlUtil(
|
||||
new DefaultOgnlExpressionCacheFactory<>(String.valueOf(10_000), BASIC.toString()),
|
||||
new DefaultOgnlBeanInfoCacheFactory<>(String.valueOf(10_000), BASIC.toString()),
|
||||
new StrutsOgnlGuard()
|
||||
));
|
||||
new DefaultOgnlExpressionCacheFactory<>(String.valueOf(10_000), BASIC.toString()),
|
||||
new DefaultOgnlBeanInfoCacheFactory<>(String.valueOf(10_000), BASIC.toString()),
|
||||
new StrutsOgnlGuard()
|
||||
));
|
||||
restActionInvocation.invoke();
|
||||
|
||||
assertEquals(123, response.getStatus());
|
||||
@@ -264,7 +263,7 @@ public class RestActionInvocationTest extends TestCase {
|
||||
|
||||
|
||||
class RestActionInvocationTester extends RestActionInvocation {
|
||||
RestActionInvocationTester() {
|
||||
RestActionInvocationTester() {
|
||||
super(new HashMap<String, Object>(), true);
|
||||
List<InterceptorMapping> interceptorMappings = new ArrayList<InterceptorMapping>();
|
||||
MockInterceptor mockInterceptor = new MockInterceptor();
|
||||
@@ -274,21 +273,21 @@ public class RestActionInvocationTest extends TestCase {
|
||||
interceptors = interceptorMappings.iterator();
|
||||
MockActionProxy actionProxy = new MockActionProxy();
|
||||
ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest",
|
||||
"RestAction", "org.apache.rest.RestAction").build();
|
||||
"RestAction", "org.apache.rest.RestAction").build();
|
||||
actionProxy.setConfig(actionConfig);
|
||||
proxy = actionProxy;
|
||||
action = new RestAction();
|
||||
setMimeTypeHandlerSelector(new DefaultContentTypeHandlerManager());
|
||||
unknownHandlerManager = new DefaultUnknownHandlerManager();
|
||||
try {
|
||||
XWorkTestCaseHelper.setUp();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
invocationContext = ActionContext.getContext();
|
||||
container = ActionContext.getContext().getContainer();
|
||||
stack = ActionContext.getContext().getValueStack();
|
||||
objectFactory = container.getInstance(ObjectFactory.class);
|
||||
try {
|
||||
XWorkTestCaseHelper.setUp();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
invocationContext = ActionContext.getContext();
|
||||
container = ActionContext.getContext().getContainer();
|
||||
stack = ActionContext.getContext().getValueStack();
|
||||
objectFactory = container.getInstance(ObjectFactory.class);
|
||||
|
||||
}
|
||||
|
||||
@@ -296,13 +295,12 @@ public class RestActionInvocationTest extends TestCase {
|
||||
|
||||
static class RestAction extends RestActionSupport implements ModelDriven<List<String>> {
|
||||
|
||||
List<String> model;
|
||||
List<String> model;
|
||||
|
||||
@StrutsParameter(depth = 1)
|
||||
@Override
|
||||
public List<String> getModel() {
|
||||
return model;
|
||||
}
|
||||
@Override
|
||||
public List<String> getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,8 @@ public class ModelDrivenAction extends ActionSupport implements ModelDriven {
|
||||
/**
|
||||
* @return the model to be pushed onto the ValueStack after the Action itself
|
||||
*/
|
||||
@StrutsParameter(depth = 2)
|
||||
@Override
|
||||
public Object getModel() {
|
||||
public TestBean getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user