mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
WW-4105 Unwraps Spring proxy in actions chain
This commit is contained in:
@@ -20,6 +20,7 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.Unchainable;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.spring.SpringUtils;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
@@ -161,7 +162,11 @@ public class ChainingInterceptor extends AbstractInterceptor {
|
||||
Map<String, Object> ctxMap = invocation.getInvocationContext().getContextMap();
|
||||
for (Object object : list) {
|
||||
if (shouldCopy(object)) {
|
||||
reflectionProvider.copy(object, invocation.getAction(), ctxMap, prepareExcludes(), includes);
|
||||
Object action = invocation.getAction();
|
||||
if(SpringUtils.isAopProxy(action)) {
|
||||
action = SpringUtils.getUltimateTargetObject(action);
|
||||
}
|
||||
reflectionProvider.copy(object, action, ctxMap, prepareExcludes(), includes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2017 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.spring;
|
||||
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* <code>SpringUtils</code>
|
||||
* <p>
|
||||
* Various utility methods dealing with spring framework
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public class SpringUtils {
|
||||
/**
|
||||
* Get the ultimate <em>target</em> object of the supplied {@code candidate}
|
||||
* object, unwrapping not only a top-level proxy but also any number of
|
||||
* nested proxies.
|
||||
* <p>If the supplied {@code candidate} is a Spring proxy, the ultimate target of all
|
||||
* nested proxies will be returned; otherwise, the {@code candidate}
|
||||
* will be returned <em>as is</em>.
|
||||
* @param candidate the instance to check (potentially a Spring AOP proxy;
|
||||
* never {@code null})
|
||||
* @return the target object or the {@code candidate} (never {@code null})
|
||||
* @throws IllegalStateException if an error occurs while unwrapping a proxy
|
||||
*/
|
||||
public static <T> T getUltimateTargetObject(Object candidate) {
|
||||
try {
|
||||
if (isAopProxy(candidate) &&
|
||||
implementsInterface(candidate.getClass(), "org.springframework.aop.framework.Advised")) {
|
||||
Object targetSource = MethodUtils.invokeMethod(candidate, "getTargetSource");
|
||||
Object target = MethodUtils.invokeMethod(targetSource, "getTarget");
|
||||
return getUltimateTargetObject(target);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to unwrap proxied object", ex);
|
||||
}
|
||||
return (T) candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is a Spring proxy.
|
||||
* @param object the object to check
|
||||
*/
|
||||
public static boolean isAopProxy(Object object) {
|
||||
Class<?> clazz = object.getClass();
|
||||
return (implementsInterface(clazz, "org.springframework.aop.SpringProxy") &&
|
||||
(Proxy.isProxyClass(clazz) || isCglibProxyClass(clazz)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the specified class is a CGLIB-generated class.
|
||||
* @param clazz the class to check
|
||||
*/
|
||||
private static boolean isCglibProxyClass(Class<?> clazz) {
|
||||
return (clazz != null && clazz.getName().contains("$$"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given class implements an interface with a given class name.
|
||||
* @param clazz the class to check
|
||||
* @param ifaceClassName the interface class name to check
|
||||
*/
|
||||
private static boolean implementsInterface(Class<?> clazz, String ifaceClassName) {
|
||||
try {
|
||||
Class ifaceClass = ClassLoaderUtil.loadClass(ifaceClassName, SpringUtils.class);
|
||||
return ifaceClass.isAssignableFrom(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
public class TestSubBean extends TestBean {
|
||||
|
||||
private String issueId;
|
||||
|
||||
public String getIssueId() {
|
||||
return issueId;
|
||||
}
|
||||
|
||||
public void setIssueId(String issueId) {
|
||||
this.issueId = issueId;
|
||||
}
|
||||
}
|
||||
@@ -77,4 +77,21 @@ public class ActionsFromSpringTest extends XWorkTestCase {
|
||||
assertTrue(springResult.isInitialize());
|
||||
assertNotNull(springResult.getStringParameter());
|
||||
}
|
||||
|
||||
public void testChainingProxiedActions() throws Exception {
|
||||
ActionProxy proxy = actionProxyFactory.createActionProxy(null, "chainedAOPedTestBeanAction",
|
||||
null, null);
|
||||
|
||||
proxy.execute();
|
||||
|
||||
TestSubBean chaintoAOPedAction = (TestSubBean) appContext.getBean("pointcutted-test-sub-bean");
|
||||
TestSubBean aspectState = (TestSubBean) appContext.getBean("aspected-test-sub-bean");
|
||||
|
||||
assertEquals(1, chaintoAOPedAction.getCount()); //check if chain
|
||||
assertEquals("WW-4105", chaintoAOPedAction.getName());
|
||||
assertNotNull(aspectState.getIssueId()); //and AOP proxied actions
|
||||
assertNotNull(aspectState.getName());
|
||||
assertEquals(aspectState.getName(), aspectState.getIssueId());
|
||||
assertEquals("WW-4105", aspectState.getIssueId()); //work together without any problem
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2017 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.spring;
|
||||
|
||||
import com.opensymphony.xwork2.*;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Test various utility methods dealing with spring framework.
|
||||
*
|
||||
*/
|
||||
public class SpringUtilsTest extends XWorkTestCase {
|
||||
private ApplicationContext appContext;
|
||||
|
||||
@Override public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
// Set up XWork
|
||||
XmlConfigurationProvider provider = new XmlConfigurationProvider("com/opensymphony/xwork2/spring/actionContext-xwork.xml");
|
||||
container.inject(provider);
|
||||
loadConfigurationProviders(provider);
|
||||
appContext = ((SpringObjectFactory)container.getInstance(ObjectFactory.class)).appContext;
|
||||
}
|
||||
|
||||
public void testIsAopProxy() throws Exception {
|
||||
Object simpleAction = appContext.getBean("simple-action");
|
||||
assertFalse(SpringUtils.isAopProxy(simpleAction));
|
||||
|
||||
Object proxiedAction = appContext.getBean("proxied-action");
|
||||
assertTrue(SpringUtils.isAopProxy(proxiedAction));
|
||||
|
||||
Object autoProxiedAction = appContext.getBean("auto-proxied-action");
|
||||
assertTrue(SpringUtils.isAopProxy(autoProxiedAction));
|
||||
|
||||
Object pointcuttedTestBean = appContext.getBean("pointcutted-test-bean");
|
||||
assertTrue(SpringUtils.isAopProxy(pointcuttedTestBean));
|
||||
|
||||
Object pointcuttedTestSubBean = appContext.getBean("pointcutted-test-sub-bean");
|
||||
assertTrue(SpringUtils.isAopProxy(pointcuttedTestSubBean));
|
||||
|
||||
Object aspectedTestSubBean = appContext.getBean("aspected-test-sub-bean");
|
||||
assertFalse(SpringUtils.isAopProxy(aspectedTestSubBean));
|
||||
}
|
||||
|
||||
public void testGetUltimateTargetObject() throws Exception {
|
||||
Object simpleAction = appContext.getBean("simple-action");
|
||||
Object simpleActionUltimateTargetObject = SpringUtils.getUltimateTargetObject(simpleAction);
|
||||
assertEquals(simpleAction, simpleActionUltimateTargetObject);
|
||||
|
||||
Object proxiedAction = appContext.getBean("proxied-action");
|
||||
Object proxiedActionUltimateTargetObject = SpringUtils.getUltimateTargetObject(proxiedAction);
|
||||
assertEquals(SimpleAction.class, proxiedActionUltimateTargetObject.getClass());
|
||||
|
||||
Object autoProxiedAction = appContext.getBean("auto-proxied-action");
|
||||
Object autoProxiedActionUltimateTargetObject = SpringUtils.getUltimateTargetObject(autoProxiedAction);
|
||||
assertEquals(SimpleAction.class, autoProxiedActionUltimateTargetObject.getClass());
|
||||
|
||||
Object pointcuttedTestBean = appContext.getBean("pointcutted-test-bean");
|
||||
Object pointcuttedTestBeanUltimateTargetObject = SpringUtils.getUltimateTargetObject(pointcuttedTestBean);
|
||||
assertEquals(TestBean.class, pointcuttedTestBeanUltimateTargetObject.getClass());
|
||||
|
||||
Object pointcuttedTestSubBean = appContext.getBean("pointcutted-test-sub-bean");
|
||||
Object pointcuttedTestSubBeanUltimateTargetObject = SpringUtils.getUltimateTargetObject(pointcuttedTestSubBean);
|
||||
assertEquals(TestSubBean.class, pointcuttedTestSubBeanUltimateTargetObject.getClass());
|
||||
|
||||
Object aspectedTestSubBean = appContext.getBean("aspected-test-sub-bean");
|
||||
Object aspectedTestSubBeanUltimateTargetObject = SpringUtils.getUltimateTargetObject(aspectedTestSubBean);
|
||||
assertEquals(aspectedTestSubBean, aspectedTestSubBeanUltimateTargetObject);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/aop
|
||||
http://www.springframework.org/schema/aop/spring-aop.xsd">
|
||||
|
||||
<bean id="simple-action" class="com.opensymphony.xwork2.SimpleAction" scope="prototype"/>
|
||||
|
||||
@@ -40,4 +43,25 @@
|
||||
<bean id="springResult" class="com.opensymphony.xwork2.spring.SpringResult" init-method="initialize">
|
||||
<property name="stringParameter" value="my string"/>
|
||||
</bean>
|
||||
|
||||
<bean id="pointcutted-test-bean" class="com.opensymphony.xwork2.TestBean">
|
||||
<property name="name"><value>WW-4105</value></property>
|
||||
<property name="count"><value>1</value></property>
|
||||
</bean>
|
||||
<bean id="pointcutted-test-sub-bean" class="com.opensymphony.xwork2.TestSubBean">
|
||||
<property name="issueId"><value>WW-4105</value></property>
|
||||
</bean>
|
||||
<bean id="aspected-test-sub-bean" class="com.opensymphony.xwork2.TestSubBean" />
|
||||
<aop:config>
|
||||
<aop:aspect id="myAspect" ref="aspected-test-sub-bean">
|
||||
<aop:pointcut id="testBeanGetName"
|
||||
expression="execution(String com.opensymphony.xwork2.TestBean.getName()) and bean(pointcutted-test-bean)" />
|
||||
<aop:after-returning pointcut-ref="testBeanGetName"
|
||||
method="setIssueId" returning="issueId" />
|
||||
<aop:pointcut id="testSubBeanGetIssueId"
|
||||
expression="execution(String com.opensymphony.xwork2.TestSubBean.getIssueId()) and bean(pointcutted-test-sub-bean)" />
|
||||
<aop:after-returning pointcut-ref="testSubBeanGetIssueId"
|
||||
method="setName" returning="name" />
|
||||
</aop:aspect>
|
||||
</aop:config>
|
||||
</beans>
|
||||
|
||||
@@ -6,8 +6,15 @@
|
||||
<result-types>
|
||||
<result-type name="null" class="com.opensymphony.xwork2.mock.MockResult" default="true"/>
|
||||
<result-type name="springResult" class="springResult" />
|
||||
<result-type name="chain"
|
||||
class="com.opensymphony.xwork2.ActionChainResult" />
|
||||
</result-types>
|
||||
|
||||
<interceptors>
|
||||
<interceptor name="chain"
|
||||
class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"></interceptor>
|
||||
</interceptors>
|
||||
|
||||
<action name="simpleAction" class="simple-action"/>
|
||||
|
||||
<action name="dependencyAction" class="dependency-action"/>
|
||||
@@ -19,5 +26,17 @@
|
||||
<action name="simpleActionSpringResult" class="simple-action">
|
||||
<result name="error" type="springResult"/>
|
||||
</action>
|
||||
|
||||
<action name="chainedAOPedTestBeanAction" class="pointcutted-test-bean"
|
||||
method="getName">
|
||||
<result name="WW-4105" type="chain">
|
||||
<param name="actionName">chaintoAOPedTestSubBeanAction</param>
|
||||
</result>
|
||||
</action>
|
||||
<action name="chaintoAOPedTestSubBeanAction" class="pointcutted-test-sub-bean"
|
||||
method="getIssueId">
|
||||
<interceptor-ref name="chain" />
|
||||
<result name="WW-4105" type="null" />
|
||||
</action>
|
||||
</package>
|
||||
</xwork>
|
||||
|
||||
Reference in New Issue
Block a user