diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ChainingInterceptor.java b/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ChainingInterceptor.java index b5bb03884..19a9e1413 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ChainingInterceptor.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ChainingInterceptor.java @@ -26,12 +26,7 @@ import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import com.opensymphony.xwork2.util.reflection.ReflectionProvider; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; /** @@ -104,6 +99,12 @@ public class ChainingInterceptor extends AbstractInterceptor { private static final Logger LOG = LoggerFactory.getLogger(ChainingInterceptor.class); + private static final String ACTION_ERRORS = "actionErrors"; + private static final String ACTION_MESSAGES = "actionMessages"; + + private boolean copyMessages = false; + private boolean copyErrors = false; + protected Collection excludes; protected Collection includes; @@ -114,37 +115,67 @@ public class ChainingInterceptor extends AbstractInterceptor { this.reflectionProvider = prov; } + @Inject(value = "struts.xwork.chaining.copyErrors", required = false) + public void setCopyErrors(String copyErrors) { + this.copyErrors = "true".equalsIgnoreCase(copyErrors); + } + + @Inject(value = "struts.xwork.chaining.copyMessages", required = false) + public void setCopyMessages(String copyMessages) { + this.copyMessages = "true".equalsIgnoreCase(copyMessages); + } + @Override public String intercept(ActionInvocation invocation) throws Exception { ValueStack stack = invocation.getStack(); CompoundRoot root = stack.getRoot(); - - if (root.size() > 1 && isChainResult(invocation)) { - List list = new ArrayList(root); - list.remove(0); - Collections.reverse(list); - - Map ctxMap = invocation.getInvocationContext().getContextMap(); - Iterator iterator = list.iterator(); - int index = 1; // starts with 1, 0 has been removed - while (iterator.hasNext()) { - index = index + 1; - Object o = iterator.next(); - if (o != null) { - if (!(o instanceof Unchainable)) { - reflectionProvider.copy(o, invocation.getAction(), ctxMap, excludes, includes); - } - } else { - LOG.warn("compound root element at index " + index + " is null"); - } - } + if (shouldCopyStack(invocation, root)) { + copyStack(invocation, root); } return invocation.invoke(); } - private boolean isChainResult(ActionInvocation invocation) throws Exception { + private void copyStack(ActionInvocation invocation, CompoundRoot root) { + List list = prepareList(root); + Map ctxMap = invocation.getInvocationContext().getContextMap(); + for (Object object : list) { + if (shouldCopy(object)) { + reflectionProvider.copy(object, invocation.getAction(), ctxMap, prepareExcludes(), includes); + } + } + } + + private Collection prepareExcludes() { + Collection localExcludes = excludes; + if (!copyErrors || !copyMessages) { + if (localExcludes == null) { + localExcludes = new HashSet(); + if (!copyErrors) { + localExcludes.add(ACTION_ERRORS); + } + if (!copyMessages) { + localExcludes.add(ACTION_MESSAGES); + } + } + } + return localExcludes; + } + + private boolean shouldCopy(Object o) { + return o != null && !(o instanceof Unchainable); + } + + @SuppressWarnings("unchecked") + private List prepareList(CompoundRoot root) { + List list = new ArrayList(root); + list.remove(0); + Collections.reverse(list); + return list; + } + + private boolean shouldCopyStack(ActionInvocation invocation, CompoundRoot root) throws Exception { Result result = invocation.getResult(); - return result != null && ActionChainResult.class.isAssignableFrom(result.getClass()); + return root.size() > 1 && (result == null || ActionChainResult.class.isAssignableFrom(result.getClass())); } /** @@ -180,7 +211,7 @@ public class ChainingInterceptor extends AbstractInterceptor { * @param includes the includes list */ public void setIncludes(Collection includes) { - this.includes = includes; + this.includes.addAll(includes); } } diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java index a13d2a583..3e3dc6127 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/interceptor/ChainingInterceptorTest.java @@ -19,10 +19,7 @@ import com.mockobjects.dynamic.Mock; import com.opensymphony.xwork2.*; import com.opensymphony.xwork2.util.ValueStack; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; +import java.util.*; /** @@ -45,7 +42,11 @@ public class ChainingInterceptorTest extends XWorkTestCase { mockInvocation.matchAndReturn("getAction", action2); stack.push(action1); stack.push(action2); + interceptor.setCopyErrors("true"); + interceptor.setCopyMessages("true"); + interceptor.intercept(invocation); + assertEquals(action1.getActionErrors(), action2.getActionErrors()); action2.addActionError("bar"); assertEquals(1, action1.getActionErrors().size()); @@ -53,6 +54,24 @@ public class ChainingInterceptorTest extends XWorkTestCase { assertTrue(action2.getActionErrors().contains("bar")); } + public void testActionErrorsNotCopiedAfterChain() throws Exception { + SimpleAction action1 = new SimpleAction(); + SimpleAction action2 = new SimpleAction(); + action1.addActionError("foo"); + mockInvocation.matchAndReturn("getAction", action2); + stack.push(action1); + stack.push(action2); + + interceptor.intercept(invocation); + + assertEquals(Collections.EMPTY_LIST, action2.getActionErrors()); + action2.addActionError("bar"); + assertEquals(1, action1.getActionErrors().size()); + assertEquals(1, action2.getActionErrors().size()); + assertTrue(action2.getActionErrors().contains("bar")); + assertFalse(action2.getActionErrors().contains("foo")); + } + public void testPropertiesChained() throws Exception { TestBean bean = new TestBean(); TestBeanAction action = new TestBeanAction(); @@ -62,7 +81,11 @@ public class ChainingInterceptorTest extends XWorkTestCase { bean.setCount(1); stack.push(bean); stack.push(action); + interceptor.setCopyErrors("true"); + interceptor.setCopyMessages("true"); + interceptor.intercept(invocation); + assertEquals(bean.getBirth(), action.getBirth()); assertEquals(bean.getName(), action.getName()); assertEquals(bean.getCount(), action.getCount()); @@ -77,11 +100,15 @@ public class ChainingInterceptorTest extends XWorkTestCase { bean.setCount(1); stack.push(bean); stack.push(action); + interceptor.setCopyErrors("true"); + interceptor.setCopyMessages("true"); Collection excludes = new ArrayList(); excludes.add("count"); interceptor.setExcludes(excludes); + interceptor.intercept(invocation); + assertEquals(bean.getBirth(), action.getBirth()); assertEquals(bean.getName(), action.getName()); assertEquals(0, action.getCount());