Issue Details (XML | Word | Printable)

Key: WW-1083
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: tm_jee
Reporter: John Devine
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
WebWork

Stack overflow in <ww:form> tag getTheme(), getAncestor(), when 2 forms on a page

Created: 12/Jan/06 12:04 PM   Updated: 19/Feb/07 02:52 AM
Component/s: Views
Affects Version/s: 2.2
Fix Version/s: 2.2.1

Issue Links:
Container
 
Related
 

Sub-Tasks  All   Open   
 Sub-Task Progress: 

 Description  « Hide
see thread: http://forums.opensymphony.com/thread.jspa?threadID=14037&tstart=0

Two separate forms on a page cause a stack overflow to be thrown from the getTheme() if no theme attribute is included in the ww:form tag.

 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
tm_jee added a comment - 13/Jan/06 07:08 AM
I tried,

<html>
<ww:form ...>
  <ww:textfield ...>
  <ww:submit />
</ww:form>

<ww:form ...>
  <ww:textfield ...>
  <ww:submit />
</ww:form>

</html>


<html>
<ww:form ...>
  <ww:textfield ...>
  <ww:submit />
</ww:form>

<!-- include2.jsp has a ww:form, ww:textfield and ww:submit -->
<ww:include value="include2.jsp" />

</html>

They seems to be working fine on my side. I suspect it might be my environment (maybe), so i tried it out in quickstart and it seems to be ok as well

Is it possible for you to try it out in quick start ( java -jar webwork-2.2.jar quickstart:showcase to run it) the jsp could go under the webapps/showcase/src/webapp/ directory and they will be accessible through the root context, to see if the problem still persists.

Kindly let us know the results. Cheers! :-)




Rich Thornett added a comment - 13/Jan/06 01:19 PM
I'm having the same issue. I've paired down a page with two forms to the following minimal version that breaks:

<%@ taglib uri="/webwork" prefix="ww" %>

<ww:form action="/searchVacations.action" name="searchVacationsForm" method="GET">
<ww:select id="airhotel_roomCount" name="roomCount" list="{1, 2, 3}" />
</ww:form>

<ww:form action="/searchHotels.action" name="searchHotelsForm" method="GET">
</ww:form>

which results in the stacktrace:

java.lang.StackOverflowError
at java.util.HashMap.hash(HashMap.java:264)
at java.util.HashMap.containsKey(HashMap.java:342)
at ognl.OgnlContext.get(OgnlContext.java:378)
at com.opensymphony.webwork.components.Component.getComponentStack(Component.java:58)
at com.opensymphony.webwork.components.Component.findAncestor(Component.java:90)
at com.opensymphony.webwork.components.UIBean.getTheme(UIBean.java:365)
at com.opensymphony.webwork.components.UIBean.getTheme(UIBean.java:367)
at com.opensymphony.webwork.components.UIBean.getTheme(UIBean.java:367)
at com.opensymphony.webwork.components.UIBean.getTheme(UIBean.java:367)
at com.opensymphony.webwork.components.UIBean.getTheme(UIBean.java:367)
...

If I remove the line with the ww:select tag, the page renders successfully.

tm_jee added a comment - 13/Jan/06 09:11 PM
fixed it in cvs head. I think it should be ok now. The changes are in UIBean and Form.

John, Rich, could you cross check for me? I'll leave the issue as open and close it once its ok with you guys. Thanks.

Brian Lenz added a comment - 17/Jan/06 01:28 PM
I've been experiencing this problem as well. I took a look at the fix, and I suspect that it will fix my problem. I'm not sure if the implementation of the fix is quite right, though. I've stepped through the debugger in trying to track my specific problem down. I believe that the problem is actually that the component stack isn't being updated correctly in the Component class.

Here's my specific case:
<ww:form id="dummy">
            <ww:radio name="hasAccount"
                      label="Do you have an account?"
                      list="#{'yes' : 'Yes', 'no' : 'No'}"
                      value="'yes'" />
        </ww:form>
        <ww:form action="register" method="get">
            <ww:hidden name="redirect"/>
            <ww:submit id="registerSubmit" value="register" />
        </ww:form>

Here's exactly what's happening with the component stack:
<ww:form id="dummy"> - pushes the Form component onto the Component stack
<ww:radio .../> - pushes the Radio component onto the Component stack and then pops it off on closing. At this point, the radiomap.ftl template is executed. radiomap.ftl has an embedded ww:iterator tag in it. This causes an IteratorComponent to be created. The Component() constructor pushes the IteratorComponent onto the Component stack correctly. Here's the state of the stack at this point:
- IteratorComponent
- Form

IteratorComponent overrides the end() method from Component. IteratorCompontent.end() does _not_ call super.end(). Thus, the IteratorComponent is not removed from the Component stack (though it should be). I believe this is the root of the problem, but I'll continue the rest of the example for completeness.

The stack remains in the state I showed above. Here is the processing for the remainder of the jsp:
</ww:form> - pops the top of the Component stack off. Since IteratorComponent did not remove itself from the stack, the IteratorComponent is now removed from the stack, leaving the Form on the stack
<ww:form action="register" .../> - Adds the Form to the stack. Then tries to resolve the theme via getTheme(), thus resulting in the infinite recursion.

Changing the InteratorComponent to remove itself from the stack on end() would solve my specific problem. I still think there is an issue with Component.findAncestor(), though. It should only start at the current "level" in the stack to prevent the infinite recursion. This would avoid infinite recursion when using nested ww:form tags.

Finally, I did a little audit of the Components that override Component.end(). It looks like the following two Components do not call super.end(), and thus may be susceptible to the same problem:
ActionComponent
ElseIf

Each of ActionComponent, ElseIf, and IteratorComponent return false. I suspect that changing "return false;" to the following should correct the problem:
return super.end(writer, ""); // use the empty string for the body to prevent anything being written to the output to maintain current functionality

The implementation for IteratorComponent isn't quite that easy. I think you would probably want to leave the "return true;" that happens if the iterator has more elements. You'd only want to change the "return false;" when the iterator does not have any more elements.

I have only a basic understanding of the WebWork code base, but these are my thoughts based on my initial investigation.

Thoughts?

Brian Lenz added a comment - 17/Jan/06 02:09 PM
FWIW, the current fix did correct the infinite recursion problem for me (as I suspected). Though, I would still recommend investigating my comments above to see if there is a better solution to the problem.

Brian Lenz added a comment - 18/Jan/06 10:32 AM
I believe that the following implementation of findAncestor should fix the problem with findAncestor I described above. It will start at the current element in the stack, rather than starting at the top of the stack each time. Granted, findAncestor() is called 8 times, so you may have to check to make sure each invocation of the method will work with this change in functionality. I believe that this implementation is more true to the actual expected behavior of such a method:

    public Component findAncestor(Class clazz) {
        Stack componentStack = getComponentStack();
        int start = componentStack.search(this);
        // subtract 2 from start since start is 1-based
        for (int i = start - 2; i >= 0; i--) {
            Component component = (Component) componentStack.get(i);
            if (clazz.isAssignableFrom(component.getClass()) && component != this) {
                return component;
            }
        }

        return null;
    }