Commits

Anonymous committed 04c104d

Issue number: XW-160
refactored chain recursion detection to keep a history of actions that have been chained to.
when chaining to an already chained to namespace+actionname, an exception is thrown

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@188e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (3)

src/java/com/opensymphony/xwork/ActionChainResult.java

  */
 package com.opensymphony.xwork;
 
-import com.opensymphony.xwork.interceptor.ChainingInterceptor;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import java.util.HashMap;
-
 
 /**
  * A special kind of view that invokes GenericDispatch (using the previously existing
  */
 public class ActionChainResult implements Result {
     
-    //~ Static fields/initializers /////////////////////////////////////////////
-    public static final String CHAIN_DEPTH = "com.opensymphony.xwork.ActionChainResult.CHAIN_DEPTH";
-
-    // to prevent infinite recursion, only allow 100 chains within the same request
-    public static int maxChainDepth = 100;
-
     private static final Log log = LogFactory.getLog(ActionChainResult.class);
     public static final String DEFAULT_PARAM = "actionName";
+    private static final String CHAIN_HISTORY = "CHAIN_HISTORY";
 
     //~ Instance fields ////////////////////////////////////////////////////////
 
      */
     public void execute(ActionInvocation invocation) throws Exception {
         
-        int currentDepth = currentChainDepth();
-        if (currentDepth > maxChainDepth) {
-            throw new XworkException("Chain infinite recursion detected, maxChainDepth=" + maxChainDepth);
+        if (isInChainHistory(namespace, actionName)) {
+            throw new XworkException("infinite recursion detected");
         }
-        incrementChainDepth();
+        addToHistory(namespace, actionName);
 
         HashMap extraContext = new HashMap();
         extraContext.put(ActionContext.VALUE_STACK, ActionContext.getContext().getValueStack());
         extraContext.put(ActionContext.PARAMETERS, ActionContext.getContext().getParameters());
         extraContext.put("com.opensymphony.xwork.interceptor.component.ComponentManager", ActionContext.getContext().get("com.opensymphony.xwork.interceptor.component.ComponentManager"));
-        extraContext.put(CHAIN_DEPTH, ActionContext.getContext().get(CHAIN_DEPTH));
+        extraContext.put(CHAIN_HISTORY, ActionContext.getContext().get(CHAIN_HISTORY));
 
         if (log.isDebugEnabled()) {
             log.debug("Chaining to action " + actionName);
         return ((actionName != null) ? actionName.hashCode() : 0);
     }
 
-    private int currentChainDepth() {
-        Integer chainDepth = (Integer) ActionContext.getContext().get(CHAIN_DEPTH);
-
-        if (chainDepth == null) {
-            return 1;
+    private boolean isInChainHistory(String namespace, String actionName) {
+        Set chainHistory = (Set) ActionContext.getContext().get(CHAIN_HISTORY);
+        if (chainHistory == null) {
+            return false;
         } else {
-            return chainDepth.intValue();
+            return chainHistory.contains(makeKey(namespace, actionName));
         }
     }
-
-    private void incrementChainDepth() {
-        ActionContext.getContext().put(CHAIN_DEPTH, new Integer(currentChainDepth() + 1));
+    private void addToHistory(String namespace, String actionName) {
+        Set chainHistory = (Set) ActionContext.getContext().get(CHAIN_HISTORY);
+        if (chainHistory == null) {
+        	chainHistory = new HashSet();
+		}
+		ActionContext.getContext().put(CHAIN_HISTORY, chainHistory);
+		
+		chainHistory.add(makeKey(namespace, actionName));
+    }
+    
+    private String makeKey(String namespace, String actionName) {
+        return namespace + "/" + actionName;
     }
 }

src/test/com/opensymphony/xwork/ChainResultTest.java

 
     public void testRecursiveChain() throws Exception {
 
-        // allowed 1 chain
-        ActionChainResult.maxChainDepth = 1;
-        ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("", "RecursionChain", null);
+        ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("", "InfiniteRecursionChain", null);
         try {
             proxy.execute();
-            fail("infinite recursion not detected");
+            fail("did not detected repeated chain to an action");
         } catch (XworkException e) {
-            assertTrue(e.getMessage().indexOf("infinite") != -1);
         }
         
     }
 
-    public void testValidChain() throws Exception {
-
-        ActionChainResult.maxChainDepth = 100;
-        ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("", "RecursionChain", null);
-        proxy.execute();
-        
-    }
-
     protected void setUp() throws Exception {
         super.setUp();
 

src/test/xwork.xml

         </action>
         
         <!-- chain resursion detection -->
-        <action name="RecursionChain" class="com.opensymphony.xwork.ActionSupport">
-        	<result name="success" type="chain">RecursionChain2</result>
-        </action>
-        <action name="RecursionChain2" class="com.opensymphony.xwork.ActionSupport">
-        	<result name="success" type="chain">RecursionChain3</result>
-        </action>
-        <action name="RecursionChain3" class="com.opensymphony.xwork.ActionSupport">
-			<result name="success" type="void"/>
+        <action name="InfiniteRecursionChain" class="com.opensymphony.xwork.ActionSupport">
+        	<result name="success" type="chain">InfiniteRecursionChain</result>
         </action>
         
    	</package>