mirror of
https://github.com/apache/struts.git
synced 2026-08-05 14:47:09 +00:00
fix(core): enable WithLazyParams interceptor configuration in stacks (#1414)
Resolves WW-5586
Previously, interceptors implementing WithLazyParams could not be
configured via interceptor stack parameters because DefaultInterceptorFactory
skipped property setting entirely for these interceptors.
Changes:
- DefaultInterceptorFactory: Always set properties for all interceptors
- WithLazyParams: Updated JavaDoc to clarify dual initialization
- Added test for stack parameter configuration with WithLazyParams
Expression parameters are re-evaluated at invocation time via LazyParamInjector,
while static parameters remain unchanged. This enables both static configuration
(e.g., allowedTypes) and dynamic expressions (e.g., ${maxUploadSize}) in the
same interceptor stack reference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -68,11 +68,10 @@ public class DefaultInterceptorFactory implements InterceptorFactory {
|
||||
throw new ConfigurationException("Class [" + interceptorClassName + "] does not implement Interceptor", interceptorConfig);
|
||||
}
|
||||
|
||||
reflectionProvider.setProperties(params, interceptor);
|
||||
if (interceptor instanceof WithLazyParams) {
|
||||
LOG.debug("Interceptor {} is marked with interface {} and params will be set during action invocation",
|
||||
LOG.debug("Interceptor {} implements {} - expression parameters will be re-evaluated during action invocation",
|
||||
interceptorClassName, WithLazyParams.class.getName());
|
||||
} else {
|
||||
reflectionProvider.setProperties(params, interceptor);
|
||||
}
|
||||
|
||||
interceptor.init();
|
||||
|
||||
@@ -29,10 +29,19 @@ import org.apache.struts2.util.reflection.ReflectionProvider;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interceptors marked with this interface won't be fully initialised during initialisation.
|
||||
* Appropriated params will be injected just before usage of the interceptor.
|
||||
*
|
||||
* Please be aware that in such case {@link Interceptor#init()} method must be prepared for this.
|
||||
* Interceptors marked with this interface support dynamic parameter evaluation at action invocation time.
|
||||
* Parameters are set during interceptor creation (factory time), then re-evaluated during each action
|
||||
* invocation to resolve expressions like ${someValue}.
|
||||
* <p>
|
||||
* This enables both:
|
||||
* <ul>
|
||||
* <li>Static configuration in interceptor stacks (e.g., allowedTypes="image/png,image/jpeg")</li>
|
||||
* <li>Dynamic expressions evaluated per-request (e.g., maximumSize="${maxUploadSize}")</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The {@link Interceptor#init()} method is called after initial parameter setting, so interceptors
|
||||
* can rely on configured values during initialization. Expression parameters (containing ${...})
|
||||
* are re-evaluated at invocation time via {@link LazyParamInjector}.
|
||||
*
|
||||
* @since 2.5.9
|
||||
*/
|
||||
@@ -68,7 +77,7 @@ public interface WithLazyParams {
|
||||
|
||||
public Interceptor injectParams(Interceptor interceptor, Map<String, String> params, ActionContext invocationContext) {
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
Object paramValue = textParser.evaluate(new char[]{ '$' }, entry.getValue(), valueEvaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
Object paramValue = textParser.evaluate(new char[]{'$'}, entry.getValue(), valueEvaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
ognlUtil.setProperty(entry.getKey(), paramValue, interceptor, invocationContext.getContextMap());
|
||||
}
|
||||
return interceptor;
|
||||
|
||||
@@ -384,6 +384,33 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
|
||||
assertEquals("this is blah", action.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for WW-5586: WithLazyParams interceptors can be configured in interceptor stacks
|
||||
* with both static parameters and dynamic expression parameters.
|
||||
*/
|
||||
public void testInvokeWithLazyParamsStackConfiguration() throws Exception {
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("blah", "dynamic value");
|
||||
|
||||
ActionContext extraContext = ActionContext.of()
|
||||
.withParameters(HttpParameters.create(params).build());
|
||||
|
||||
DefaultActionInvocation defaultActionInvocation = new DefaultActionInvocation(extraContext.getContextMap(), true);
|
||||
container.inject(defaultActionInvocation);
|
||||
|
||||
ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "LazyFooWithStackParams", null, extraContext.getContextMap());
|
||||
defaultActionInvocation.init(actionProxy);
|
||||
defaultActionInvocation.invoke();
|
||||
|
||||
SimpleAction action = (SimpleAction) defaultActionInvocation.getAction();
|
||||
|
||||
// Verify expression parameter is evaluated at invocation time
|
||||
assertEquals("dynamic value", action.getName());
|
||||
|
||||
// Verify static parameter is set and not evaluated as expression
|
||||
assertEquals("static value", action.getBlah());
|
||||
}
|
||||
|
||||
public void testInvokeWithAsyncManager() throws Exception {
|
||||
DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap<>(), false);
|
||||
dai.stack = container.getInstance(ValueStackFactory.class).createValueStack();
|
||||
@@ -458,7 +485,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
|
||||
|
||||
public void testActionEventListener() throws Exception {
|
||||
ActionProxy actionProxy = actionProxyFactory.createActionProxy("",
|
||||
"ExceptionFoo", "exceptionMethod", new HashMap<>());
|
||||
"ExceptionFoo", "exceptionMethod", new HashMap<>());
|
||||
DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation();
|
||||
|
||||
SimpleActionEventListener actionEventListener = new SimpleActionEventListener("prepared", "exceptionHandled");
|
||||
|
||||
@@ -21,20 +21,36 @@ package org.apache.struts2.mock;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.SimpleAction;
|
||||
import org.apache.struts2.interceptor.AbstractInterceptor;
|
||||
import org.apache.struts2.interceptor.Interceptor;
|
||||
import org.apache.struts2.interceptor.WithLazyParams;
|
||||
|
||||
public class MockLazyInterceptor extends AbstractInterceptor implements WithLazyParams {
|
||||
|
||||
private String foo = "";
|
||||
private String bar = "";
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
if (invocation.getAction() instanceof SimpleAction) {
|
||||
((SimpleAction) invocation.getAction()).setName(foo);
|
||||
// Only set blah if bar is configured (not empty)
|
||||
if (bar != null && !bar.isEmpty()) {
|
||||
((SimpleAction) invocation.getAction()).setBlah(bar);
|
||||
}
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</action>
|
||||
|
||||
<action name="ExceptionFoo" class="org.apache.struts2.SimpleAction" method="exceptionMethod">
|
||||
<result name="exceptionHandled" type="void" />
|
||||
<result name="exceptionHandled" type="void"/>
|
||||
<interceptor-ref name="debugStack"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
@@ -56,77 +56,86 @@
|
||||
</action>
|
||||
|
||||
<action name="LazyFoo" class="org.apache.struts2.SimpleAction">
|
||||
<result name="error" type="void" />
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="lazy">
|
||||
<param name="foo">${blah}</param>
|
||||
</interceptor-ref>
|
||||
<result name="error" type="void"/>
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="lazy">
|
||||
<param name="foo">${blah}</param>
|
||||
</interceptor-ref>
|
||||
</action>
|
||||
|
||||
<action name="LazyFooWithStackParams" class="org.apache.struts2.SimpleAction">
|
||||
<result name="error" type="void"/>
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="lazy">
|
||||
<param name="foo">${blah}</param>
|
||||
<param name="bar">static value</param>
|
||||
</interceptor-ref>
|
||||
</action>
|
||||
|
||||
<action name="WildCard" class="org.apache.struts2.SimpleAction">
|
||||
<param name="foo">17</param>
|
||||
<param name="bar">23</param>
|
||||
<result name="success" type="void" />
|
||||
<result name="*" type="mock" />
|
||||
<result name="success" type="void"/>
|
||||
<result name="*" type="mock"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
|
||||
<action name="WildCardInput" class="org.apache.struts2.SimpleAction" method="input">
|
||||
<param name="foo">17</param>
|
||||
<param name="bar">23</param>
|
||||
<result name="success" type="void" />
|
||||
<result name="*" type="mock" />
|
||||
<result name="success" type="void"/>
|
||||
<result name="*" type="mock"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
|
||||
<action name="WildCardError" class="org.apache.struts2.SimpleAction">
|
||||
<result name="success" type="void" />
|
||||
<result name="*" type="mock" />
|
||||
<result name="success" type="void"/>
|
||||
<result name="*" type="mock"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
|
||||
<action name="WildCard/*/*" class="org.apache.struts2.{1}Action"
|
||||
method="{2}">
|
||||
<action name="WildCard/*/*" class="org.apache.struts2.{1}Action"
|
||||
method="{2}">
|
||||
<param name="foo">{1}</param>
|
||||
<param name="bar">{2}</param>
|
||||
<result name="success" type="void" />
|
||||
<result name="success" type="void"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
|
||||
<action name="aliasTest" class="org.apache.struts2.SimpleAction">
|
||||
<param name="aliases">#{ "aliasSource" : "aliasDest", "bar":"baz", "notExisting":"blah" }</param>
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="alias"/>
|
||||
<result name="success" type="mock" />
|
||||
<param name="aliases">#{ "aliasSource" : "aliasDest", "bar":"baz", "notExisting":"blah" }</param>
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="alias"/>
|
||||
<result name="success" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="dynamicAliasTest" class="org.apache.struts2.SimpleAction">
|
||||
<param name="aliases">#{ #parameters['name'] : #parameters['value'] }</param>
|
||||
<interceptor-ref name="params"/>
|
||||
<interceptor-ref name="alias"/>
|
||||
<result name="success" type="mock" />
|
||||
<result name="success" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="packagelessAction" class="PackagelessAction">
|
||||
</action>
|
||||
|
||||
<action name="Bar" class="org.apache.struts2.SimpleAction">
|
||||
<param name="foo">17</param>
|
||||
<param name="foo">17</param>
|
||||
<param name="bar">23</param>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="MyBean" class="org.apache.struts2.util.MyBeanAction">
|
||||
<interceptor-ref name="debugStack"/>
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<result name="success" type="mock" />
|
||||
<result name="success" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="TestInterceptorParam" class="org.apache.struts2.SimpleAction">
|
||||
<interceptor-ref name="test">
|
||||
<param name="expectedFoo">expectedFoo</param>
|
||||
</interceptor-ref>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="TestInterceptorParamOverride" class="org.apache.struts2.SimpleAction">
|
||||
@@ -134,18 +143,18 @@
|
||||
<param name="foo">foo123</param>
|
||||
<param name="expectedFoo">foo123</param>
|
||||
</interceptor-ref>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="TestModelDrivenValidation" class="org.apache.struts2.ModelDrivenAction">
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="validation"/>
|
||||
<result name="success" type="mock" />
|
||||
<result name="success" type="mock"/>
|
||||
</action>
|
||||
|
||||
<!-- chain resursion detection -->
|
||||
<action name="InfiniteRecursionChain" class="org.apache.struts2.ActionSupport">
|
||||
<result name="success" type="chain">InfiniteRecursionChain</result>
|
||||
<result name="success" type="chain">InfiniteRecursionChain</result>
|
||||
</action>
|
||||
<action name="chain_with_namespace" class="org.apache.struts2.SimpleAction">
|
||||
<result name="error" type="chain">
|
||||
@@ -163,9 +172,9 @@
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
</action>
|
||||
|
||||
</package>
|
||||
</package>
|
||||
|
||||
<package name="bar" extends="default" namespace="/foo/bar">
|
||||
<package name="bar" extends="default" namespace="/foo/bar">
|
||||
<interceptors>
|
||||
<interceptor-stack name="barDefaultStack">
|
||||
<interceptor-ref name="debugStack"/>
|
||||
@@ -179,14 +188,14 @@
|
||||
|
||||
<action name="Bar" class="org.apache.struts2.SimpleAction">
|
||||
<interceptor-ref name="barDefaultStack"/>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="TestInterceptorParamInheritance" class="org.apache.struts2.SimpleAction">
|
||||
<interceptor-ref name="test">
|
||||
<param name="expectedFoo">expectedFoo</param>
|
||||
</interceptor-ref>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
|
||||
<action name="TestInterceptorParamInehritanceOverride" class="org.apache.struts2.SimpleAction">
|
||||
@@ -194,7 +203,7 @@
|
||||
<param name="foo">foo123</param>
|
||||
<param name="expectedFoo">foo123</param>
|
||||
</interceptor-ref>
|
||||
<result name="error" type="mock" />
|
||||
<result name="error" type="mock"/>
|
||||
</action>
|
||||
</package>
|
||||
|
||||
@@ -210,7 +219,7 @@
|
||||
<result name="error" type="chain">
|
||||
<param name="actionName">bar</param>
|
||||
</result>
|
||||
<result name="success" type="mock" />
|
||||
<result name="success" type="mock"/>
|
||||
<interceptor-ref name="staticParams"/>
|
||||
</action>
|
||||
<action name="myCommand" class="org.apache.struts2.SimpleAction" method="commandMethod">
|
||||
@@ -222,10 +231,10 @@
|
||||
<interceptor-ref name="logger"/>
|
||||
</action>
|
||||
<action name="doMethodTest" class="org.apache.struts2.SimpleAction" method="input">
|
||||
<result name="input" type="mock" />
|
||||
<result name="input" type="mock"/>
|
||||
</action>
|
||||
<action name="unknownMethodTest" class="org.apache.struts2.SimpleAction">
|
||||
<result name="found" type="mock" />
|
||||
<result name="found" type="mock"/>
|
||||
</action>
|
||||
<action name="resultAction" class="org.apache.struts2.SimpleAction" method="resultAction">
|
||||
<param name="bar">456</param>
|
||||
@@ -238,7 +247,7 @@
|
||||
|
||||
<package name="wildcardNamespaces" extends="default" namespace="/animals/*">
|
||||
<action name="commandTest" class="org.apache.struts2.SimpleAction">
|
||||
<result name="success" type="mock" />
|
||||
<result name="success" type="mock"/>
|
||||
<interceptor-ref name="staticParams"/>
|
||||
</action>
|
||||
</package>
|
||||
@@ -253,47 +262,47 @@
|
||||
</package>
|
||||
|
||||
|
||||
<package name="Abstract-crud" extends="default">
|
||||
<!-- edit is often used as the create/view -->
|
||||
<default-class-ref class="org.apache.struts2.SimpleAction"/>
|
||||
<action name="edit" >
|
||||
<result name="input" type="mock">edit.vm</result>
|
||||
<result name="success" type="mock">edit.vm</result>
|
||||
<result name="error" type="mock">edit.vm</result>
|
||||
</action>
|
||||
<action name="save" >
|
||||
<result name="input" type="mock">edit.vm</result>
|
||||
<result name="success" type="chain">list</result>
|
||||
<result name="error" type="mock">edit.vm</result>
|
||||
<result name="cancel" type="mock">list.action</result>
|
||||
</action>
|
||||
<action name="list">
|
||||
<result name="success" type="mock">list</result>
|
||||
</action>
|
||||
<action name="delete">
|
||||
<result name="success" type="mock">list</result>
|
||||
</action>
|
||||
<package name="Abstract-crud" extends="default">
|
||||
<!-- edit is often used as the create/view -->
|
||||
<default-class-ref class="org.apache.struts2.SimpleAction"/>
|
||||
<action name="edit">
|
||||
<result name="input" type="mock">edit.vm</result>
|
||||
<result name="success" type="mock">edit.vm</result>
|
||||
<result name="error" type="mock">edit.vm</result>
|
||||
</action>
|
||||
<action name="save">
|
||||
<result name="input" type="mock">edit.vm</result>
|
||||
<result name="success" type="chain">list</result>
|
||||
<result name="error" type="mock">edit.vm</result>
|
||||
<result name="cancel" type="mock">list.action</result>
|
||||
</action>
|
||||
<action name="list">
|
||||
<result name="success" type="mock">list</result>
|
||||
</action>
|
||||
<action name="delete">
|
||||
<result name="success" type="mock">list</result>
|
||||
</action>
|
||||
|
||||
</package>
|
||||
</package>
|
||||
|
||||
<package name="Example" extends="Abstract-crud" namespace="/example">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction" />
|
||||
</package>
|
||||
<package name="Example" extends="Abstract-crud" namespace="/example">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction"/>
|
||||
</package>
|
||||
|
||||
<package name="Example2" extends="Abstract-crud" namespace="/example2">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction" />
|
||||
<action name="override">
|
||||
<result name="success" type="mock">somethingelse.vm</result>
|
||||
</action>
|
||||
</package>
|
||||
<package name="Example2" extends="Abstract-crud" namespace="/example2">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction"/>
|
||||
<action name="override">
|
||||
<result name="success" type="mock">somethingelse.vm</result>
|
||||
</action>
|
||||
</package>
|
||||
|
||||
<package name="SubItem" extends="Abstract-crud" namespace="/example2/subItem">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction" />
|
||||
</package>
|
||||
<package name="SubItem" extends="Abstract-crud" namespace="/example2/subItem">
|
||||
<default-class-ref class="org.apache.struts2.ModelDrivenAction"/>
|
||||
</package>
|
||||
|
||||
<package name="Example3" extends="Abstract-crud" namespace="/example3">
|
||||
<!-- default-class-ref is expected to be inherited -->
|
||||
</package>
|
||||
<package name="Example3" extends="Abstract-crud" namespace="/example3">
|
||||
<!-- default-class-ref is expected to be inherited -->
|
||||
</package>
|
||||
|
||||
<include file="includeTest.xml"/>
|
||||
</struts>
|
||||
|
||||
Reference in New Issue
Block a user