Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,10 @@ public Interceptor buildInterceptor(InterceptorConfig interceptorConfig, Map<Str
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@
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
*/
Expand Down Expand Up @@ -68,7 +77,7 @@ public void setOgnlUtil(OgnlUtil ognlUtil) {

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,33 @@ public void testInvokeWithLazyParams() throws Exception {
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();
Expand Down Expand Up @@ -458,7 +485,7 @@ public void invokeAsyncAction(Callable asyncAction) {

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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,36 @@
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();
}
Expand Down
Loading