简体   繁体   中英

JSF recursive ui:include jumbles output tree on multiple postback

I have a report that displays college course enrollment information in an expandable tree format. It displays the course prefix with the summary enrollment numbers and the specific course enrollment numbers when the prefix is clicked on. The report works fine when it is first run. Should you decide to run the report again for another session, however, things start to get jumbled up.

When Report One is run, it is correct. When Report Two is run (for a different session, same session runs work fine), notice the second has duplicated/missing elements. Both of these reports will work correctly if they're run "first", ie, no other report has been run in that users session.

I'm using a recursively included page to build the tree. Here's the pertinent code from enrollment.xhtml, the report page itself:

<div id="resultSet" class="treeContainer">
   <c:if test="${EnrollmentBean.reportRan}">
      <ui:include src="/WEB-INF/includes/reportTree.xhtml">
         <ui:param name="nodes" value="#{EnrollmentBean.reportTreeData.getChildren()}" />
         <ui:param name="isHidden" value="false" />
      </ui:include>
   </c:if>
</div>

The included reportTree.xhtml code (tree expansion code done with CSS/jQuery):

<ul class="${isHidden ? 'hidden' : ''}">
   <c:forEach items="#{nodes}" var="node">
      <li><a href="#" class="reportTreeLabel">#{node.getData().getKey()}
              <span class="reportTreeData">#{node.getData().getValue()}</span>
          </a>

          <c:if test="#{node.hasChildren()}">
              <ui:include src="/WEB-INF/includes/reportTree.xhtml">
                 <ui:param name="nodes" value="#{node.getChildren()}" />
                 <ui:param name="isHidden" value="true" />
              </ui:include>
          </c:if>
      </li>
   </c:forEach>
</ul>

The pertinent parts of the backing bean, EnrollmentBean.java:

@Named("EnrollmentBean")
@SessionScoped
public class EnrollmentBean implements Serializable {
   /** Holds Report Data */
   private List< List<String> > reportData;
   /** Hold Report Data in tree format */
   private TreeNode<SimpleEntry<String, Integer>> reportTreeData;

   private void buildReportTree() {
      this.reportTreeData = new TreeNode<SimpleEntry<String,Integer>>();

      String prefix = "";
      Integer prefixEnrollSum = 0;      
      // Stores the tree data for the prefix being processed. Once all the data has been processed, this is added as a child to the reportTreeData field.
      TreeNode<SimpleEntry<String,Integer>> PrefixTree = null;
      SimpleEntry<String,Integer> data = null;

      for (List<String> line : this.reportData) { // for each raw data line
         String course = line.get(0);
         Integer enrollments = Integer.parseInt(line.get(1));

         if (!prefix.equals(this.getPrefix(course)) ) { // if the prefix changed since the last line that was processed
            prefix = this.getPrefix(course); // set the new prefix

            if (PrefixTree != null) { // if we're not dealing with the very first line of data
               // set the sum of enrollments gathered for this prefix and reset the counter for the next prefix.
               PrefixTree.getData().setValue(prefixEnrollSum);
               prefixEnrollSum = 0;
            }

            // prepare a data element for the prefix summary node, then create the node, passing in the data and the parent for this node. 
            data = new SimpleEntry<String,Integer>(prefix, 0);
            PrefixTree = new TreeNode<SimpleEntry<String,Integer>>(data);
            this.reportTreeData.addChild(PrefixTree);
         } // end prefix changed

         data = new SimpleEntry<String,Integer>(course, enrollments);
         PrefixTree.addChild(data);
         prefixEnrollSum += enrollments;
      } // end for each data line

      // If there was data to process, upon processing the final row, assign the summed enrollments to the final prefix tree.
      if (PrefixTree != null) { PrefixTree.getData().setValue(prefixEnrollSum); }
   }
}

The TreeNode class is one that I've created and it provides simple tracking of data, parent, and children. I can post that code if needed, but I believe it is superfluous at this time.

During my troubleshooting of this problem, I've verified the reportData the report tree is built on in the backing bean is always correct. Through using the Logger class, I've verified that the tree is being generated correctly (by writing to the server log each line that was being processed into the tree). I've even verified that the reportTreeData is correct after every run by writing out the tree to the server log after the tree was constructed.

I can only conclude that something is going wrong in the Render Response phase of the JSF lifecycle, as I did notice that if I change the backing bean from @SessionScoped to @RequestScoped , the report is generated correctly every time. I'd rather not us this as my fix, as I have a "Download CSV" link that uses the already generated report data in the backing bean so the report logic doesn't need to re-run to generate the CSV.

Does anyone know why this is happening and what I can do to correct this behavior? I'm using JSF 2.2 with Java EE 7 on GlassFish Open Source Edition 4.1 (build 13)

UPDATE 12/24/15

I've been stepping through the render response phase JSF code and it seems that the EL expression is just being evaluated with the wrong data on a second report run. I can see where it is making the function call to evaluate the EL expression and it is coming back with the wrong data. I will try to get the weld-osgi-bundle.jar source so I can step into that function call at a later date.

基于大量的调试和研究,但特别是这个答案 ,我认为我的问题与试图还原的视图状态有关,而我正在使用的递归使JSF框架难以正确更新组件树模型。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM