This is an automated email from the ASF dual-hosted git repository. kusal pushed a commit to branch WW-5468-modeldriven-strutsparameter-fix in repository https://gitbox.apache.org/repos/asf/struts.git
commit 7cdcd84b838eaea5a3f10af5ea64e6856dc3c27d Author: Kusal Kithul-Godage <g...@kusal.io> AuthorDate: Mon Oct 14 18:59:11 2024 +1100 Merge pull request #1072 from apache/fix/WW-5468-modeldriven-2 WW-5468 Exempt ModelDriven Actions from @StrutsParameter requirement --- .../showcase/modelDriven/ModelDrivenAction.java | 2 +- .../main/java/org/apache/struts2/ModelDriven.java | 6 + .../parameter/ParametersInterceptor.java | 45 ++- .../com/opensymphony/xwork2/ModelDrivenAction.java | 3 +- .../xwork2/ModelDrivenAnnotationAction.java | 3 +- .../interceptor/ModelDrivenInterceptorTest.java | 2 +- .../xwork2/test/ModelDrivenAction2.java | 4 +- .../xwork2/test/ModelDrivenAnnotationAction2.java | 4 +- .../xwork2/test/subtest/NullModelDrivenAction.java | 3 +- .../validator/VisitorValidatorModelAction.java | 5 +- .../parameter/StrutsParameterAnnotationTest.java | 36 +- .../apache/struts2/result/StreamResultTest.java | 6 +- .../beanvalidation/actions/ModelDrivenAction.java | 2 - .../actions/ValidateGroupAction.java | 2 - .../apache/struts2/junit/StrutsRestTestCase.java | 3 +- .../struts2/rest/RestActionInvocationTest.java | 400 ++++++++++----------- .../com/opensymphony/xwork2/ModelDrivenAction.java | 3 +- 17 files changed, 281 insertions(+), 248 deletions(-) diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/modelDriven/ModelDrivenAction.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/modelDriven/ModelDrivenAction.java index 60692b0e6..0fae6c481 100644 --- a/apps/showcase/src/main/java/org/apache/struts2/showcase/modelDriven/ModelDrivenAction.java +++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/modelDriven/ModelDrivenAction.java @@ -42,7 +42,7 @@ public class ModelDrivenAction extends ActionSupport implements ModelDriven { } @Override - public Object getModel() { + public Gangster getModel() { return new Gangster(); } } diff --git a/core/src/main/java/org/apache/struts2/ModelDriven.java b/core/src/main/java/org/apache/struts2/ModelDriven.java index 0704109f1..993a6f5eb 100644 --- a/core/src/main/java/org/apache/struts2/ModelDriven.java +++ b/core/src/main/java/org/apache/struts2/ModelDriven.java @@ -18,6 +18,8 @@ */ package org.apache.struts2; +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(); } diff --git a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java index 239bc6d6c..9f6ff8f53 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java @@ -35,6 +35,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.struts2.ModelDriven; import org.apache.struts2.StrutsConstants; import org.apache.struts2.action.NoParameters; import org.apache.struts2.action.ParameterNameAware; @@ -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); @@ -399,7 +410,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) { @@ -409,8 +420,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); @@ -447,19 +460,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); @@ -468,6 +485,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()); } @@ -629,7 +648,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()); @@ -644,8 +663,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()); } @@ -663,8 +682,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); } @@ -686,8 +705,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); } @@ -757,7 +776,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 { @@ -782,7 +801,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 { diff --git a/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java b/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java index 6ffcad2ff..e300d036b 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java +++ b/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java @@ -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; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAnnotationAction.java b/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAnnotationAction.java index 5549b60b1..f05641732 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAnnotationAction.java +++ b/core/src/test/java/com/opensymphony/xwork2/ModelDrivenAnnotationAction.java @@ -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; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptorTest.java index 65b21c86d..f6513f5f2 100644 --- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptorTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptorTest.java @@ -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() { diff --git a/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAction2.java b/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAction2.java index 0c445860b..5e29e6899 100644 --- a/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAction2.java +++ b/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAction2.java @@ -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; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAnnotationAction2.java b/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAnnotationAction2.java index 7c26dcfab..b2fcdf542 100644 --- a/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAnnotationAction2.java +++ b/core/src/test/java/com/opensymphony/xwork2/test/ModelDrivenAnnotationAction2.java @@ -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; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/test/subtest/NullModelDrivenAction.java b/core/src/test/java/com/opensymphony/xwork2/test/subtest/NullModelDrivenAction.java index be0916d9d..3551a4643 100644 --- a/core/src/test/java/com/opensymphony/xwork2/test/subtest/NullModelDrivenAction.java +++ b/core/src/test/java/com/opensymphony/xwork2/test/subtest/NullModelDrivenAction.java @@ -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; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorModelAction.java b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorModelAction.java index 9f5baeee0..9ec226f98 100644 --- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorModelAction.java +++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorModelAction.java @@ -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(); } } diff --git a/core/src/test/java/org/apache/struts2/interceptor/parameter/StrutsParameterAnnotationTest.java b/core/src/test/java/org/apache/struts2/interceptor/parameter/StrutsParameterAnnotationTest.java index 53fa14717..dfcecddcb 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/parameter/StrutsParameterAnnotationTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/parameter/StrutsParameterAnnotationTest.java @@ -18,8 +18,12 @@ */ 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 com.opensymphony.xwork2.util.ValueStack; import org.apache.commons.lang3.ClassUtils; import org.apache.struts2.dispatcher.HttpParameters; import org.apache.struts2.dispatcher.Parameter; @@ -63,6 +67,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 +85,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 +263,21 @@ public class StrutsParameterAnnotationTest { testParameter(new MethodAction(), "publicStrNotAnnotated", true); } + @Test + public void publicModelPojo() { + ModelAction action = new ModelAction(); + + // Emulate ModelDrivenInterceptor running previously + ValueStack 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)); + } - class FieldAction { + static class FieldAction { @StrutsParameter private String privateStr; @@ -275,7 +293,7 @@ public class StrutsParameterAnnotationTest { public Pojo publicPojoDepthZero; @StrutsParameter(depth = 1) - public Pojo publicPojoDepthOne ; + public Pojo publicPojoDepthOne; @StrutsParameter(depth = 2) public Pojo publicPojoDepthTwo; @@ -290,7 +308,7 @@ public class StrutsParameterAnnotationTest { public Map<String, Pojo> publicPojoMapDepthTwo; } - class MethodAction { + static class MethodAction { @StrutsParameter private void setPrivateStr(String str) { @@ -343,6 +361,14 @@ public class StrutsParameterAnnotationTest { } } - class Pojo { + static class ModelAction implements ModelDriven<Pojo> { + + @Override + public Pojo getModel() { + return new Pojo(); + } + } + + static class Pojo { } } diff --git a/core/src/test/java/org/apache/struts2/result/StreamResultTest.java b/core/src/test/java/org/apache/struts2/result/StreamResultTest.java index 5661db5d3..a02781812 100644 --- a/core/src/test/java/org/apache/struts2/result/StreamResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/StreamResultTest.java @@ -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; diff --git a/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ModelDrivenAction.java b/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ModelDrivenAction.java index 9b32fa24b..3964ed397 100644 --- a/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ModelDrivenAction.java +++ b/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ModelDrivenAction.java @@ -21,7 +21,6 @@ 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 javax.validation.Valid; @@ -30,7 +29,6 @@ public class ModelDrivenAction extends ActionSupport implements ModelDriven<Pers @Valid private final Person model = new Person(); - @StrutsParameter(depth = 2) @Override public Person getModel() { return model; diff --git a/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ValidateGroupAction.java b/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ValidateGroupAction.java index 7d3540551..ff63256dc 100644 --- a/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ValidateGroupAction.java +++ b/plugins/bean-validation/src/test/java/org/apache/struts/beanvalidation/actions/ValidateGroupAction.java @@ -22,7 +22,6 @@ import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import org.apache.struts.beanvalidation.constraints.ValidationGroup; import org.apache.struts.beanvalidation.models.Person; -import org.apache.struts2.interceptor.parameter.StrutsParameter; import javax.validation.Valid; @@ -31,7 +30,6 @@ public class ValidateGroupAction extends ActionSupport implements ModelDriven<Pe @Valid private final Person model = new Person(); - @StrutsParameter(depth = 2) @Override public Person getModel() { return model; diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsRestTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsRestTestCase.java index 80a954f8e..82656edae 100644 --- a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsRestTestCase.java +++ b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsRestTestCase.java @@ -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: [" diff --git a/plugins/rest/src/test/java/org/apache/struts2/rest/RestActionInvocationTest.java b/plugins/rest/src/test/java/org/apache/struts2/rest/RestActionInvocationTest.java index 6f22a583e..432cb4a58 100644 --- a/plugins/rest/src/test/java/org/apache/struts2/rest/RestActionInvocationTest.java +++ b/plugins/rest/src/test/java/org/apache/struts2/rest/RestActionInvocationTest.java @@ -32,7 +32,6 @@ import com.opensymphony.xwork2.ognl.OgnlUtil; import com.opensymphony.xwork2.util.XWorkTestCaseHelper; import junit.framework.TestCase; import org.apache.struts2.ServletActionContext; -import org.apache.struts2.interceptor.parameter.StrutsParameter; import org.apache.struts2.result.HttpHeaderResult; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -47,97 +46,97 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; public class RestActionInvocationTest extends TestCase { - RestActionInvocation restActionInvocation; - MockHttpServletRequest request; - MockHttpServletResponse response; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - 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 { - - 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 HttpHeaderResult(HttpServletResponse.SC_ACCEPTED); - assertEquals(null, restActionInvocation.saveResult(actionConfig, methodResult)); - assertEquals(methodResult, restActionInvocation.createResult()); - - setUp(); - try { - methodResult = new Object(); - restActionInvocation.saveResult(actionConfig, methodResult); - - // ko - assertFalse(true); - - } catch (ConfigurationException c) { - // ok, object not allowed - } - } - - /** - * 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); - - // 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); + RestActionInvocation restActionInvocation; + MockHttpServletRequest request; + MockHttpServletResponse response; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + 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 { + + 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 HttpHeaderResult(HttpServletResponse.SC_ACCEPTED); + assertEquals(null, restActionInvocation.saveResult(actionConfig, methodResult)); + assertEquals(methodResult, restActionInvocation.createResult()); + + setUp(); + try { + methodResult = new Object(); + restActionInvocation.saveResult(actionConfig, methodResult); + + // ko + assertFalse(true); + + } catch (ConfigurationException c) { + // ok, object not allowed + } + } + + /** + * 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); + + // 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); // disable content restriction to GET only model = new ArrayList<String>(); @@ -151,102 +150,100 @@ 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"); + + 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()); + + } + + /** + * 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"); + + 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.processResult(); + assertEquals(123, response.getStatus()); + + } + + public void testNoResult() throws Exception { - request.addHeader("If-None-Match", "123"); - request.setMethod("GET"); + 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>() { - @Override - public int hashCode() { - return 123; - } - }; - model.add("Item"); - restAction.model = model; + try { + restActionInvocation.processResult(); - restActionInvocation.processResult(); - assertEquals(SC_NOT_MODIFIED, response.getStatus()); + // ko + assertFalse(true); + + } catch (ConfigurationException c) { + // ok, no result + } } - /** - * 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"); - - 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.processResult(); - assertEquals(123, response.getStatus()); - - } - - 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"); - - try { - restActionInvocation.processResult(); - - // ko - assertFalse(true); - - } catch (ConfigurationException c) { - // ok, no result - } - - } - - /** - * Test the global execution - * @throws Exception - */ - public void testInvoke() throws Exception { - - // 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); - - request.setMethod("GET"); + /** + * Test the global execution + * @throws Exception + */ + public void testInvoke() throws Exception { + + // 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); + + request.setMethod("GET"); restActionInvocation.setOgnlUtil(new OgnlUtil()); restActionInvocation.invoke(); @@ -256,31 +253,31 @@ public class RestActionInvocationTest extends TestCase { class RestActionInvocationTester extends RestActionInvocation { - RestActionInvocationTester() { - super(new HashMap<String, Object>(), true); - List<InterceptorMapping> interceptorMappings = new ArrayList<InterceptorMapping>(); + RestActionInvocationTester() { + super(new HashMap<>(), true); + List<InterceptorMapping> interceptorMappings = new ArrayList<>(); MockInterceptor mockInterceptor = new MockInterceptor(); mockInterceptor.setFoo("interceptor"); mockInterceptor.setExpectedFoo("interceptor"); interceptorMappings.add(new InterceptorMapping("interceptor", mockInterceptor)); interceptors = interceptorMappings.iterator(); MockActionProxy actionProxy = new MockActionProxy(); - ActionConfig actionConfig = new ActionConfig.Builder("org.apache.rest", - "RestAction", "org.apache.rest.RestAction").build(); + ActionConfig actionConfig = new ActionConfig.Builder( + "org.apache.rest", "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); } @@ -288,13 +285,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; + } } } diff --git a/plugins/spring/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java b/plugins/spring/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java index 6ffcad2ff..e300d036b 100644 --- a/plugins/spring/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java +++ b/plugins/spring/src/test/java/com/opensymphony/xwork2/ModelDrivenAction.java @@ -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; } }