Commits

Anonymous committed ea06295

Issue number: XW-160
implemented propper recursion detection using the ActionChainResilt

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

Comments (0)

Files changed (5)

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

  * @version $Revision$
  */
 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";
      * @param invocation the DefaultActionInvocation calling the action call stack
      */
     public void execute(ActionInvocation invocation) throws Exception {
+        
+        int currentDepth = currentChainDepth();
+        if (currentDepth > maxChainDepth) {
+            throw new XworkException("Chain infinite recursion detected, maxChainDepth=" + maxChainDepth);
+        }
+        incrementChainDepth();
+
         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(ChainingInterceptor.ACTION_CONTEXT_CHAIN_DEPTH, ActionContext.getContext().get(ChainingInterceptor.ACTION_CONTEXT_CHAIN_DEPTH));
+        extraContext.put(CHAIN_DEPTH, ActionContext.getContext().get(CHAIN_DEPTH));
 
         if (log.isDebugEnabled()) {
             log.debug("Chaining to action " + actionName);
 
         proxy = ActionProxyFactory.getFactory().createActionProxy(this.namespace, actionName, extraContext);
         proxy.execute();
+
     }
 
     public int hashCode() {
         return ((actionName != null) ? actionName.hashCode() : 0);
     }
+
+    private int currentChainDepth() {
+        Integer chainDepth = (Integer) ActionContext.getContext().get(CHAIN_DEPTH);
+
+        if (chainDepth == null) {
+            return 1;
+        } else {
+            return chainDepth.intValue();
+        }
+    }
+
+    private void incrementChainDepth() {
+        ActionContext.getContext().put(CHAIN_DEPTH, new Integer(currentChainDepth() + 1));
+    }
 }

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

+/*
+ * Created on 28/02/2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.opensymphony.xwork;
+
+import junit.framework.TestCase;
+
+import com.opensymphony.xwork.config.ConfigurationManager;
+
+
+/**
+ * @author CameronBraid
+ *
+ * To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+public class ChainResultTest extends TestCase {
+
+    public void testRecursiveChain() throws Exception {
+
+        // allowed 1 chain
+        ActionChainResult.maxChainDepth = 1;
+        ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("", "RecursionChain", null);
+        try {
+            proxy.execute();
+            fail("infinite recursion not detected");
+        } 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();
+
+        // ensure we're using the default configuration, not simple config
+        ConfigurationManager.clearConfigurationProviders();
+        ConfigurationManager.getConfiguration().reload();
+    }
+}

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

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork;
+
+import com.opensymphony.xwork.ActionInvocation;
+import com.opensymphony.xwork.Result;
+
+
+/**
+ */
+public class VoidResult implements Result {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof VoidResult)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public void execute(ActionInvocation invocation) throws Exception {
+    }
+
+    public int hashCode() {
+        return 42;
+    }
+}

src/test/xwork-default.xml

     <package name="xwork-default">
         <result-types>
             <result-type name="chain" class="com.opensymphony.xwork.ActionChainResult" default="true"/>
+            <result-type name="void" class="com.opensymphony.xwork.VoidResult"/>
         </result-types>
 
         <interceptors>

src/test/xwork.xml

             <interceptor-ref name="debugStack"/>
             <interceptor-ref name="defaultStack"/>
         </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>
+        
    	</package>
    	
    	<package name="test-external-refs" extends="default" namespace="test/externalRef/">
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.