Author: jkuhnert
Date: Mon May 28 11:57:36 2007
New Revision: 542297

URL: http://svn.apache.org/viewvc?view=rev&rev=542297
Log:
Fixes TAPESTRY-638.  Added detection of component reference recursion to avoid 
stack overflows as well as properly report the line precise location of the 
recursive reference.

Modified:
    
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageLoader.java
    
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadMessages.java
    
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadStrings.properties
    
tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/pageload/PageLoaderTest.java

Modified: 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageLoader.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageLoader.java?view=diff&rev=542297&r1=542296&r2=542297
==============================================================================
--- 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageLoader.java
 (original)
+++ 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageLoader.java
 Mon May 28 11:57:36 2007
@@ -31,10 +31,7 @@
 import org.apache.tapestry.services.ComponentTemplateLoader;
 import org.apache.tapestry.spec.*;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
 
 /**
  * Implementation of tapestry.page.PageLoader. Runs the process of building the
@@ -42,7 +39,8 @@
  * <p>
  * This implementation is not threadsafe, therefore the pooled service model
  * must be used.
- *
+ * </p>
+ * 
  * @author Howard Lewis Ship
  */
 
@@ -147,6 +145,12 @@
 
     private ClassResolver _classResolver;
 
+    /**
+     * As each component is constructed it is placed on to the component 
stack,  when construction is finished it is pushed
+     * back off the stack.  This helps in detecting component nesting and 
properly reporting errors.
+     */
+    private Stack _componentStack = new Stack();
+
     public void initializeService()
     {
 
@@ -349,6 +353,8 @@
         if (_depth > _maxDepth)
             _maxDepth = _depth;
 
+        beginConstructComponent(container, containerSpec);
+        
         String defaultBindingPrefix = 
_componentPropertySource.getComponentProperty(container, 
TapestryConstants.DEFAULT_BINDING_PREFIX_NAME);
 
         List ids = new ArrayList(containerSpec.getComponentIds());
@@ -364,7 +370,7 @@
                 // container's specification.
 
                 IContainedComponent contained = containerSpec.getComponent(id);
-
+                
                 String type = contained.getType();
                 Location location = contained.getLocation();
 
@@ -415,12 +421,57 @@
         {
             throw new 
ApplicationRuntimeException(PageloadMessages.unableToInstantiateComponent(container,
 ex),
                                                   container, null, ex);
+        } finally {
+            
+            endConstructComponent(container);
         }
 
         _depth--;
     }
 
     /**
+     * Checks the component stack to ensure that the specified component 
hasn't been improperly nested
+     * and referenced recursively within itself.
+     *
+     * @param component
+     *          The component to add to the current component stack and check 
for recursion.
+     * @param specification
+     *          The specification of the specified component.
+     */
+    void beginConstructComponent(IComponent component, IComponentSpecification 
specification)
+    {
+        // check recursion
+
+        int position = _componentStack.search(component);
+        if (position > -1)
+        {
+            Location location = specification.getLocation();
+
+            // try to get the more precise container position location that 
was referenced
+            // in the template to properly report the precise position of the 
recursive reference
+            
+            IContainedComponent container = component.getContainedComponent();
+            if (container != null)
+                location = container.getLocation();
+            
+            throw new 
ApplicationRuntimeException(PageloadMessages.recursiveComponent(component), 
location, null);
+        }
+
+        _componentStack.push(component);
+    }
+
+    /**
+     * Pops the current component off the stack.
+     *
+     * @param component
+     *          The component that has just been constructed.
+     */
+    void endConstructComponent(IComponent component)
+    {
+        _componentStack.pop();
+    }
+
+    /**
      * Invoked to create an implicit component (one which is defined in the
      * containing component's template, rather that in the containing
      * component's specification).
@@ -572,6 +623,7 @@
         _count = 0;
         _depth = 0;
         _maxDepth = 0;
+        _componentStack.clear();
 
         _locale = _threadLocale.getLocale();
 

Modified: 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadMessages.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadMessages.java?view=diff&rev=542297&r1=542296&r2=542297
==============================================================================
--- 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadMessages.java
 (original)
+++ 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadMessages.java
 Mon May 28 11:57:36 2007
@@ -146,4 +146,9 @@
     {
         return _formatter.format("component-not-found", id);
     }
+
+    public static String recursiveComponent(IComponent component)
+    {
+        return _formatter.format("recursive-component", component);
+    }
 }

Modified: 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadStrings.properties
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadStrings.properties?view=diff&rev=542297&r1=542296&r2=542297
==============================================================================
--- 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadStrings.properties
 (original)
+++ 
tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/PageloadStrings.properties
 Mon May 28 11:57:36 2007
@@ -30,3 +30,4 @@
 used-parameter-alias=Parameter {2} (for component {1}, at {0}) was bound; this 
parameter has been deprecated, bind parameter {3} instead.
 deprecated-parameter=Parameter {0} (at {1}) has been deprecated, and may be 
removed in a future release. Consult the documentation for component {2} to 
determine an appropriate replacement.
 component-not-found=No component found in tree for EventListener binding with 
a matching component id of {0}.
+recursive-component=Component {0} has recursive reference to itself. This is 
most likely caused by referencing the same component in your html template.

Modified: 
tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/pageload/PageLoaderTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/pageload/PageLoaderTest.java?view=diff&rev=542297&r1=542296&r2=542297
==============================================================================
--- 
tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/pageload/PageLoaderTest.java
 (original)
+++ 
tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/pageload/PageLoaderTest.java
 Mon May 28 11:57:36 2007
@@ -118,12 +118,6 @@
         verify();
     }
 
-    private void trainCreateBinding(BindingSource source, IComponent 
container, String description,
-            String expression, String defaultBindingPrefix, Location l, 
IBinding binding)
-    {
-        expect(source.createBinding(container, description, expression, 
defaultBindingPrefix, l)).andReturn(binding);
-    }
-
     private void trainCreateBinding(BindingSource source, IComponent 
container, IParameterSpecification ps, String description,
             String expression, String defaultBindingPrefix, Location l, 
IBinding binding)
     {


Reply via email to