我有一个报告,它以可扩展的树格式显示大学课程注册信息。单击前缀时,它会显示带有摘要注册号和特定课程注册号的课程前缀。该报告在首次运行时运行良好。然而,如果您决定在另一个会话中再次运行报告,事情就会变得一团糟。
运行Report One时,它是正确的。当Report Two运行时(对于不同的会话,相同的会话运行正常),请注意第二个会话有重复/丢失的元素。如果这两个报告都是"先"运行的,也就是说,在该用户会话中没有运行其他报告,那么它们都可以正常工作。
我正在使用一个递归包含的页面来构建树。以下是来自enrollment.xhtml的相关代码,即报告页面本身:
<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>
包含的reportTree.xhtml代码(使用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>
支持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); }
}
}
TreeNode类是我创建的,它提供了对数据、父级和子级的简单跟踪。如果需要的话,我可以发布那个代码,但我认为现在它是多余的。
在解决这个问题的过程中,我已经验证了在backingbean中构建报告树的reportData始终是正确的。通过使用Logger类,我已经验证了树是否正确生成(通过将处理到树中的每一行写入服务器日志)。我甚至在构建树后将树写入服务器日志,从而验证了每次运行后reportTreeData的正确性。
我只能得出结论,JSF生命周期的"渲染响应"阶段出现了问题,因为我确实注意到,如果我将backingbean从@SessionScoped
更改为@RequestScoped
,那么每次都会正确生成报告。我宁愿不把它作为我的解决方案,因为我有一个"下载CSV"链接,它使用了backingbean中已经生成的报告数据,因此不需要重新运行报告逻辑来生成CSV。
有人知道为什么会发生这种情况吗?我能做些什么来纠正这种行为?我在GlassFish Open Source Edition 4.1(build 13)上使用JSF 2.2和Java EE 7
更新12/24/15
我已经逐步完成了呈现响应阶段的JSF代码,在第二次运行报告时,EL表达式似乎只是用错误的数据进行了评估。我可以看到它在哪里进行函数调用来评估EL表达式,结果返回了错误的数据。我将尝试获取weld-osgi-bundle.jar源代码,以便稍后可以进入该函数调用。
基于大量的调试和研究,特别是这个答案,我认为我的问题与试图恢复的视图状态有关,而我使用的递归使JSF框架很难正确更新组件树模型。