dev@javaserverfaces.java.net

[REVIEW:Fix - Issue 12]

From: Roger Kitain <Roger.Kitain_at_Sun.COM>
Date: Thu, 22 Jul 2004 11:59:23 -0400

Background
-----------------
https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=12

Additional Background:
The current client side state saving processing is broken in some cases
(primarily when "verbatim" components are used). A cause of this is
primarily
a "misallignment" when processing state save/restore. Consider the
following example:

JSP
---
<f:view>
<h:form id="form">
                                                                                     
    <h:panelGrid id="panel" columns="2">
        <f:facet name="header" >
            <h:outputText id="outputheader" value="this is the header" />
        </f:facet>
                                                                                     
        <h:commandLink id="commandLink">
            <h:outputText id="redisplay" value="redisplay"/>
        </h:commandLink>
                                                                                     
        <f:verbatim>
            verbatim text
        </f:verbatim>
    </h:panelGrid>
                                                                                     
</h:form>
</f:view>
</html>
Let's focus on the verbatim tag and the components surrounding the
verbatim tag.  At the completion of save state processing, we end up
with the following:
In state saving terms, the panel component ends up with the following
structure:
 The "Panel" Component has 3 children:
childState[0] Component:commandLink  State:[Ljava.lang.Object;@1e090ee
childState[1] Component:_id0 (This is verbatim tag) State: null
childState[2] Component:outputHeader State:[Ljava.lang.Object;@1123eb0
When the restore state process kicks in, we end up with the following:
Component:commandLink is restored with state from "childState[0]" (correct)
Component:outputHeader is restored with state from "childState[1]
    ** which is null **  hence, this component will not appear on postback.
     this component should have been restored with "childState[2]"...
As explained in the issue comments, there are two possible fixes
for this state saving bug:
- Option 1:  api code change  (deal with ignoring transient children
                     and facets during state saving process in 
UIComponentBase).
                     The tree state that will be saved will not include 
"verbatim"
                      component slots.
- Option 2: ri code change (remove transient/facets from the physical tree
                   in StateManagerImpl for client side state saving.  
This is already
                   done for server side state saving).  This would be 
done before
                   the API (UIComponentBase.processSaveState) is executed,
                   hence, end result would be the same as Option 1 - the 
tree state
                   that will be saved will not include "verbatim" 
components slots.
Keep in mind the following:
The spec (javadoc comments in the api - UIComponent.processSaveState)
mention that as each component is examined, /the "transient" property should
be consulted, and if true, return "null"; /
The spec (javadoc comments in the api - StateManager) say:
/"Components may opt out of being included in the serialized view
  by setting their <code>transient</code> property to <code>true</code>.
  This must cause the component itself, as well as all of that 
component's    
  children and facets, to be omitted from the saved  tree structure
  and component state information."
 /To me, this implies that we can also handle transient components in
  the StateManager implementation.
Note:  If Option 2 is used (the RI approach), then I can't think of a 
reason,
            that there would be transient components in the tree during
            API (UIComponentBase.processSaveState) processing - I kept the
            checks in because the spec mentions it...
**Option 1 Change Bundle **
M src/javax/faces/component/UIComponentBase.java
M systest/src/com/sun/faces/systest/NavigationTestCase.java
A systest/web/jsp/verbatim-one-test.jsp
A systest/web/jsp/verbatim-two-test.jsp
Index: UIComponentBase.java
===================================================================
RCS file: 
/cvs/javaserverfaces-sources/jsf-api/src/javax/faces/component/UIComponentBase.java,v
retrieving revision 1.101
diff -u -r1.101 UIComponentBase.java
--- UIComponentBase.java        16 Jun 2004 00:06:55 -0000      1.101
+++ UIComponentBase.java        22 Jul 2004 15:35:27 -0000
@@ -951,20 +951,20 @@
             return null;
         }
         Object [] stateStruct = new Object[2];
-        Object [] childState = null;
         
         // Process this component itself
         stateStruct[MY_STATE] = saveState(context);
         
         // Process all the children of this component
-        int i = 0, len = getChildren().size() + 
getFacets().keySet().size();
 
-        childState = new Object[len];
-        stateStruct[CHILD_STATE] = childState;
+        List nonTransientState = new ArrayList();
         Iterator kids = getChildren().iterator();
         while (kids.hasNext()) {
             UIComponent kid = (UIComponent) kids.next();
-            childState[i++] = kid.processSaveState(context);
+            if (kid.isTransient()) {
+                continue;
+            }
+            nonTransientState.add(kid.processSaveState(context));
         }
         
         Iterator myFacets = getFacets().keySet().iterator();
@@ -975,18 +975,16 @@
         while (myFacets.hasNext()) {
             facetName = (String) myFacets.next();
             facet = (UIComponent) getFacets().get(facetName);
-            if (!facet.isTransient()) {
-                facetState = facet.processSaveState(context);
-                facetSaveState = new Object[1][2];
-                facetSaveState[0][0] = facetName;
-                facetSaveState[0][1] = facetState;
-                childState[i] = facetSaveState;
-            }
-            else {
-                childState[i] = null;
+            if (facet.isTransient()) {
+                continue;
             }
-            i++;
+            facetState = facet.processSaveState(context);
+            facetSaveState = new Object[1][2];
+            facetSaveState[0][0] = facetName;
+            facetSaveState[0][1] = facetState;
+            nonTransientState.add(facetSaveState);
         }
+        stateStruct[CHILD_STATE] = nonTransientState.toArray();
         return stateStruct;
     }
** Option 2 Change Bundle **
M src/com/sun/faces/application/StateManagerImpl.java
M systest/src/com/sun/faces/systest/NavigationTestCase.java
A systest/web/jsp/verbatim-one-test.jsp
A systest/web/jsp/verbatim-two-test.jsp
Index: StateManagerImpl.java
===================================================================
RCS file: 
/cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/application/StateManagerImpl.java,v
retrieving revision 1.22
diff -u -r1.22 StateManagerImpl.java
--- StateManagerImpl.java       1 Jun 2004 17:06:26 -0000       1.22
+++ StateManagerImpl.java       22 Jul 2004 15:37:16 -0000
@@ -101,6 +101,7 @@
                 sessionMap.put(FACES_VIEW_LIST, viewList);
             }
         } else {
+            removeTransientChildrenAndFacets(context, viewRoot, new 
HashSet());
             if (log.isDebugEnabled()) {
                 log.debug("Begin creating serialized view for " +
                           viewRoot.getViewId());
Test Case Modifications/Additions Common To
Both Options:
Index: NavigationTestCase.java
===================================================================
RCS file: 
/cvs/javaserverfaces-sources/jsf-ri/systest/src/com/sun/faces/systest/NavigationTestCase.java,v
retrieving revision 1.5
diff -u -r1.5 NavigationTestCase.java
--- NavigationTestCase.java     1 May 2004 00:48:43 -0000       1.5
+++ NavigationTestCase.java     22 Jul 2004 15:40:46 -0000
@@ -120,4 +120,58 @@
            assertTrue(false);
        }
     }
+
+    public void testNavigateWithVerbatim_One() throws Exception {
+        HtmlForm form;
+        HtmlSubmitInput submit;
+        HtmlPage page, page1;
+
+        page = getPage("/faces/jsp/verbatim-one-test.jsp");
+        form = getFormById(page, "form");
+        assertNotNull("form exists", form);
+        submit = (HtmlSubmitInput)
+            form.getInputByName("form" + NamingContainer.SEPARATOR_CHAR +
+                                "submit");
+
+        // press the link, return to the same page, and check that
+        // output text (header) is still present...
+
+        try {
+            page1 = (HtmlPage) submit.click();
+            assertTrue(-1 != page1.asText().indexOf("this is the header"));
+        } catch (Exception e) {
+            e.printStackTrace();
+            assertTrue(false);
+        }
+    }
+
+    public void testNavigateWithVerbatim_Two() throws Exception {
+        HtmlForm form;
+        HtmlSubmitInput submit;
+        HtmlPage page, page1;
+
+
+        page = getPage("/faces/jsp/verbatim-two-test.jsp");
+        form = getFormById(page, "form");
+        assertNotNull("form exists", form);
+        submit = (HtmlSubmitInput)
+            form.getInputByName("form" + NamingContainer.SEPARATOR_CHAR +
+                                "submit");
+
+
+        // submit the form, return to the same page, and check that
+        // output text (header) is still present...
+        // and verbatim text is still present...
+
+        try {
+            page1 = (HtmlPage) submit.click();
+            assertTrue(-1 != page1.asText().indexOf("verbatim one text 
here"));
+            assertTrue(-1 != page1.asText().indexOf("this is the header"));
+            assertTrue(-1 != page1.asText().indexOf("verbatim two text 
here"));
+        } catch (Exception e) {
+            e.printStackTrace();
+            assertTrue(false);
+        }
+    }
+
verbatim-one-test.jsp
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
 
<f:view>
<head>
<title><h:outputText id="title" value="title"/></title>
</head>
 
<body>
<h:form id="form">
   <h:panelGrid id="panel1" columns="2" styleClass="book"
      columnClasses="menuColumn, chapterColumn">
 
      <f:facet name="header" >
         <h:panelGrid id="panel2" columns="1" >
            <h:outputText id="outputheader" value="this is the header" />
            <f:verbatim><hr/></f:verbatim>
         </h:panelGrid>
      </f:facet>
 
      <h:commandButton id="submit" value="submit"/>
 
      <f:verbatim >
         verbatim text here
      </f:verbatim>
 
   </h:panelGrid>
</h:form>
</body>
</f:view>
</html>
verbatim-two-test.jsp
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
 
<f:view>
<head>
<title><h:outputText id="title" value="title"/></title>
</head>
 
<body>
<h:form id="form">
   <h:panelGrid id="panel1" columns="2" styleClass="book"
      columnClasses="menuColumn, chapterColumn">
 
      <f:verbatim >
         verbatim one text here
      </f:verbatim>
 
      <h:panelGrid id="panel2" columns="1" >
         <h:outputText id="outputheader" value="this is the header" />
         <f:verbatim><hr/></f:verbatim>
      </h:panelGrid>
 
      <h:commandButton id="submit" value="submit"/>
 
      <f:verbatim >
         verbatim two text here
      </f:verbatim>
 
   </h:panelGrid>
</h:form>
</body>
</f:view>
</html>