我有一份报告,它以可扩展的树格式显示大学课程注册信息。单击前缀时,它会显示带有摘要注册号的课程前缀和特定的课程注册号。该报告在首次运行时运行良好。但是,如果您决定为另一个会话再次运行报告,事情就会开始变得混乱。
运行报告一时,它是正确的。运行报告二时(对于不同的会话,相同的会话运行正常),请注意第二个有重复/缺失的元素。如果“首先”运行这两个报告,它们将正常工作,即在该用户会话中没有运行其他报告。
我正在使用递归包含的页面来构建树。这是来自报告页面本身的注册.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 类是我创建的,它提供了对数据、父级和子级的简单跟踪。如果需要,我可以发布该代码,但我认为此时它是多余的。
在我对这个问题进行故障排除期间,我已经验证了在支持 bean 中构建报告树的 reportData 始终是正确的。通过使用 Logger 类,我验证了树的生成是否正确(通过将正在处理到树中的每一行写入服务器日志)。我什至通过在构建树后将树写到服务器日志来验证每次运行后 reportTreeData 是否正确。
我只能得出结论,JSF 生命周期的渲染响应阶段出了点问题,因为我确实注意到,如果我将支持 bean 从 更改@SessionScoped
为@RequestScoped
,则每次都会正确生成报告。我不希望将此作为我的解决方案,因为我有一个“下载 CSV”链接,该链接使用支持 bean 中已经生成的报告数据,因此报告逻辑不需要重新运行来生成 CSV。
有谁知道为什么会发生这种情况以及我可以做些什么来纠正这种行为?我在 GlassFish Open Source Edition 4.1 (build 13) 上使用 JSF 2.2 和 Java EE 7
2015 年 12 月 24 日更新
我一直在逐步完成呈现响应阶段的 JSF 代码,似乎 EL 表达式只是在第二次运行报告时使用错误的数据进行评估。我可以看到它在哪里进行函数调用以评估 EL 表达式,并且它返回了错误的数据。我将尝试获取weld-osgi-bundle.jar 源代码,以便以后可以进入该函数调用。