WW-4687 Allows define interceptors with dynamic parameters

This commit is contained in:
Lukasz Lenart
2017-01-09 10:07:20 +01:00
parent b9c05a7181
commit 2b12f06b01
9 changed files with 191 additions and 12 deletions
@@ -21,7 +21,9 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -67,6 +69,7 @@ public class DefaultActionInvocation implements ActionInvocation {
protected Container container;
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil;
protected WithLazyParams.LazyParamInjector lazyParamInjector;
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
this.extraContext = extraContext;
@@ -233,11 +236,15 @@ public class DefaultActionInvocation implements ActionInvocation {
}
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
final InterceptorMapping interceptorMapping = interceptors.next();
String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
Interceptor interceptor = interceptorMapping.getInterceptor();
if (interceptor instanceof WithLazyParams) {
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
}
resultCode = interceptor.intercept(DefaultActionInvocation.this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
@@ -400,6 +407,13 @@ public class DefaultActionInvocation implements ActionInvocation {
invocationContext.setName(proxy.getActionName());
createInterceptors(proxy);
prepareLazyParamInjector(invocationContext.getValueStack());
}
protected void prepareLazyParamInjector(ValueStack valueStack) {
lazyParamInjector = new WithLazyParams.LazyParamInjector(valueStack);
container.inject(lazyParamInjector);
}
protected void createInterceptors(ActionProxy proxy) {
@@ -19,6 +19,8 @@ package com.opensymphony.xwork2.config.entities;
import com.opensymphony.xwork2.interceptor.Interceptor;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* <code>InterceptorMapping</code>
@@ -30,10 +32,16 @@ public class InterceptorMapping implements Serializable {
private String name;
private Interceptor interceptor;
private final Map<String, String> params;
public InterceptorMapping(String name, Interceptor interceptor) {
this(name, interceptor, new HashMap<String, String>());
}
public InterceptorMapping(String name, Interceptor interceptor, Map<String, String> params) {
this.name = name;
this.interceptor = interceptor;
this.params = params;
}
public String getName() {
@@ -44,6 +52,10 @@ public class InterceptorMapping implements Serializable {
return interceptor;
}
public Map<String, String> getParams() {
return params;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -65,7 +77,7 @@ public class InterceptorMapping implements Serializable {
@Override
public String toString() {
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + ']';
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + "] with params [" + params + "]" ;
}
}
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.util.location.Location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -43,7 +44,6 @@ public class InterceptorBuilder {
private static final Logger LOG = LogManager.getLogger(InterceptorBuilder.class);
/**
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig (InterceptorMapping object).
*
@@ -67,13 +67,11 @@ public class InterceptorBuilder {
InterceptorConfig config = (InterceptorConfig) referencedConfig;
Interceptor inter;
try {
inter = objectFactory.buildInterceptor(config, refParams);
result.add(new InterceptorMapping(refName, inter));
result.add(new InterceptorMapping(refName, inter, refParams));
} catch (ConfigurationException ex) {
LOG.warn("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
config.getClassName(), ex.getLocation(), config.getName());
LOG.error("Unable to load config class {}", config.getClassName(), ex);
LOG.warn(new ParameterizedMessage("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
config.getClassName(), ex.getLocation(), config.getName()), ex);
}
} else if (referencedConfig instanceof InterceptorStackConfig) {
@@ -5,7 +5,10 @@ import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
@@ -15,6 +18,8 @@ import java.util.Map;
*/
public class DefaultInterceptorFactory implements InterceptorFactory {
private static final Logger LOG = LogManager.getLogger(DefaultInterceptorFactory.class);
private ObjectFactory objectFactory;
private ReflectionProvider reflectionProvider;
@@ -40,7 +45,12 @@ public class DefaultInterceptorFactory implements InterceptorFactory {
try {
// interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
Object o = objectFactory.buildBean(interceptorClassName, null);
reflectionProvider.setProperties(params, o);
if (o instanceof WithLazyParams) {
LOG.debug("Interceptor {} is marked with interface {} and params will be set during action invocation",
interceptorClassName, WithLazyParams.class.getName());
} else {
reflectionProvider.setProperties(params, o);
}
if (o instanceof Interceptor) {
Interceptor interceptor = (Interceptor) o;
@@ -0,0 +1,63 @@
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.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.
*
* @since 2.5.9
*/
public interface WithLazyParams {
class LazyParamInjector {
protected OgnlUtil ognlUtil;
protected TextParser textParser;
protected ReflectionProvider reflectionProvider;
private final TextParseUtil.ParsedValueEvaluator valueEvaluator;
public LazyParamInjector(final ValueStack valueStack) {
valueEvaluator = new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(String parsedValue) {
return valueStack.findValue(parsedValue); // no asType !!!
}
};
}
@Inject
public void setTextParser(TextParser textParser) {
this.textParser = textParser;
}
@Inject
public void setReflectionProvider(ReflectionProvider reflectionProvider) {
this.reflectionProvider = reflectionProvider;
}
@Inject
public void setOgnlUtil(OgnlUtil ognlUtil) {
this.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);
ognlUtil.setProperty(entry.getKey(), paramValue, interceptor, invocationContext.getContextMap());
}
return interceptor;
}
}
}
@@ -3,16 +3,20 @@ package com.opensymphony.xwork2;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.mock.MockContainer;
import com.opensymphony.xwork2.mock.MockInterceptor;
import com.opensymphony.xwork2.mock.MockLazyInterceptor;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import org.apache.struts2.dispatcher.HttpParameters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -298,6 +302,36 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
assertEquals("success", result);
}
public void testInvokeWithLazyParams() throws Exception {
HashMap<String, Object> params = new HashMap<>();
params.put("blah", "this is blah");
HashMap<String, Object> extraContext = new HashMap<>();
extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
DefaultActionInvocation defaultActionInvocation = new DefaultActionInvocation(extraContext, true);
container.inject(defaultActionInvocation);
ActionProxy actionProxy = actionProxyFactory.createActionProxy( "", "LazyFoo", null, extraContext);
defaultActionInvocation.init(actionProxy);
defaultActionInvocation.invoke();
SimpleAction action = (SimpleAction) defaultActionInvocation.getAction();
assertEquals("this is blah", action.getBlah());
assertEquals("this is blah", action.getName());
}
@Override
protected void setUp() throws Exception {
super.setUp();
// ensure we're using the default configuration, not simple config
XmlConfigurationProvider configurationProvider = new XmlConfigurationProvider("xwork-sample.xml");
container.inject(configurationProvider);
loadConfigurationProviders(configurationProvider);
}
}
class DefaultActionInvocationTester extends DefaultActionInvocation {
@@ -0,0 +1,39 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.mock;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.SimpleAction;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
import org.junit.Assert;
public class MockLazyInterceptor extends AbstractInterceptor implements WithLazyParams {
private String foo = "";
public void setFoo(String foo) {
this.foo = foo;
}
public String intercept(ActionInvocation invocation) throws Exception {
if (invocation.getAction() instanceof SimpleAction) {
((SimpleAction) invocation.getAction()).setName(foo);
}
return invocation.invoke();
}
}
+8
View File
@@ -26,6 +26,14 @@
<interceptor-ref name="defaultStack"/>
</action>
<action name="LazyFoo" class="com.opensymphony.xwork2.SimpleAction">
<result name="error" type="void" />
<interceptor-ref name="params"/>
<interceptor-ref name="lazy">
<param name="foo">${blah}</param>
</interceptor-ref>
</action>
<action name="WildCard" class="com.opensymphony.xwork2.SimpleAction">
<param name="foo">17</param>
<param name="bar">23</param>
@@ -19,10 +19,11 @@
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="validation" class="com.opensymphony.xwork2.validator.ValidationInterceptor"/>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="test" class="com.opensymphony.xwork2.mock.MockInterceptor">
<param name="foo">expectedFoo</param>
</interceptor>
<interceptor name="lazy" class="com.opensymphony.xwork2.mock.MockLazyInterceptor"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="staticParams"/>